2018-05-09
旋轉看起來挺費勁的,其實非常簡單。我們只需要給shader傳入mvp矩陣即可。旋轉分為兩類:camera旋轉、物體旋轉。當指定mvp矩陣時,model矩陣是每個物體攜帶的資料,projection矩陣是由camera 的fov、aspect、near/far距離決定的,對於camera旋轉,我們只需要更新view矩陣即可。view矩陣由三部分組成:eye pos,lookat center,up。這就是乙個camera引數。camera一般圍繞center旋轉,up不變,那麼eye 新的位置就可以通過旋轉角度求出。對於物體旋轉,我們只需要更新m矩陣即可。
我們在螢幕畫素空間拖動滑鼠,這個操作只影響三個空間維度中兩個維度。我們很容易就能求解出兩個方向的旋轉角度。假設螢幕橫縱向的距離代表的角度為90°,橫縱方向移動的畫素距離w,w/width() * 90°就是橫向旋轉的角度了,縱向同理。開始移動的時候,我們需要確定旋轉所圍繞的向量。
旋轉物體時,需要以物體中心構造arcball來旋轉。有兩種處理方式:改變物體的座標、改變物體的model矩陣。對於顯示來講,無甚區別。一般,我們拖動物體或者旋轉物體的時候,都會以物體的中心構造arcball,就是軌跡球。物體旋轉示例**如下:
glm::vec3 get_arcball_vector(double x, double y)camera旋轉示例**:glm::vec3 prevpos = get_arcball_vector(startpos.x(), startpos.y());
glm::vec3 currpos = get_arcball_vector(e->pos().x(), e->pos().y());
float angle = acos(std::min(1.0f, glm::dot(prevpos, currpos)));
glm::vec3 camaxis = glm::cross(prevpos, currpos);
glm::mat4 viewrotation = glm::rotate(glm::degrees(angle)*0.01f, camaxis);
cloudmodel = (viewrotation)* cloudmodel;
startpos = e->pos();
float yaw_beta = (float)delta.x() / width() * m_pi_4; // yaw radian
float pitch_theta = (float)delta.y() / height() * m_pi_4; // pitch radian
glm::vec3 roll = glm::normalize(eye - center);
glm::vec3 pitch = glm::normalize(glm::cross(up, roll)); // pitch
glm::vec3 yaw = glm::normalize(glm::cross(roll, pitch)); // yaw axis
glm::mat4 yawmat = glm::rotate(yaw_beta, yaw);
glm::mat4 pitchmat = glm::rotate(pitch_theta, pitch);
eye = pitchmat * yawmat * glm::vec4(eye - center, 0.0) + glm::vec4(center, 0.0);
up = yaw;
在camera旋轉中,旋轉中心(center)的選擇,這也是影響操作的主要因素。如果沒有選中物體,眼睛-滑鼠點射線方向上任乙個點都可以作為center,假設選擇了很遠的乙個點,當我們稍微拖動滑鼠,旋轉角度很小,但是物體已經處在camera左側或者右側非常遠的地方了。故關鍵問題便在於如何在涉嫌上選取合理的乙個點。如果我們使用角度來計算,那就會遇到乙個非常著名的問題:萬向節死鎖。這裡的講解非常到位。
對於平移,很重要的乙個問題是,對於眼睛在不同的位置,拖動相同的畫素距離,camera的真實平移距離是多少?對於旋轉,我們只考慮相對角度,平移所對應的絕對距離就是關鍵問題。首先要確定平移的向量transvec,也可求得在近平面上的移動距離ed ,這樣子就可以求解lookat center 的 平移向量。最後更新eye位置,這裡最大的影響因素就是nearplane,如果這個值很小,那麼意味著我們認為transvec近似為eye的平移向量。當此值逐漸變大時,那麼lookat center的平移向量就偏小,就意味著此時的平移就帶有繞lookat center旋轉的成分,可以看到遠處的東西比我們預想移動的要慢。示例**如下:
float d = glm::length(center - eye); // eye center distance
glm::vec3 neweye = screen2world(e->pos()); //新的
glm::vec3 startcoord = screen2world(startpos);
glm::vec3 transvec = startcoord - neweye;
float ed = glm::length(transvec); // 近平面移動距離
float cd = ed/nearplane *d + ed; // lookat center 移動距離
center = center + glm::normalize(transvec) * cd;
eye = eye + transvec;
縮放是最容易實現的功能。因為只需要更改camera的資訊即可。至於是等距離移動,還是按比例距離移動,則取決於需求。另外乙個問題是:如何選擇縮放的方向。方式一,無論滑鼠點在**,滾動滑輪時eye只在視線方向移動,其示例**如下:
float numstep = (e->angledelta().y() / 8) / 15;
float step = 1.0f/10;
if (1 == numstep)
else
glm::vec3 delta = (center - eye); // (screen2world(event->pos())
eye += step * delta ;
方式二,eye與滑鼠點在近平面上的投影點構成的直線上移動。這就要求更新eye pos,lookat center,up。在透視投影模式下,可以改變fov。相較於改變camera位置,這看起來更像時縮放[1]。 在平行投影模式下,我們可以僅僅調整glortho() 所需引數即可。
我們最常用到兩個座標轉換的函式 screen2world() world2screen(),可使用glm來實現。需要注意的是,qt的螢幕座標係以左上角為原點。我們希望使用左下角為畫素座標原點,需要自行轉換。
關於投影模式,以前我們需要通過gluperspective 設定遠近平面,用來剪裁可視空間,等同於現在我們可以直接指定projection矩陣,其實就只影響mvp矩陣中的p。因為現在所有的mvp矩陣都是我們自己寫**計算的,就不需要glmatrixmode()與gluperspective() 這倆函式產生乙個mvp矩陣並傳入到著色器。對於最新的opengl,我們盡可能少的使用gl開頭的介面函式。老式的gl介面真是***,當時學習的過**是令人難受。示例**如下:
if(projectview) // gl_projection
proj = glm::perspective((fov), width() / (float)height(), nearplane, farplane);
else
proj = glm::ortho(0.0f, (float)width(), 0.0f, (float)height(), nearplane, farplane); // gl_modelview
上面的示例**都預設時在透視投影模式下的。如果想要實現平行投影,稍微更改一下計算即可。
how-can-i-orbit-a-camera-about-its-target-point 這篇文章這的是太重要了,我花費了一天時間才把這個bug fix 掉。
OpenGL座標變換 平移,縮放與旋轉
opengl座標變換 平移,縮放與旋轉 opengl有內建的座標系,事實上opengl有兩套座標系,乙個座標系被稱為眼睛座標 eye coordinate system 簡稱ecs opengl還有一套座標,被稱為 object coordinate system 簡稱ocs 而這個才是更為重要的,...
變換(旋轉 縮放 平移)
scale 縮放 rotate 旋轉 shear 裁切 為什麼 是什麼 affifine map 仿射變換 引入齊次座標後的二維變換 復合變換 旋轉 4.1 尤拉角 為什麼 是什麼 4.2 萬向節死鎖 轉動的術語 出現死鎖 這裡桶滾自由度丟失了,只有兩個自由度,無法表示需要三個自由度的旋轉,產生了死...
iOS開發 旋轉 縮放 平移
一 建立乙個uiview import viewcontroller.h inte ce viewcontroller property nonatomic,strong uiview myview end implementation viewcontroller void viewdidload...