相机约定

模型变换把物体从局部坐标放入世界坐标。观察同一场景时,相机的位置和朝向仍然可以任意变化,因此需要把世界坐标继续转换到 相机坐标 Camera Space

设相机位置为 e\mathbf e,观察方向为 g\mathbf g,向上参考方向为 t\mathbf t。课程采用右手相机坐标:相机位于原点,朝 z-z 方向观察,+y+y 向上。先构造相机的三个单位基:

w=gg,u=t×wt×w,v=w×u.\mathbf w=-\frac{\mathbf g}{\lVert\mathbf g\rVert}, \qquad \mathbf u= \frac{\mathbf t\times\mathbf w} {\lVert\mathbf t\times\mathbf w\rVert}, \qquad \mathbf v=\mathbf w\times\mathbf u.

u,v,w\mathbf u,\mathbf v,\mathbf w 分别指向相机的右、上、后方。第一步取 w=g^\mathbf w=-\hat{\mathbf g},是因为相机看向自身的 z-z;第二步用叉积得到右方向;第三步再做一次叉积,使三个方向严格正交。若给定的 t\mathbf t 没有与 g\mathbf g 完全垂直,这个过程也完成了一次 Gram–Schmidt 正交化。

t\mathbf tg\mathbf g 平行时,t×w=0\mathbf t\times\mathbf w=\mathbf0,相机的横向方向无法确定。实际实现应检测这种退化情况,并换用不平行的上方向。

视图矩阵

视图矩阵 View Matrix 是相机到世界变换的逆。相机局部坐标到世界坐标的旋转矩阵以相机基为列:

Rcamworld=(uxvxwxuyvywyuzvzwz).R_{\mathrm{cam}\rightarrow\mathrm{world}} = \begin{pmatrix} u_x&v_x&w_x\\ u_y&v_y&w_y\\ u_z&v_z&w_z \end{pmatrix}.

连同相机位置,完整的相机到世界变换为

Mcam=(uxvxwxexuyvywyeyuzvzwzez0001).M_{\mathrm{cam}} = \begin{pmatrix} u_x&v_x&w_x&e_x\\ u_y&v_y&w_y&e_y\\ u_z&v_z&w_z&e_z\\ 0&0&0&1 \end{pmatrix}.

渲染需要相反方向:把世界中的点写成相机坐标,所以视图矩阵是 Mcam1M_{\mathrm{cam}}^{-1}。相机基为正交单位基,旋转矩阵满足 R1=RTR^{-1}=R^T。先把世界平移 e-\mathbf e,再投影到三个相机基上:

Mview=(uxuyuz0vxvyvz0wxwywz00001)(100ex010ey001ez0001).M_{\mathrm{view}} = \begin{pmatrix} u_x&u_y&u_z&0\\ v_x&v_y&v_z&0\\ w_x&w_y&w_z&0\\ 0&0&0&1 \end{pmatrix} \begin{pmatrix} 1&0&0&-e_x\\ 0&1&0&-e_y\\ 0&0&1&-e_z\\ 0&0&0&1 \end{pmatrix}.

对世界坐标点 p\mathbf p,结果前三维也可直接写成

pview=(u(pe)v(pe)w(pe)).\mathbf p_{\mathrm{view}} = \begin{pmatrix} \mathbf u\cdot(\mathbf p-\mathbf e)\\ \mathbf v\cdot(\mathbf p-\mathbf e)\\ \mathbf w\cdot(\mathbf p-\mathbf e) \end{pmatrix}.

这说明视图变换的本质是:以相机为新原点,分别测量点在相机右、上、后三个方向上的坐标。把相机移动到标准位置,与让整个世界做相反变换完全等价。

相机移动与世界施加逆变换在画面中产生等价的相对运动

正交投影

正交投影 Orthographic Projection 的可见区域是长方体。为保持与课件一致,设相机看向 z-z,近平面和远平面的坐标分别为 nnff,二者都为负数,并满足 n>fn>f。这里的 n,fn,f 是相机坐标中的有符号位置;实际接口常把近平面、远平面写成正距离 dn,dfd_n,d_f,在该约定下应转换为 n=dnn=-d_nf=dff=-d_f,不要把两种记法直接混用。长方体为

[l,r]×[b,t]×[f,n].[l,r]\times[b,t]\times[f,n].

目标是把它映射到规范立方体 [1,1]3[-1,1]^3。这一过程只有平移和缩放。

长方体中心为

c=(l+r2,b+t2,f+n2).\mathbf c= \left( \frac{l+r}{2}, \frac{b+t}{2}, \frac{f+n}{2} \right).

