3D物体被渲染到屏幕之前,会经历以下的坐标变换:
在不考虑光照计算、着色处理和纹理映射,只考虑几何变换的情况下,3D渲染管线中的坐标变换流程如下:
该变换流程,将3D模型的局部坐标,变换到屏幕坐标。
本文先不考虑物体消除、背面消除和物体空间的剪裁。
局部坐标也叫模型坐标,使用局部坐标,可以将模型平移到世界坐标中的实际位置。
可以将物体的中心当做局部坐标系的原点,如下图所示的立方体:
立方体边长为10个单位,中心坐标为(0,0,0),各顶点在局部坐标系中的坐标如下:
Vertex 0: (10,10,10) Vertex 1: (-10,10,10)
Vertex 2: (-10,10,-10) Vertex 3: (10,10,-10)
Vertex 4: (10,-10,10) Vertex 5: (-10,-10,10)
Vertex 6: (-10,-10,-10) Vertex 7: (10,-10,-10)
也可以将其他位置作为局部坐标系的原点,例如下图所示的机器人手臂:
可以将手臂的一端作为局部坐标系的原点,从而实现旋转摆动手臂的动作。
我们可以将旋转和缩放放到局部坐标变换中完成。
将局部坐标变换到世界坐标,只需将物体平移到世界坐标的某个位置即可。
假设游戏的世界大小为1000x1000x1000,且要将模型平移到世界空间中的位置 ( x w , y w , z w ) (x_w, y_w, z_w) (xw,yw,zw):
则有平移矩阵形式如下:
T m w = [ 1 0 0 0 0 1 0 0 0 0 1 0 x w y w z w 1 ] {\bf T_{mw}} =\left[ \begin{matrix} 1&0&0&0\\\\ 0&1&0&0\\\\ 0&0&1&0\\\\ x_w&y_w& z_w &1\\\\ \end{matrix} \right] Tmw=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡100xw010yw001zw0001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
将向量和矩阵相乘,可以实现模型坐标到世界坐标的平移,矩阵运算如下:
V m T m w = [ x m y m z m 1 ] T [ 1 0 0 0 0 1 0 0 0 0 1 0 x w y w z w 1 ] = [ x m + x w y m + y w z m + z w 1 ] T {\bf V_mT_{mw}} =\left[\begin{matrix} x_m\\\\ y_m\\\\ z_m\\\\ 1\end{matrix}\right]^ \mathrm{ T } \left[ \begin{matrix} 1&0&0&0\\\\ 0&1&0&0\\\\ 0&0&1&0\\\\ x_w&y_w& z_w &1\\\\ \end{matrix} \right] =\left[\begin{matrix} x_m+x_w\\\\ y_m+y_w\\\\ z_m+z_w\\\\ 1\end{matrix}\right]^ \mathrm{ T } VmTmw=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xmymzm1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤T⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡100xw010yw001zw0001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xm+xwym+ywzm+zw1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤T
相机的参数:
将世界坐标变换为相机坐标:
对世界中的物体进行变换,使其位置是相对于相机的。因此,要人为地将物体从世界坐标系变换到相机坐标系,使得相机位于世界坐标系的原点,并指向+Z轴方向。
在图形学中,物体的运动是相对的,要移动相机,使得相机位于世界坐标系的原点,并指向+Z轴方向,只需相应移动所有的物体。这种变换分两步完成:先平移,后旋转。
A:平移前,物体的位置为 P o b j = ( w o r l d x , w o r l d y , w o r l d z ) P_{obj}=(world_x,world_y,world_z) Pobj=(worldx,worldy,worldz),相机的位置为 P c a m = ( c a m x , c a m y , c a m z ) P_{cam}=(cam_x,cam_y,cam_z) Pcam=(camx,camy,camz)
B:平移后,物体的位置为 P o b j − P c a m P_{obj} - P_{cam} Pobj−Pcam
C:旋转后,物体的世界坐标变换到了相机坐标。
平移矩阵最下面一行是相机位置求反:
T c a m − 1 = [ 1 0 0 0 0 1 0 0 0 0 1 0 − c a m x − c a m y − c a m z 1 ] {\bf T_{cam}}^{-1} = \left[ \begin{matrix} 1&0&0&0\\\\ 0&1&0&0\\\\ 0&0&1&0\\\\ -cam_x&-cam_y&-cam_z &1\\\\ \end{matrix} \right] Tcam−1=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡100−camx010−camy001−camz0001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
绕三个轴旋转的旋转矩阵,旋转角度也要取反:
R c a m x − 1 = [ 1 0 0 0 0 cos ( − θ x ) sin ( − θ x ) 0 0 − sin ( − θ x ) cos ( − θ x ) 0 0 0 0 1 ] = [ 1 0 0 0 0 cos ( θ x ) − sin ( θ x ) 0 0 sin ( θ x ) cos ( θ x ) 0 0 0 0 1 ] {\bf R_{camx}}^{-1} = \left[ \begin{matrix} 1&0&0&0\\\\ 0&\cos(-\theta_x)&\sin(-\theta_x)&0\\\\ 0&-\sin(-\theta_x)&\cos(-\theta_x)&0\\\\ 0&0&0&1\\\\ \end{matrix} \right] =\left[ \begin{matrix} 1&0&0&0\\\\ 0&\cos(\theta_x)&-\sin(\theta_x)&0\\\\ 0&\sin(\theta_x)&\cos(\theta_x)&0\\\\ 0&0&0&1\\\\ \end{matrix} \right] Rcamx−1=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡10000cos(−θx)−sin(−θx)00sin(−θx)cos(−θx)00001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡10000cos(θx)sin(θx)00−sin(θx)cos(θx)00001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
R c a m y − 1 = [ cos ( − θ y ) 0 − sin ( − θ y ) 0 0 1 0 0 sin ( − θ y ) 0 cos ( − θ y ) 0 0 0 0 1 ] = [ cos ( θ y ) 0 sin ( θ y ) 0 0 1 0 0 − sin ( θ y ) 0 cos ( θ y ) 0 0 0 0 1 ] {\bf R_{camy}}^{-1} = \left[ \begin{matrix} \cos(-\theta_y)&0&-\sin(-\theta_y)&0\\\\ 0&1&0&0\\\\ \sin(-\theta_y)&0&\cos(-\theta_y)&0\\\\ 0&0&0&1\\\\ \end{matrix} \right]= \left[ \begin{matrix} \cos(\theta_y)&0&\sin(\theta_y)&0\\\\ 0&1&0&0\\\\ -\sin(\theta_y)&0&\cos(\theta_y)&0\\\\ 0&0&0&1\\\\ \end{matrix} \right] Rcamy−1=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡cos(−θy)0sin(−θy)00100−sin(−θy)0cos(−θy)00001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡cos(θy)0−sin(θy)00100sin(θy)0cos(θy)00001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
R c a m z − 1 = [ cos ( − θ z ) sin ( − θ z ) 0 0 − sin ( − θ z ) cos ( − θ z ) 0 0 0 0 1 0 0 0 0 1 ] = [ cos ( θ z ) − sin ( θ z ) 0 0 sin ( θ z ) cos ( θ z ) 0 0 0 0 1 0 0 0 0 1 ] {\bf R_{camz}}^{-1} = \left[ \begin{matrix} \cos(-\theta_z)&\sin(-\theta_z)&0&0\\\\ -\sin(-\theta_z)&\cos(-\theta_z)&0&0\\\\ 0&0&1&0\\\\ 0&0&0&1\\\\ \end{matrix} \right]= \left[ \begin{matrix} \cos(\theta_z)&-\sin(\theta_z)&0&0\\\\ \sin(\theta_z)&\cos(\theta_z)&0&0\\\\ 0&0&1&0\\\\ 0&0&0&1\\\\ \end{matrix} \right] Rcamz−1=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡cos(−θz)−sin(−θz)00sin(−θz)cos(−θz)0000100001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡cos(θz)sin(θz)00−sin(θz)cos(θz)0000100001⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
上述三个旋转矩阵的推导可参考:3D旋转矩阵的推导
综合上述四个矩阵,世界坐标到相机坐标的变换矩阵为:
T w c = T c a m − 1 R c a m x − 1 R c a m y − 1 R c a m z − 1 {\bf T_{wc}} = {\bf T_{cam}}^{-1}{\bf R_{camx}}^{-1}{\bf R_{camy}}^{-1}{\bf R_{camz}}^{-1} Twc=Tcam−1Rcamx−1Rcamy−1Rcamz−1
透视变换(投影变换):从相机坐标到透视坐标的变换。本质上就是做一个透视除法,实现近大远小的效果。
视距 d d d:视平面和投影点(视点)之间的距离。
视平面(viewplane):
视距计算公式:
d = W / 2 × tan ( θ h / 2 ) d = W/2\times \tan (\theta_h/2) d=W/2×tan(θh/2)
透视变换:将所有物体投影到视平面上。给定视距 d d d,可以计算出物体上的点和视点的连线与视平面的交点:
根据三角形相似,可得透视坐标:
{ X p e r = d x 0 / z 0 Y p e r = d y 0 / z 0 \begin{cases} X_{per} = dx_0/z_0 \\ Y_{per} = dy_0/z_0 \\ \end{cases} {Xper=dx0/z0Yper=dy0/z0
视口(viewport) = 屏幕 (screen)= 光栅化窗口
屏幕变换:将视平面坐标转换到屏幕坐标。
屏幕的原点是(0,0),位于左上角,屏幕宽高分别为 W s W_s Ws和 H s H_s Hs,屏幕大小为 W s × H s W_s \times H_s Ws×Hs,宽高比 a r = W s / H s ar = W_s / H_s ar=Ws/Hs,且y轴被反转,如下图所示:
1.视野为90度且视距为1的情况
在这种情况下,透视空间中,视平面的坐标在每个轴上被归一化为-1到1。
X p e r X_{per} Xper(从-1到1) 转换到 X s c r e e n X_{screen} Xscreen (从0到 W s − 1 W_s-1 Ws−1)
Y p e r Y_{per} Yper(从-1到1) 转换到 Y s c r e e n Y_{screen} Yscreen (从0到 H s − 1 H_s-1 Hs−1)
{ X s c r e e n = ( X p e r + 1 ) / 2 × ( W s − 1 ) Y s c r e e n = ( Y − 1 ) − ( Y p e r + 1 ) / 2 × ( H s − 1 ) \begin{cases} X_{screen} = (X_{per}+1)/2 \times (W_s-1) \\ Y_{screen} = (Y-1)-(Y_{per}+1)/2 \times (H_s-1) \\ \end{cases} {Xscreen=(Xper+1)/2×(Ws−1)Yscreen=(Y−1)−(Yper+1)/2×(Hs−1)
令 α = ( W s − 1 ) / 2 \alpha =(W_s-1)/2 α=(Ws−1)/2, β = ( H s − 1 ) / 2 \beta=(H_s-1)/2 β=(Hs−1)/2,则有
{ X s c r e e n = α + α X p e r Y s c r e e n = β − β Y p e r \begin{cases} X_{screen} =\alpha+ \alpha X_{per} \\ Y_{screen} = \beta -\beta Y_{per} \\ \end{cases} {Xscreen=α+αXperYscreen=β−βYper
其中, ( α , β ) (\alpha,\beta) (α,β)是屏幕中心坐标。
2.一般情况:任意视野和任意视距的情况
在任意视野和任意视距的情况下,我们使用公式 d = W / 2 × tan ( θ h / 2 ) d = W/2\times \tan (\theta_h/2) d=W/2×tan(θh/2)来计算视距,因为没有被归一化,同样令 α = ( W s − 1 ) / 2 \alpha =(W_s-1)/2 α=(Ws−1)/2, β = ( H s − 1 ) / 2 \beta=(H_s-1)/2 β=(Hs−1)/2,则有
{ X s c r e e n = α + X p e r Y s c r e e n = β − Y p e r \begin{cases} X_{screen} =\alpha+ X_{per} \\ Y_{screen} = \beta -Y_{per} \\ \end{cases} {Xscreen=α+XperYscreen=β−Yper
参考资料: