LookAt 矩陣

OpenGL本身沒有攝像機(Camera)的概念,但我們可以通過把場景中的所有物體往相反方向移動的方式來模擬出攝像機,產生一種我們在移動的感覺,而不是場景在移動。

lookat矩陣是由這些東西來定義的:攝像頭位置,它看向的方向,以及向上的up方向。由於「OpenGL中我們知道攝像機指向z軸負方向」,所以給圖如下:

那麼我們知道實際上坐標系的方向是這樣的,我們用(eye - center)就能得到z軸的方向,然後我們定義一個向上的方向 up(0, 1, 0) ,我們可以用 up 	imes z - 向上的方向叉乘z得到x方向,再用 z 	imes x - z方向叉乘x得到y.

也可以參考這個圖:

再根據之前的坐標變換知識,於是我們可以得到lookat矩陣:

 lookat = egin{pmatrix}  R_x & R_y & R_z & 0 \  U_x & U_y & U_z & 0 \  D_x & D_y & D_z & 0 \  0 & 0 & 0 & 1 end{pmatrix} egin{pmatrix}  1 & 0 & 0 & -P_x\  0 & 1 & 0 & -P_y \  0 & 0 & 1 & -P_z \  0 & 0 & 0 & 1 end{pmatrix}

  • R - 右向量,也就是相機坐標系x在世界坐標系中的表示
  • U - 上向量,也就是相機坐標系y在世界坐標系中的表示
  • D - 方向向量,也就是相機坐標系z在世界坐標系中的表示
  • P - 相機在世界坐標系中位置

我感覺也可以這樣理解,Rotation、Translation其實是相機在世界坐標系中的變換:

M_{camera} = M_{Translation} * M_{Rotation}

其實我們用它來看物體也就是它這個變換的逆變換:

M_{camera}^{-1}= M_{Rotation}^{-1} * M_{Translation}^{-1}

而相機的旋轉矩陣的逆是它的轉置,平移矩陣的逆也可以在就是把它移回去,這樣也跟上面的lookat矩陣一樣。寫個代碼:

void lookat(Vec3f eye, Vec3f center, Vec3f up) {
Vec3f z = (eye-center).normalize();
Vec3f x = cross(up,z).normalize();
Vec3f y = cross(z,x).normalize();
Matrix Minv = Matrix::identity();
Matrix Tr = Matrix::identity();
for (int i=0; i<3; i++) {
Minv[0][i] = x[i];
Minv[1][i] = y[i];
Minv[2][i] = z[i];
Tr[i][3] = -center[i];
}
ModelView = Minv*Tr;
}

Viewport 矩陣

我們再來求另一個常見矩陣,我們現在有模型都在[-1,1]*[-1,1]*[-1,1]正方體中,我們想把它映射到位置[x,x+w]*[y,y+h]*[0,d]中,我們的操作是:

  • 平移:先把[-1,1]*[-1,1]*[-1,1]平移到[0,2]*[0,2]*[0,2]
  • 縮放:[0,2]*[0,2]*[0,2]縮放到[0,1]*[0,1]*[0,1]
  • 縮放:[0,1]*[0,1]*[0,1]縮放到[0,w]*[0,h]*[0,d]
  • 平移:[0,w]*[0,h]*[0,d]移動到[x,x+w]*[y,y+h]*[0,d]

就跟窗口變換十分類似,我們可以來求得這個變換的矩陣:

 egin{pmatrix}  1 & 0 & 0 & x\  0 & 1 & 0 & y \ 0 & 0 & 1 & 0 \  0 & 0 & 0 & 1 end{pmatrix} egin{pmatrix}  w & 0 & 0 & 0\  0 & h & 0 & 0 \  0 & 0 & d & 0 \  0 & 0 & 0 & 1 end{pmatrix} egin{pmatrix}  1/2 & 0 & 0 & 0\  0 & 1/2 & 0 & 0 \  0 & 0 & 1/2 & 0 \  0 & 0 & 0 & 1 end{pmatrix} egin{pmatrix}  1 & 0 & 0 & 1\  0 & 1 & 0 & 1 \  0 & 0 & 1 & 1 \  0 & 0 & 0 & 1 end{pmatrix}

計算得:

egin{pmatrix}  w/2 & 0 & 0 & x + w/2\  0 & h/2 & 0 & y + h/2 \ 0 & 0 & d/2 & d/2 \  0 & 0 & 0 & 1 end{pmatrix}

這個矩陣是OpenGL中的Viewport矩陣。

Perspective 矩陣

投影有兩種:

正交:

透視:

我們都知道透視法則-近大遠小,就像上圖,平行的鐵路公路在我們的眼睛裡也是會在遠處相交的。

如果我們要將P點投影到 z=0 平面上的 P 點,camera放在C點(0, 0, c),很容易可以按照比例算出 |AB|/|AC| = |OD|/|OC| 可知: x/(c-z) = x/c,可知:

x = frac{x}{1-z/c}

 y = frac{y}{1-z/c}

所以我們也可以得到投影矩陣:

egin{pmatrix}  1 & 0 & 0 & 0\  0 & 1 & 0 & 0 \  0 & 0 & 1 & 0 \  0 & 0 & -1/c & 1 end{pmatrix}

參考:

  • 攝像機
  • 坐標系統
  • glViewport
  • Perspective projection

推薦閱讀:

相关文章