先平移 c-\mathbf c,各轴区间就关于原点对称;三个边长分别为 rlr-ltbt-bnfn-f,再分别乘以 2/(rl)2/(r-l)2/(tb)2/(t-b)2/(nf)2/(n-f),半边长便变成 1。因此

Mortho=(2rl00002tb00002nf00001)(100r+l2010t+b2001n+f20001).M_{\mathrm{ortho}} = \begin{pmatrix} \frac{2}{r-l}&0&0&0\\ 0&\frac{2}{t-b}&0&0\\ 0&0&\frac{2}{n-f}&0\\ 0&0&0&1 \end{pmatrix} \begin{pmatrix} 1&0&0&-\frac{r+l}{2}\\ 0&1&0&-\frac{t+b}{2}\\ 0&0&1&-\frac{n+f}{2}\\ 0&0&0&1 \end{pmatrix}.

展开后,

Mortho=(2rl00r+lrl02tb0t+btb002nfn+fnf0001).M_{\mathrm{ortho}} = \begin{pmatrix} \frac{2}{r-l}&0&0&-\frac{r+l}{r-l}\\ 0&\frac{2}{t-b}&0&-\frac{t+b}{t-b}\\ 0&0&\frac{2}{n-f}&-\frac{n+f}{n-f}\\ 0&0&0&1 \end{pmatrix}.

代入边界可验证:x=l,rx=l,r 分别映射到 1,1-1,1y=b,ty=b,t 分别映射到 1,1-1,1z=f,nz=f,n 分别映射到 1,1-1,1。正交投影不含除以深度的操作,所以物体的屏幕大小不随远近变化。

正交投影沿三个坐标轴平移并缩放,把可见长方体映射到规范立方体

透视目标

透视投影 Perspective Projection 的可见区域是截头锥。仍采用同一有符号约定:近平面位于 z=n<0z=n<0,其矩形边界为 l,r,b,tl,r,b,t;远平面位于 z=f<nz=f<n。先把截头锥挤压为正交投影使用的长方体,再应用 MorthoM_{\mathrm{ortho}}

取相机空间中的点 P=(x,y,z)P=(x,y,z)。连接原点与 PP 的射线和近平面相交于 P=(x,y,n)P'=(x',y',n)。从 xzxzyzyz 两个截面观察,两个三角形分别相似:

xn=xz,yn=yz.\frac{x'}{n}=\frac{x}{z}, \qquad \frac{y'}{n}=\frac{y}{z}.

因此

x=nxz,y=nyz.x'=\frac{nx}{z}, \qquad y'=\frac{ny}{z}.

由于 n,zn,z 同为负数,比例 n/zn/z 为正。z|z| 越大,比例越小,远处物体自然缩小。

把透视视锥压缩为长方体

这里出现了除以 zz,普通三维线性变换无法直接完成。齐次坐标允许先用矩阵得到四维裁剪坐标,再统一除以第四分量。若令

(xc,yc,wc)=(nx,ny,z),(x_c,y_c,w_c)=(nx,ny,z),

齐次除法后就有

(xcwc,ycwc)=(nxz,nyz).\left( \frac{x_c}{w_c}, \frac{y_c}{w_c} \right) = \left( \frac{nx}{z}, \frac{ny}{z} \right).

因此,挤压矩阵的第一、二、四行已经确定:

(n0000n0000AB0010).\begin{pmatrix} n&0&0&0\\ 0&n&0&0\\ 0&0&A&B\\ 0&0&1&0 \end{pmatrix}.

深度求解

挤压不能丢掉深度,否则后续无法做遮挡判断。xx'yy' 只依赖射线方向,同一条相机射线上的点会投到同一个屏幕位置;深度仍要保持前后顺序。

由于目标深度只应依赖原来的 zz,设第三行产生

zc=Az+B.z_c=Az+B.

齐次除法后的深度是

z=zcwc=Az+Bz.z'=\frac{z_c}{w_c} = \frac{Az+B}{z}.

课件选择让近、远两个平面的深度保持不变。于是

z=nAn+Bn=n,z=n \quad\Longrightarrow\quad \frac{An+B}{n}=n,

z=fAf+Bf=f.z=f \quad\Longrightarrow\quad \frac{Af+B}{f}=f.

整理为二元一次方程:

An+B=n2,Af+B=f2.An+B=n^2, \qquad Af+B=f^2.

两式相减,

A(nf)=n2f2=(nf)(n+f),A(n-f)=n^2-f^2=(n-f)(n+f),

