顶点着色器和之后在 OpenGL (ES) 2.0 管道 中的阶段中最重要的一项任务是,将图元(例如三角形)的顶点从原始坐标(例如在 3D 建模工具中指定的坐标)变换到屏幕坐标。虽然可编程的顶点着色器允许以多种方式变换顶点,但一些变换是在顶点着色器后的固定功能阶段执行的。因此,在编程顶点着色器时,了解哪些变换必须在顶点着色器中执行尤为重要。这些变换通常以统一变量的形式指定,并通过矩阵-向量乘法应用于传入的顶点位置和法向量。虽然对于点和方向来说这是直观的,但对于法向量来说就不那么直观了,如 “应用矩阵变换”部分 中所述。
这里,我们将首先概述坐标系以及它们之间的变换,然后讨论各个变换。
相机类比:1. 定位模型,2. 定位相机,3. 调整缩放,4. 裁剪图像
将变换顶点的整个过程想象成一个相机类比,如右图所示,步骤和相应的顶点变换是
定位模型 - 模型变换
定位相机 - 观察变换
调整缩放 - 投影变换
裁剪图像 - 视口变换
前三个变换在顶点着色器中应用。然后,透视除法(可能被认为是投影变换的一部分)在顶点着色器后的固定功能阶段自动应用。视口变换也在这个固定功能阶段自动应用。虽然固定功能阶段中的变换不可修改,但其他变换可以被替换为这里描述的其他类型的变换。然而,了解传统的变换很有用,因为它们可以充分利用裁剪和对变化变量的透视正确插值。
以下概述显示了不同坐标系之间的顶点变换序列,并包括表示变换的矩阵
对象/模型坐标
顶点着色器的输入,即属性中的位置
↓
模型变换 :模型矩阵 M object → world {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{world}}}}
世界坐标
↓
观察变换 :观察矩阵 M world → view {\displaystyle \mathrm {M} _{{\text{world}}\to {\text{view}}}}
观察/眼睛坐标
↓
投影变换 :投影矩阵 M projection {\displaystyle \mathrm {M} _{\text{projection}}}
裁剪坐标
顶点着色器的输出,即 gl_Position
↓
透视除法 (除以 gl_Position.w
)
归一化设备坐标
↓
视口变换
屏幕/窗口坐标
片段着色器中的 gl_FragCoord
请注意,模型、观察和投影变换在顶点着色器中应用。透视除法和视口变换在顶点着色器后的固定功能阶段应用。接下来的部分将详细讨论所有这些变换。
模型变换指定从对象坐标(也称为模型坐标或局部坐标)到公共世界坐标系的变换。对象坐标通常特定于每个对象或模型,并且通常在 3D 建模工具中指定。另一方面,世界坐标是场景中所有对象的公共坐标系,包括光源、3D 音频源等。由于不同的对象具有不同的对象坐标系,因此模型变换也不同;即,必须对每个对象应用不同的模型变换。
实际上,它会将对象从原点“推开”,并可以选择对其应用旋转。
模型变换可以用一个 4×4 矩阵表示,我们将其表示为模型矩阵 M object → world {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{world}}}} . 它的结构是
M object → world = [ a 1 , 1 a 1 , 2 a 1 , 3 t 1 a 2 , 1 a 2 , 2 a 2 , 3 t 2 a 3 , 1 a 3 , 2 a 3 , 3 t 3 0 0 0 1 ] {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{world}}}=\left[{\begin{matrix}a_{1,1}&a_{1,2}&a_{1,3}&t_{1}\\a_{2,1}&a_{2,2}&a_{2,3}&t_{2}\\a_{3,1}&a_{3,2}&a_{3,3}&t_{3}\\0&0&0&1\end{matrix}}\right]} with A = [ a 1 , 1 a 1 , 2 a 1 , 3 a 2 , 1 a 2 , 2 a 2 , 3 a 3 , 1 a 3 , 2 a 3 , 3 ] {\displaystyle {\text{ with }}\mathrm {A} =\left[{\begin{matrix}a_{1,1}&a_{1,2}&a_{1,3}\\a_{2,1}&a_{2,2}&a_{2,3}\\a_{3,1}&a_{3,2}&a_{3,3}\end{matrix}}\right]} and t = [ t 1 t 2 t 3 ] {\displaystyle {\text{ and }}\mathbf {t} =\left[{\begin{matrix}t_{1}\\t_{2}\\t_{3}\end{matrix}}\right]}
A {\displaystyle \mathrm {A} } 是一个 3×3 矩阵,它表示 3D 空间中的线性变换。这包括旋转、缩放和其他不太常见的线性变换的任何组合。t 是一个 3D 向量,表示 3D 空间中的平移(即位移)。 M object → world {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{world}}}} 将 A {\displaystyle \mathrm {A} } 和 t 组合在一个方便的 4×4 矩阵中。从数学角度来说,模型矩阵代表仿射变换:线性变换加上平移。为了使这能够工作,所有三维点都用四维向量表示,第四个坐标等于 1。
P = [ p 1 p 2 p 3 1 ] {\displaystyle P=\left[{\begin{matrix}p_{1}\\p_{2}\\p_{3}\\1\end{matrix}}\right]}
当我们将矩阵乘以这样的点 P {\displaystyle P} 时,三维线性变换和平移的组合会体现在结果中。
M object → world P = [ a 1 , 1 a 1 , 2 a 1 , 3 t 1 a 2 , 1 a 2 , 2 a 2 , 3 t 2 a 3 , 1 a 3 , 2 a 3 , 3 t 3 0 0 0 1 ] [ p 1 p 2 p 3 1 ] {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{world}}}\;P=\left[{\begin{matrix}a_{1,1}&a_{1,2}&a_{1,3}&t_{1}\\a_{2,1}&a_{2,2}&a_{2,3}&t_{2}\\a_{3,1}&a_{3,2}&a_{3,3}&t_{3}\\0&0&0&1\end{matrix}}\right]\left[{\begin{matrix}p_{1}\\p_{2}\\p_{3}\\1\end{matrix}}\right]} = [ a 1 , 1 p 1 + a 1 , 2 p 2 + a 1 , 3 p 3 + t 1 a 2 , 1 p 1 + a 2 , 2 p 2 + a 2 , 3 p 3 + t 2 a 3 , 1 p 1 + a 3 , 2 p 2 + a 3 , 3 p 3 + t 3 1 ] {\displaystyle =\left[{\begin{matrix}a_{1,1}p_{1}+a_{1,2}p_{2}+a_{1,3}p_{3}+t_{1}\\a_{2,1}p_{1}+a_{2,2}p_{2}+a_{2,3}p_{3}+t_{2}\\a_{3,1}p_{1}+a_{3,2}p_{2}+a_{3,3}p_{3}+t_{3}\\1\end{matrix}}\right]}
除了第四个坐标(应该是 1,因为它是点),结果等于
A [ p 1 p 2 p 3 ] + [ t 1 t 2 t 3 ] {\displaystyle \mathrm {A} \left[{\begin{matrix}p_{1}\\p_{2}\\p_{3}\end{matrix}}\right]+\left[{\begin{matrix}t_{1}\\t_{2}\\t_{3}\end{matrix}}\right]}
模型矩阵 M object → world {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{world}}}} 可以定义为一个统一变量,以便在顶点着色器中使用。然而,它通常与视图变换矩阵结合起来形成模型视图矩阵,然后将其设置为统一变量。在一些版本的 OpenGL(ES)中,在顶点着色器中可以使用内置的统一变量 gl_ModelViewMatrix
。(另见 “应用矩阵变换”部分 。)
严格来说,GLSL 程序员不必担心模型矩阵的计算,因为它以统一变量的形式提供给顶点着色器。事实上,渲染引擎、场景图和游戏引擎通常会提供模型矩阵;因此,顶点着色器的程序员不必担心计算模型矩阵。然而,在现代版本的 OpenGL 和 OpenGL ES 或 WebGL 中开发应用程序时,必须计算模型矩阵。(OpenGL 3.2 之前的版本、新版本 OpenGL 的兼容性配置文件以及 OpenGL ES 1.x 提供了计算模型矩阵的函数。)
模型矩阵通常通过组合对象的基本变换的 4×4 矩阵来计算,特别是平移、旋转和缩放。具体来说,在层次场景图的情况下,对象的父组的所有变换(父、祖父母等)被组合起来形成模型矩阵。让我们来看看最重要的基本变换及其矩阵。
表示由向量 t = ( t 1 , t 2 , t 3 ) {\displaystyle =(t_{1},t_{2},t_{3})} 执行的平移的 4×4 矩阵是
M translation = [ 1 0 0 t 1 0 1 0 t 2 0 0 1 t 3 0 0 0 1 ] {\displaystyle \mathrm {M} _{\text{translation}}=\left[{\begin{matrix}1&0&0&t_{1}\\0&1&0&t_{2}\\0&0&1&t_{3}\\0&0&0&1\end{matrix}}\right]}
表示按因子 s x {\displaystyle s_{x}} 沿 x {\displaystyle x} 轴缩放、按因子 s y {\displaystyle s_{y}} 沿 y {\displaystyle y} 轴缩放,以及按因子 s z {\displaystyle s_{z}} 沿 z {\displaystyle z} 轴缩放的 4×4 矩阵是
M scaling = [ s x 0 0 0 0 s y 0 0 0 0 s z 0 0 0 0 1 ] {\displaystyle \mathrm {M} _{\text{scaling}}=\left[{\begin{matrix}s_{x}&0&0&0\\0&s_{y}&0&0\\0&0&s_{z}&0\\0&0&0&1\end{matrix}}\right]}
表示绕归一化轴 ( x , y , z ) {\displaystyle (x,y,z)} 旋转角度 α {\displaystyle \alpha } 的 4×4 矩阵是
M rotation = [ ( 1 − cos α ) x x + cos α ( 1 − cos α ) x y − z sin α ( 1 − cos α ) z x + y sin α 0 ( 1 − cos α ) x y + z sin α ( 1 − cos α ) y y + cos α ( 1 − cos α ) y z − x sin α 0 ( 1 − cos α ) z x − y sin α ( 1 − cos α ) y z + x sin α ( 1 − cos α ) z z + cos α 0 0 0 0 1 ] {\displaystyle \mathrm {M} _{\text{rotation}}=\left[{\begin{matrix}(1-\cos \alpha )x\,x+\cos \alpha &(1-\cos \alpha )x\,y-z\sin \alpha &(1-\cos \alpha )z\,x+y\sin \alpha &0\\(1-\cos \alpha )x\,y+z\sin \alpha &(1-\cos \alpha )y\,y+\cos \alpha &(1-\cos \alpha )y\,z-x\sin \alpha &0\\(1-\cos \alpha )z\,x-y\sin \alpha &(1-\cos \alpha )y\,z+x\sin \alpha &(1-\cos \alpha )z\,z+\cos \alpha &0\\0&0&0&1\end{matrix}}\right]}
绕特定轴的旋转的特殊情况可以很容易地推导出。例如,这些对于实现欧拉角旋转是必要的。但是,欧拉角有多种约定,这里不再讨论。
归一化四元数 ( w q , x q , y q , z q ) {\displaystyle (w_{q},x_{q},y_{q},z_{q})} 对应于绕角度 2 arccos ( w q ) {\displaystyle 2\arccos(w_{q})} 的旋转。旋转轴的方向可以通过对 3D 向量 ( x q , y q , z q ) {\displaystyle (x_{q},y_{q},z_{q})} 进行归一化来确定。
还存在其他基本变换,但对于模型矩阵的计算而言,它们并不那么重要。这些变换或其他变换的 4×4 矩阵可以通过矩阵乘法来组合。假设矩阵 M 1 {\displaystyle \mathrm {M} _{1}} 、 M 2 {\displaystyle \mathrm {M} _{2}} 和 M 3 {\displaystyle \mathrm {M} _{3}} 按此特定顺序应用于一个物体。( M 1 {\displaystyle \mathrm {M} _{1}} 可能代表从物体坐标到父组坐标系的变换; M 2 {\displaystyle \mathrm {M} _{2}} 代表从父组到祖父母组的变换; M 3 {\displaystyle \mathrm {M} _{3}} 代表从祖父母组到世界坐标的变换。)那么组合的矩阵乘积为
M combined = M 3 M 2 M 1 {\displaystyle \mathrm {M} _{\text{combined}}=\mathrm {M} _{3}\mathrm {M} _{2}\mathrm {M} _{1}\,\!}
请注意,矩阵因子的顺序很重要。还要注意,此矩阵乘积应从右(向量相乘的地方)到左阅读,即 M 1 {\displaystyle \mathrm {M} _{1}} 首先应用,而 M 3 {\displaystyle \mathrm {M} _{3}} 最后应用。
视图坐标系的示意图。
视点变换对应于放置和定向相机(或观察者的眼睛)。然而,思考视点变换的最佳方式是它将世界坐标转换为相机的视图坐标系(也称为眼睛坐标系),该相机放置在坐标系的原点,指向 **负** z {\displaystyle z} 轴,并放置在 x z {\displaystyle xz} 平面上,即向上方向由正 y {\displaystyle y} 轴给出。
此步骤将整个世界旋转朝向相机,相机始终从原点看向一个固定位置。
与模型变换类似,视点变换由一个 4×4 矩阵表示,称为视图矩阵 M world → view {\displaystyle \mathrm {M} _{{\text{world}}\to {\text{view}}}} 。它可以定义为顶点着色器的统一变量;但是,它通常与模型矩阵 M object → world {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{world}}}} 结合形成模型视图矩阵 M object → view {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{view}}}} 。(在某些版本的 OpenGL (ES) 中,顶点着色器中提供了一个内置的统一变量 gl_ModelViewMatrix
。)由于模型矩阵首先应用,所以正确的组合是
M object → view = M world → view M object → world {\displaystyle \mathrm {M} _{{\text{object}}\to {\text{view}}}=\mathrm {M} _{{\text{world}}\to {\text{view}}}\mathrm {M} _{{\text{object}}\to {\text{world}}}\,\!}
(另请参阅 “应用矩阵变换”部分 。)
与模型矩阵类似,GLSL 程序员不必担心视图矩阵的计算,因为它以统一变量的形式提供给顶点着色器。但是,在开发现代版本的 OpenGL 和 OpenGL ES 或 WebGL 中的应用程序时,有必要计算视图矩阵。(在旧版本的 OpenGL 中,这通常通过一个名为 gluLookAt
的实用程序函数来实现。)
在这里,我们简要总结了如何从相机的方位 t 、观察方向 d 和世界向上向量 k (都在世界坐标中)计算视图矩阵 M world → view {\displaystyle \mathrm {M} _{{\text{world}}\to {\text{view}}}} 。步骤很简单
1. 计算(在世界坐标中)视图坐标系的 z {\displaystyle z} 轴的方向 z ,作为负的标准化 d 向量
z = − d | d | {\displaystyle \mathbf {z} =-{\frac {\mathbf {d} }{|\mathbf {d} |}}}
2. 计算(再次在世界坐标中)视图坐标系的 x {\displaystyle x} 轴的方向 x ,方法是
x = d × k | d × k | {\displaystyle \mathbf {x} ={\frac {\mathbf {d} \times \mathbf {k} }{|\mathbf {d} \times \mathbf {k} |}}}
3. 在世界坐标系中计算视图坐标系 y {\displaystyle y} 轴的方向 y
y = z × x {\displaystyle \mathbf {y} =\mathbf {z} \times \mathbf {x} }
使用 x 、y 、z 和 t ,可以轻松确定逆视图矩阵 M view → world {\displaystyle \mathrm {M} _{{\text{view}}\to {\text{world}}}} ,因为该矩阵将原点 (0,0,0) 映射到 t ,并将单位向量 (1,0,0)、(0,1,0) 和 (0,0,1) 映射到 x 、y, 、z 。因此,后一个向量必须位于矩阵 M view → world {\displaystyle \mathrm {M} _{{\text{view}}\to {\text{world}}}} 的列中。
M view → world = [ x 1 y 1 z 1 t 1 x 2 y 2 z 2 t 2 x 3 y 3 z 3 t 3 0 0 0 1 ] {\displaystyle \mathrm {M} _{{\text{view}}\to {\text{world}}}=\left[{\begin{matrix}x_{1}&y_{1}&z_{1}&t_{1}\\x_{2}&y_{2}&z_{2}&t_{2}\\x_{3}&y_{3}&z_{3}&t_{3}\\0&0&0&1\end{matrix}}\right]}
但是,我们需要矩阵 M world → view {\displaystyle \mathrm {M} _{{\text{world}}\to {\text{view}}}} ;因此,我们必须计算矩阵 M view → world {\displaystyle \mathrm {M} _{{\text{view}}\to {\text{world}}}} 的逆矩阵。请注意,矩阵 M view→world {\displaystyle \mathrm {M} _{\text{view→world}}} 的形式为
M view → world = [ R t 0 T 1 ] {\displaystyle \mathrm {M} _{{\text{view}}\to {\text{world}}}=\left[{\begin{matrix}\mathrm {R} &\mathbf {t} \\\mathbf {0} ^{T}&1\end{matrix}}\right]}
其中, R {\displaystyle \mathrm {R} } 是一个 3×3 矩阵,t 是一个 3D 向量。此类矩阵的逆矩阵为
M view → world − 1 = M world → view = [ R − 1 − R − 1 t 0 T 1 ] {\displaystyle \mathrm {M} _{{\text{view}}\to {\text{world}}}^{-1}=\mathrm {M} _{{\text{world}}\to {\text{view}}}=\left[{\begin{matrix}\mathrm {R} ^{-1}&-\mathrm {R} ^{-1}\mathbf {t} \\\mathbf {0} ^{T}&1\end{matrix}}\right]}
由于在此特定情况下矩阵 R {\displaystyle \mathrm {R} } 是正交的(因为它的列向量是归一化的并且彼此正交), R {\displaystyle \mathrm {R} } 的逆矩阵只是它的转置,即第四步是计算
M world → view = [ R T − R T t 0 T 1 ] {\displaystyle \mathrm {M} _{{\text{world}}\to {\text{view}}}=\left[{\begin{matrix}\mathrm {R} ^{T}&-\mathrm {R} ^{T}\mathbf {t} \\\mathbf {0} ^{T}&1\end{matrix}}\right]} with R = [ x 1 y 1 z 1 x 2 y 2 z 2 x 3 y 3 z 3 ] {\displaystyle {\text{with }}\mathrm {R} =\left[{\begin{matrix}x_{1}&y_{1}&z_{1}\\x_{2}&y_{2}&z_{2}\\x_{3}&y_{3}&z_{3}\end{matrix}}\right]}
虽然推导这个结果需要一些线性代数的知识,但最终的计算只需要基本的向量和矩阵运算,并且可以很容易地在任何常见的编程语言中实现。
文艺复兴时期的透视画法:“男人画鲁特琴”由阿尔布雷希特·丢勒,1525年
首先,投影变换决定投影的类型,例如透视投影或正交投影。透视投影对应于具有缩短的线性透视,而正交投影是没有缩短的正交投影。缩短实际上是通过透视除法完成的;但是,所有控制透视投影的参数都设置在投影变换中。
从技术上讲,投影变换将视图坐标转换为裁剪坐标。(场景中可见部分外部的所有图元部分都在裁剪坐标中被裁剪掉。)它应该是顶点着色器中应用于顶点的最后一个变换,在顶点以 gl_Position
返回之前。然后,这些裁剪坐标通过**透视除法**转换为归一化设备坐标,该除法只是将所有坐标除以第四个坐标。(归一化设备坐标之所以如此命名,是因为它们的取值范围在场景中可见部分的所有点的 -1 到 +1 之间。)
此步骤将物体顶点的 3D 位置转换为屏幕上的 2D 位置。
与模型变换和视图变换类似,投影变换由一个 4×4 矩阵表示,称为投影矩阵 M projection {\displaystyle \mathrm {M} _{\text{projection}}} 。它通常被定义为顶点着色器的uniform变量。(在某些版本的 OpenGL(ES)中,顶点着色器中可以使用内置 uniform 变量 gl_Projection
;另请参见“应用矩阵变换”部分 。)
与模型视图矩阵类似,GLSL 程序员不必担心投影矩阵的计算。但是,在现代版本的 OpenGL 和 OpenGL ES 或 WebGL 中开发应用程序时,需要计算投影矩阵。在旧版本的 OpenGL 中,这通常使用函数 gluPerspective
、glFrustum
或 glOrtho
来完成。
在这里,我们针对三种情况展示投影矩阵
标准透视投影(对应于 gluPerspective
)
倾斜透视投影(对应于 glFrustum
)
正交投影(对应于 glOrtho
)
角度 θ fovy {\displaystyle \theta _{\text{fovy}}} 的说明,该角度指定了 y 方向的视场。
图示了在 z = − n {\displaystyle z=-n} 和 z = − f {\displaystyle z=-f} 的近裁剪平面和远裁剪平面。
标准透视投影 的特征在于
一个角度 θ fovy {\displaystyle \theta _{\text{fovy}}} ,它指定了 y {\displaystyle y} 方向上的视野,如图所示,
到近裁剪平面的距离 n {\displaystyle n} 和到远裁剪平面的距离 f {\displaystyle f} ,如图所示,
近裁剪平面上以中心为原点的矩形的宽高比 a {\displaystyle a} 。
连同视点和裁剪平面,这个以中心为原点的矩形定义了视锥体,即对于特定的投影变换可见的 3D 空间区域。所有原语和所有位于视锥体外部的原语部分都会被裁剪掉。近裁剪平面和远裁剪平面是必需的,因为深度值以有限精度存储;因此,不可能覆盖无限大的视锥体。
使用参数 θ fovy {\displaystyle \theta _{\text{fovy}}} 、 a {\displaystyle a} 、 n {\displaystyle n} 和 f {\displaystyle f} ,透视投影的投影矩阵 M projection {\displaystyle \mathrm {M} _{\text{projection}}} 为
M projection = [ d a 0 0 0 0 d 0 0 0 0 n + f n − f 2 n f n − f 0 0 − 1 0 ] {\displaystyle \mathrm {M} _{\text{projection}}=\left[{\begin{matrix}{\frac {d}{a}}&0&0&0\\0&d&0&0\\0&0&{\frac {n+f}{n-f}}&{\frac {2nf}{n-f}}\\0&0&-1&0\end{matrix}}\right]} with d = 1 tan ( θ fovy / 2 ) {\displaystyle {\text{ with }}d={\frac {1}{\tan(\theta _{\text{fovy}}/2)}}}
斜透视投影的参数。
斜透视投影 的特征在于
与标准透视投影情况相同,到裁剪平面的距离为 n {\displaystyle n} 和 f {\displaystyle f} 。
坐标 r {\displaystyle r} (右)、 l {\displaystyle l} (左)、 t {\displaystyle t} (上)和 b {\displaystyle b} (下),如图所示。这些坐标决定了视锥体的正面矩形的位置;因此,可以指定比使用纵横比 a {\displaystyle a} 和视野角度 θ fovy {\displaystyle \theta _{\text{fovy}}} 更多的视锥体(例如,偏心)。
给定参数 n {\displaystyle n} 、 f {\displaystyle f} 、 r {\displaystyle r} 、 l {\displaystyle l} 、 t {\displaystyle t} 和 b {\displaystyle b} ,斜透视投影的投影矩阵 M projection {\displaystyle \mathrm {M} _{\text{projection}}} 为
M projection = [ 2 n r − l 0 r + l r − l 0 0 2 n t − b t + b t − b 0 0 0 n + f n − f 2 n f n − f 0 0 − 1 0 ] {\displaystyle \mathrm {M} _{\text{projection}}=\left[{\begin{matrix}{\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{matrix}}\right]}
正交投影参数。
正交投影 (没有透视缩短)如图右侧所示。参数与斜透视投影的情况相同;但是,视锥体(更准确地说,视体积)现在是一个长方体,而不是一个截断的棱锥。
在参数 n {\displaystyle n} , f {\displaystyle f} , r {\displaystyle r} , l {\displaystyle l} , t {\displaystyle t} , 以及 b {\displaystyle b} ,正投影的投影矩阵 M projection {\displaystyle \mathrm {M} _{\text{projection}}} 为
M projection = [ 2 r − l 0 0 − r + l r − l 0 2 t − b 0 − t + b t − b 0 0 − 2 f − n − f + n f − n 0 0 0 1 ] {\displaystyle \mathrm {M} _{\text{projection}}=\left[{\begin{matrix}{\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}{f-n}}&-{\frac {f+n}{f-n}}\\0&0&0&1\end{matrix}}\right]}
视口变换的示意图。
投影变换将视坐标映射到裁剪坐标,然后通过裁剪坐标的第四个分量进行透视除法,映射到归一化的设备坐标。在归一化的设备坐标 (ndc) 中,视体积始终是一个以原点为中心的立方体,其中立方体内的坐标介于 -1 和 +1 之间。然后,该立方体通过视口变换映射到屏幕坐标(也称为窗口坐标),如对应图所示。此映射的参数是视口(屏幕上呈现的矩形)左下角的坐标 s x {\displaystyle s_{x}} 和 s y {\displaystyle s_{y}} ,以及它的宽度 w s {\displaystyle w_{s}} 和高度 h s {\displaystyle h_{s}} ,以及近裁剪平面和远裁剪平面的深度 n s {\displaystyle n_{s}} 和 f s {\displaystyle f_{s}} 。(这些深度介于 0 和 1 之间)。在 OpenGL 和 OpenGL ES 中,这些参数通过两个函数设置
glViewport(GLint
s x {\displaystyle s_{x}} , GLint
s y {\displaystyle s_{y}} , GLsizei
w s {\displaystyle w_{s}} , GLsizei
h s {\displaystyle h_{s}} );
glDepthRangef(GLclampf
n s {\displaystyle n_{s}} , GLclampf
f s {\displaystyle f_{s}} );
视口变换矩阵并不重要,因为它在固定功能阶段自动应用。 然而,为了完整起见,这里提供它。
M viewport = [ w s 2 0 0 s x + w s 2 0 h s 2 0 s y + h s 2 0 0 f s − n s 2 n s + f s 2 0 0 0 1 ] {\displaystyle \mathrm {M} _{\text{viewport}}=\left[{\begin{matrix}{\frac {w_{s}}{2}}&0&0&s_{x}+{\frac {w_{s}}{2}}\\0&{\frac {h_{s}}{2}}&0&s_{y}+{\frac {h_{s}}{2}}\\0&0&{\frac {f_{s}-n_{s}}{2}}&{\frac {n_{s}+f_{s}}{2}}\\0&0&0&1\end{matrix}}\right]}
此处描述的传统顶点变换在“OpenGL 4.1 Compatibility Profile Specification”的第 2.12 节中进行了详细定义,该规范可在 Khronos OpenGL 网站 上获得。
Dave Shreiner 编写的“OpenGL Programming Guide”一书的第 3 章(关于视图)中给出了对顶点变换更易于理解的描述,该书由 Addison-Wesley 出版。(较旧的版本可在 网上 获得)。
< GLSL 编程
除非另有说明,否则本页上的所有示例源代码均授予公共领域。