前言
由于webGL
存在着几个不同的坐标系:模型坐标系、世界坐标系、相机坐标系和裁剪坐标系;因此将物体的同一坐标表示在不同的坐标系时,就需要对坐标进行转换,而根据不同坐标系之间的特性转换的具体参数可能有不同之处。
坐标系变换矩阵
将坐标从原坐标系变换到新坐标系中,有以下通用变换矩阵:
⎝⎜⎜⎜⎛UxxUxyUxz0UyxUyyUyz0UzxUzyUzz0OxOyOz1⎠⎟⎟⎟⎞−1UxUyUzO=(Uxx, Uxy, Uxz), x轴基向量=(Uyx, Uyy, Uyz), y轴基向量=(Uzx, Uzy, Uzz), z轴基向量=(Ox, Oy, Oz), 原点
其中,Ux,Uy,Uz,O分别是新坐标系的基向量及原点在原坐标系中的表示。只需要将变换矩阵左乘一个列向量,即可得到该向量在新坐标系中的表示。
通过通用的坐标系变换矩阵可以推导出其它变换(平移、旋转、缩放等)的矩阵;
注:左乘是因为得到的变换矩阵是列主序的,为啥矩阵要列主序?因为glsl
中的矩阵就是列主序的!
思考:为啥矩阵要取逆矩阵?
模型变换矩阵
模型变换指的是将坐标从模型坐标系转换到世界坐标系;一个模型内的所有顶点坐标都是相对于该模型的中心点(也就是模型坐标系的原点)而确定的,在实际应用中,一般都只能确定模型中心点在世界坐标系中的坐标,以及平移、旋转、缩放等操作,除此之外,一般模型的中心点的初始位置就是世界坐标系的原点位置(0, 0, 0)
,所以模型变换矩阵可以简化为:
M=T∗R∗STRS:平移矩阵:旋转矩阵:缩放矩阵
思考:为什么能够简化?
注意顺序一定不能弄错,因为矩阵不满足交换律……那么为啥矩阵顺序必须是这样的?原因如下:
- 平移后会影响旋转,因此这里的旋转指的是是绕模型中心进行的旋转操作;所以平移要后于旋转进行。
- 同样,旋转后再进行缩放时,可能会造成缩放的效果不对(主要是旋转后
x, y, z
坐标发生改变,而缩放x, y, z
系数不一致时,就可能对应不上);因此缩放要先于旋转进行。
视图变换矩阵
视图变换指的是将坐标从世界坐标系转换到相机坐标系(也叫观察坐标系);根据通用坐标系变换矩阵公式可知,只需要得到相机坐标系的原点及三个基向量在世界坐标系中的表示,即可得到视图变换矩阵。
需要注意的是相机坐标系是右手系!!!
相机空间
- VRP(View Reference Point):观察参考点,也叫视点(eyepoint);相机坐标系的原点。
- VPN(View Plane Normal):观察平面法向量,也叫观察/相机方向;相机坐标系的
z
轴。
- VUV(View Up Vector):相机顶部正朝向;相机坐标系的
y
轴大概方向。
推导过程
- 首先,相机坐标系原点(即人眼或相机位置)在世界坐标系中的位置是已知的;相机的关注点(
lookat
)的位置也是已知的;VUV
也是已知的;
- 根据关注点和相机位置可以得到
VPN
(即相机坐标系的z
轴基向量,N);
- 根据
VUV
及VPN
的叉乘可以得到相机坐标系的x
轴基向量,U;
- 同理,根据N和U的叉乘可以得到真正的
y
轴基向量,V;
- 将U,V,N以及
VRP
的坐标代入到通用变换公式即可得到视图变换矩阵V:
V=⎝⎜⎜⎜⎛uxuyuz0vxvyvz0nxnynz0OxOyOz1⎠⎟⎟⎟⎞−1=(T∗R)−1=R−1∗T−1=⎝⎜⎜⎜⎜⎛uxvxnx0uyvyny0uzvznz0−U⋅O−V⋅O−N⋅O1⎠⎟⎟⎟⎟⎞RTR−1=RTT−1=⎝⎜⎜⎜⎛uxuyuz0vxvyvz0nxnynz00001⎠⎟⎟⎟⎞=⎝⎜⎜⎜⎛100001000010OxOyOz1⎠⎟⎟⎟⎞=⎝⎜⎜⎜⎛uxvxnx0uyvyny0uzvznz00001⎠⎟⎟⎟⎞=⎝⎜⎜⎜⎛100001000010−Ox−Oy−Oz1⎠⎟⎟⎟⎞
注:当一个矩阵为正交矩阵时(即行向量和列向量两两正交),其转置矩阵等于其逆矩阵!
投影变换矩阵
投影变换指的是将坐标从相机坐标系转换到裁剪坐标系,最终投影变换将得到顶点投影后的标准化设备坐标(Normalized Device Coordinate, NDC
),可以输入到顶点着色器中进行使用。
所谓的标准化设备坐标指的是x, y , z
坐标值的范围都在[-1, 1]
之间。
投影变换跟前面的坐标系之间的变换方法有些不同,裁剪坐标系并不是指定一个坐标原点和三个基向量而形成的,而是通过一些观察参数得到的平截头体(Frustum
,一个空间范围)形成的,这个空间范围除了跟观察参数有关以外还和投影方式有关!
相关概念
实际上在设定观察参数时,用到的参考坐标系仍然是相机坐标系,也就是相当于平截头体是相机空间的子空间,只不过变换后坐标进行了转换。
- 投影点:相机坐标系的原点。
- 近平面:通过设置近平面距离Dnear可得,所以近平面指的是相机坐标系z=−Dnear平面。
- 远平面:同理,设置一个远平面距离Dfar可得,近平面指的是相机坐标系z=−Dfar平面。
- 平截头体的宽高:指的是平截头体在近平面一端上的矩形宽高(远平面一端矩形的宽高可以根据参数推算)。
正交投影
正交投影是平行投影的一种,其投影线是垂直于投影平面(这里的投影平面值的就是近平面)的。因此可以得到正交投影的平截头体为一个长方体:
注:正交投影平截头体的宽高一般是通过设置xleft、xright、ytop和ybottom得到的。
由于投影平面垂直于相机坐标系的z
轴,因此相机坐标系内的一点在投影平面的xyz
坐标只需要对原坐标做一个范围缩放处理即可:
x′=xright−xleftx−xleft∗2−1=xright−xleft2∗(x−xleft)−xright+xleft=xright−xleft2x−(xleft+xright)=xright−xleft2x−xright−xleftxleft+xright
从上面x
坐标的变换可以看到,这种变换实际上就是[xleft,xright]到[−1,1]的变换。同理,y
坐标就是[ybottom,ytop]→[−1,1];而z
坐标则是[zfar,znear]→[−1,1]。
y′z′=ytop−ybottom2y−ytop−ybottomybottom+ytop=znear−zfar2z−znear−zfarzfar+znear
注:由于相机坐标系是右手系,因此znear>zfar!
从投影变换结果(x′,y′,z′)可以得到正交投影的投影变换矩阵:
P正交=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛xright−xleft20000ytop−ybottom20000znear−zfar20−xright−xleftxleft+xright−ytop−ybottomybottom+ytop−znear−zfarzfar+znear1⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞
透视投影
投射投影则是模拟真实世界中看见物体的模式进行的投影,具有立体效果。其平截头体如下所示:
可以看到点和投影点有一组三角相似关系:
进而根据三角相似原理可以得到一组等式:
xx′=yy′=zznear⇒⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧x′y′z′=zznear∗x=zznear∗y=znear
需要注意的是,这里的x′和y′并不是最终我们想要的标准化设备坐标,而是在近平面的投影坐标,实际上还是处于相机坐标系中;因此还需要进行一下范围变换:
x′′y′′=xright−xleftx′−xleft∗2−1=widthx′−(−width/2)∗2−1=width2x′=z∗widthznear∗2x=z∗heightznear∗2y
不过由于投影坐标系是左手系的,因此z
轴方向与相机坐标系刚好相反,需要取负值;其次,由于w分量为z,所以各分量需要先乘以z:
x′′y′′=−widthznear∗2x=−heightznear∗2y
由于投影到平面的点是2D
的(因为投影后所有点的z都相同),不过由于深度测试需要用到z值来判断点在空间中前后顺序,因此需要对原有的z坐标进行线性插值得到一个符合标准化设备坐标系的坐标值;不过,由于齐次坐标的w分量为z,因此实际上应该对z1进行插值:
思考:我在想如果使w=1,那么是不是可以用普通的一次线性函数插值?还有就是为啥一定要利用w的特性来除以x,y,z坐标,而不是直接让其为目标值?这样看起来有点多此一举……
事实上,使用普通一次线性函数z′′=az+b得到的参数似乎对不上公认的那个透视投影矩阵。
z′′=a⋅z1+b
同时,在近平面z′′=−1和远平面z′′=1处有确定的投影关系:
⎩⎪⎪⎨⎪⎪⎧a∗znear1+ba∗zfar1+b=−1=1⇒⎩⎪⎪⎪⎨⎪⎪⎪⎧ab=znear−zfar2∗znear∗zfar=−znear−zfarznear+zfar
经过这么多步转换后,可以得到透视投影矩阵为:
P透视=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛width2∗znear0000height2∗znear0000znear−zfarznear+zfar−100znear−zfar2∗znear∗zfar0⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞
不过上述参数只是从数学上假设的,实际应用中计算透视投影矩阵时通常使用视场角(fovy
)、宽高比(aspect
)、近平面和远平面这四个值来计算透视投影矩阵:
很明显,通过视场角、近平面和宽高比能够算出近平面的宽高;
⎩⎪⎪⎪⎨⎪⎪⎪⎧tan(fovy/2)aspect=znear0.5∗height=heightwidth记 θ=fovy/2⇒P透视=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛aspectcot(θ)0000cot(θ)0000znear−zfarznear+zfar−100znear−zfar2∗znear∗zfar0⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞
相关文档