因此

A=n+f.A=n+f.

再代回第一式:

B=n2n(n+f)=nf.B=n^2-n(n+f)=-nf.

截头锥到长方体的挤压矩阵由此得到:

Mpersportho=(n0000n0000n+fnf0010).M_{\mathrm{persp}\rightarrow\mathrm{ortho}} = \begin{pmatrix} n&0&0&0\\ 0&n&0&0\\ 0&0&n+f&-nf\\ 0&0&1&0 \end{pmatrix}.

这个结果可以逐项检查:

  • 在近平面 z=nz=n 上,x=xx'=xy=yy'=yz=nz'=n,整个近平面保持不变;
  • 在远平面 z=fz=f 上,x=nx/fx'=nx/fy=ny/fy'=ny/f,恰好把较大的远平面压到 [l,r]×[b,t][l,r]\times[b,t]
  • 位于近平面和远平面之间的点保持深度顺序,但深度不再线性变化。

透视矩阵先把原来的 zz 放进齐次分量 ww,统一矩阵乘法之后再除以 ww;远小近大的效果来自这次除法。

投影矩阵

完整透视投影先挤压,再做正交归一化:

Mpersp=MorthoMpersportho.M_{\mathrm{persp}} = M_{\mathrm{ortho}} M_{\mathrm{persp}\rightarrow\mathrm{ortho}}.

相乘得到

Mpersp=(2nrl0r+lrl002ntbt+btb000n+fnf2nfnf0010).M_{\mathrm{persp}} = \begin{pmatrix} \frac{2n}{r-l}&0&-\frac{r+l}{r-l}&0\\ 0&\frac{2n}{t-b}&-\frac{t+b}{t-b}&0\\ 0&0&\frac{n+f}{n-f}&-\frac{2nf}{n-f}\\ 0&0&1&0 \end{pmatrix}.

对称视锥满足 l=rl=-rb=tb=-t,第一、二行中的偏移项消失。取近平面左边界 (l,y,n)(l,y,n) 验证:

xndc=2nrllr+lrlnn=1.x_{ndc} = \frac{ \frac{2n}{r-l}l -\frac{r+l}{r-l}n }{n} =-1.

同理,x=rx=r 映射到 11,上下边界也分别映射到 1,1-1,1。矩阵的不同写法经常只差符号、转置或规范深度范围;比较公式前必须先统一列向量与行向量、左右手系、相机朝向和 n,fn,f 的定义。

整个齐次矩阵乘以任意非零常数不会改变除法后的三维点。上面的推导矩阵产生 wc=z<0w_c=z<0;常见图形管线则要求可见点满足 wc>0w_c>0,并用 wcxc,yc,zcwc-w_c\le x_c,y_c,z_c\le w_c 表示齐次裁剪范围。此时应把整个投影矩阵乘以 1-1

Mpersp(+w)=Mpersp=(2nrl0r+lrl002ntbt+btb000n+fnf2nfnf0010).M_{\mathrm{persp}}^{(+w)} = -M_{\mathrm{persp}} = \begin{pmatrix} -\frac{2n}{r-l}&0&\frac{r+l}{r-l}&0\\ 0&-\frac{2n}{t-b}&\frac{t+b}{t-b}&0\\ 0&0&-\frac{n+f}{n-f}&\frac{2nf}{n-f}\\ 0&0&-1&0 \end{pmatrix}.

这样 wc=z>0w_c=-z>0,透视除法后的 NDC 完全不变。只修改最后一行会破坏比例关系,不能得到等价投影。

深度分布

挤压后的深度为

z=(n+f)nfz.z'=(n+f)-\frac{nf}{z}.

再经过正交投影,规范深度为

zndc=n+f2nfznf.z_{ndc} = \frac{n+f-\frac{2nf}{z}} {n-f}.

它含有 1/z1/z,所以透视深度不是相机空间深度的线性缩放。求导可得

dzndcdz=2nf(nf)z2.\frac{\mathrm dz_{ndc}}{\mathrm dz} = \frac{2nf}{(n-f)z^2}.

变化率与 1/z21/z^2 成正比:靠近相机时,同样的世界空间距离占用更多深度值;远处许多深度会挤在很小范围内。把近平面设得过近,会把大量精度浪费在相机附近,使远处近乎共面的表面更容易发生 z-fighting。提高远平面距离的影响通常没有把近平面推近那么剧烈。

不同 API 会把规范深度映射到 [1,1][-1,1][0,1][0,1],也可能使用 reversed-Z 改善浮点深度精度。这些选择改变矩阵系数,却不改变深度来自 1/z1/z 的根本原因。

视场参数

对称视锥常用垂直视场角 fovY\operatorname{fovY}、宽高比 a=W/Ha=W/H 与近平面距离 n|n| 定义。观察 yzyz 截面,半个视场形成直角三角形:

tanfovY2=tn.\tan\frac{\operatorname{fovY}}2 = \frac{t}{|n|}.

因此

t=ntanfovY2,b=t.t=|n|\tan\frac{\operatorname{fovY}}2, \qquad b=-t.

又因为近裁剪面宽高比与屏幕一致,

a=rltb=2r2t=rt,a=\frac{r-l}{t-b} = \frac{2r}{2t} = \frac rt,

所以

r=at,l=r.r=at, \qquad l=-r.

视场越大,近裁剪面在同一距离处越宽,物体在屏幕中越小。近平面不能取零,因为此时相似三角形比例和透视除法都失去意义。推导中的角度按弧度参与三角函数;若接口接收 degree,应先转换为

fovYrad=fovYdegπ180,\operatorname{fovY}_{\mathrm{rad}} = \operatorname{fovY}_{\mathrm{deg}}\frac{\pi}{180},

再传给 tan\tan,不要把 degree 数值直接当作弧度。

裁剪除法

MVP 变换 Model–View–Projection Transformation 按从右到左的顺序作用于列向量:

pclip=MprojMviewMmodelplocal.\mathbf p_{\mathrm{clip}} = M_{\mathrm{proj}} M_{\mathrm{view}} M_{\mathrm{model}} \mathbf p_{\mathrm{local}}.

采用常见的 wc>0w_c>0 齐次裁剪规则时,这里的 MprojM_{\mathrm{proj}} 应取前文整体乘 1-1 后的 Mpersp(+w)M_{\mathrm{persp}}^{(+w)};若使用具体图形 API,则以该 API 的手性、NDC 深度范围和裁剪约定替换,投影矩阵与裁剪规则必须成套一致。

结果 pclip=(xc,yc,zc,wc)T\mathbf p_{\mathrm{clip}}=(x_c,y_c,z_c,w_c)^T 仍是 裁剪坐标 Clip Coordinates。图元应在这一阶段对视锥边界裁剪;若三角形跨过近平面,直接对三个顶点分别除法会得到越过无穷远的错误形状。

裁剪后执行 透视除法 Perspective Divide

(xndc,yndc,zndc)=(xcwc,ycwc,zcwc).(x_{ndc},y_{ndc},z_{ndc}) = \left( \frac{x_c}{w_c}, \frac{y_c}{w_c}, \frac{z_c}{w_c} \right).

矩阵变换作用于齐次坐标,透视除法使最终映射成为非线性投影。MVP 常被口头称为一个变换矩阵,但完整流程还包含裁剪和除法,二者不能省略或随意交换顺序。

视口变换

规范设备坐标 Normalized Device Coordinatesx,y[1,1]x,y\in[-1,1] 还要通过 视口变换 Viewport Transformation 映射到宽 WW、高 HH 的屏幕连续区域:

xs=W2(xndc+1),ys=H2(yndc+1).x_s=\frac{W}{2}(x_{ndc}+1), \qquad y_s=\frac{H}{2}(y_{ndc}+1).

只写屏幕平面映射时,对应齐次矩阵为

Mviewport=(W/200W/20H/20H/200100001).M_{\mathrm{viewport}} = \begin{pmatrix} W/2&0&0&W/2\\ 0&H/2&0&H/2\\ 0&0&1&0\\ 0&0&0&1 \end{pmatrix}.

课程的约定把近、远平面分别映射到 zndc=1,1z_{ndc}=1,-1。若深度缓冲希望近处为 0、远处为 1,可再令

d=1zndc2.d=\frac{1-z_{ndc}}2.

不同 API 也可能使用另一 NDC 范围或 reversed-Z,比较方向必须随约定一起改变。像素 (x,y)(x,y) 的中心位于 (x+0.5,y+0.5)(x+0.5,y+0.5)。只给屏幕 x,yx,y 只能确定一条相机射线,无法唯一逆变换到 3D 点,因为还缺少深度和齐次信息;给定深度以及完整的投影、视图约定后,才能执行反投影 unprojection。视口阶段的 zz 映射只服务深度比较和深度缓冲,不参与屏幕平面位置的恢复。哪些像素被三角形覆盖、每个样本的属性如何插值,属于后续光栅化阶段。