跳转到内容

GLSL 编程/顶点变换

来自维基教科书,开放的书籍,开放的世界

顶点着色器和之后在 OpenGL (ES) 2.0 管道 中的阶段中最重要的一项任务是,将图元(例如三角形)的顶点从原始坐标(例如在 3D 建模工具中指定的坐标)变换到屏幕坐标。虽然可编程的顶点着色器允许以多种方式变换顶点,但一些变换是在顶点着色器后的固定功能阶段执行的。因此,在编程顶点着色器时,了解哪些变换必须在顶点着色器中执行尤为重要。这些变换通常以统一变量的形式指定,并通过矩阵-向量乘法应用于传入的顶点位置和法向量。虽然对于点和方向来说这是直观的,但对于法向量来说就不那么直观了,如 “应用矩阵变换”部分 中所述。

这里,我们将首先概述坐标系以及它们之间的变换,然后讨论各个变换。

相机类比:1. 定位模型,2. 定位相机,3. 调整缩放,4. 裁剪图像

概述:相机类比

[编辑 | 编辑源代码]

将变换顶点的整个过程想象成一个相机类比,如右图所示,步骤和相应的顶点变换是

  1. 定位模型 - 模型变换
  2. 定位相机 - 观察变换
  3. 调整缩放 - 投影变换
  4. 裁剪图像 - 视口变换

前三个变换在顶点着色器中应用。然后,透视除法(可能被认为是投影变换的一部分)在顶点着色器后的固定功能阶段自动应用。视口变换也在这个固定功能阶段自动应用。虽然固定功能阶段中的变换不可修改,但其他变换可以被替换为这里描述的其他类型的变换。然而,了解传统的变换很有用,因为它们可以充分利用裁剪和对变化变量的透视正确插值。

以下概述显示了不同坐标系之间的顶点变换序列,并包括表示变换的矩阵


对象/模型坐标 顶点着色器的输入,即属性中的位置
模型变换:模型矩阵
世界坐标
观察变换:观察矩阵
观察/眼睛坐标
投影变换:投影矩阵
裁剪坐标 顶点着色器的输出,即 gl_Position
透视除法(除以 gl_Position.w
归一化设备坐标
视口变换
屏幕/窗口坐标 片段着色器中的 gl_FragCoord


请注意,模型、观察和投影变换在顶点着色器中应用。透视除法和视口变换在顶点着色器后的固定功能阶段应用。接下来的部分将详细讨论所有这些变换。

模型变换

[编辑 | 编辑源代码]

模型变换指定从对象坐标(也称为模型坐标或局部坐标)到公共世界坐标系的变换。对象坐标通常特定于每个对象或模型,并且通常在 3D 建模工具中指定。另一方面,世界坐标是场景中所有对象的公共坐标系,包括光源、3D 音频源等。由于不同的对象具有不同的对象坐标系,因此模型变换也不同;即,必须对每个对象应用不同的模型变换。

实际上,它会将对象从原点“推开”,并可以选择对其应用旋转。

模型矩阵的结构

[编辑 | 编辑源代码]

模型变换可以用一个 4×4 矩阵表示,我们将其表示为模型矩阵 . 它的结构是

     

是一个 3×3 矩阵,它表示 3D 空间中的线性变换。这包括旋转、缩放和其他不太常见的线性变换的任何组合。t 是一个 3D 向量,表示 3D 空间中的平移(即位移)。t 组合在一个方便的 4×4 矩阵中。从数学角度来说,模型矩阵代表仿射变换:线性变换加上平移。为了使这能够工作,所有三维点都用四维向量表示,第四个坐标等于 1。

当我们将矩阵乘以这样的点 时,三维线性变换和平移的组合会体现在结果中。

  

除了第四个坐标(应该是 1,因为它是点),结果等于

在顶点着色器中访问模型矩阵

[edit | edit source]

模型矩阵 可以定义为一个统一变量,以便在顶点着色器中使用。然而,它通常与视图变换矩阵结合起来形成模型视图矩阵,然后将其设置为统一变量。在一些版本的 OpenGL(ES)中,在顶点着色器中可以使用内置的统一变量 gl_ModelViewMatrix。(另见 “应用矩阵变换”部分。)

计算模型矩阵

[edit | edit source]

严格来说,GLSL 程序员不必担心模型矩阵的计算,因为它以统一变量的形式提供给顶点着色器。事实上,渲染引擎、场景图和游戏引擎通常会提供模型矩阵;因此,顶点着色器的程序员不必担心计算模型矩阵。然而,在现代版本的 OpenGL 和 OpenGL ES 或 WebGL 中开发应用程序时,必须计算模型矩阵。(OpenGL 3.2 之前的版本、新版本 OpenGL 的兼容性配置文件以及 OpenGL ES 1.x 提供了计算模型矩阵的函数。)

模型矩阵通常通过组合对象的基本变换的 4×4 矩阵来计算,特别是平移、旋转和缩放。具体来说,在层次场景图的情况下,对象的父组的所有变换(父、祖父母等)被组合起来形成模型矩阵。让我们来看看最重要的基本变换及其矩阵。

表示由向量 t 执行的平移的 4×4 矩阵是

表示按因子 沿 轴缩放、按因子 沿 轴缩放,以及按因子 沿 轴缩放的 4×4 矩阵是

表示绕归一化轴 旋转角度 的 4×4 矩阵是

绕特定轴的旋转的特殊情况可以很容易地推导出。例如,这些对于实现欧拉角旋转是必要的。但是,欧拉角有多种约定,这里不再讨论。

归一化四元数 对应于绕角度 的旋转。旋转轴的方向可以通过对 3D 向量 进行归一化来确定。

还存在其他基本变换,但对于模型矩阵的计算而言,它们并不那么重要。这些变换或其他变换的 4×4 矩阵可以通过矩阵乘法来组合。假设矩阵 按此特定顺序应用于一个物体。( 可能代表从物体坐标到父组坐标系的变换; 代表从父组到祖父母组的变换; 代表从祖父母组到世界坐标的变换。)那么组合的矩阵乘积为

请注意,矩阵因子的顺序很重要。还要注意,此矩阵乘积应从右(向量相乘的地方)到左阅读,即 首先应用,而 最后应用。

视图坐标系的示意图。

视图变换

[编辑 | 编辑源代码]

视点变换对应于放置和定向相机(或观察者的眼睛)。然而,思考视点变换的最佳方式是它将世界坐标转换为相机的视图坐标系(也称为眼睛坐标系),该相机放置在坐标系的原点,指向 **负** 轴,并放置在 平面上,即向上方向由正 轴给出。

此步骤将整个世界旋转朝向相机,相机始终从原点看向一个固定位置。

在顶点着色器中访问视图矩阵

[edit | edit source]

与模型变换类似,视点变换由一个 4×4 矩阵表示,称为视图矩阵 。它可以定义为顶点着色器的统一变量;但是,它通常与模型矩阵 结合形成模型视图矩阵 。(在某些版本的 OpenGL (ES) 中,顶点着色器中提供了一个内置的统一变量 gl_ModelViewMatrix。)由于模型矩阵首先应用,所以正确的组合是

(另请参阅 “应用矩阵变换”部分。)

计算视图矩阵

[edit | edit source]

与模型矩阵类似,GLSL 程序员不必担心视图矩阵的计算,因为它以统一变量的形式提供给顶点着色器。但是,在开发现代版本的 OpenGL 和 OpenGL ES 或 WebGL 中的应用程序时,有必要计算视图矩阵。(在旧版本的 OpenGL 中,这通常通过一个名为 gluLookAt 的实用程序函数来实现。)

在这里,我们简要总结了如何从相机的方位 t、观察方向 d 和世界向上向量 k(都在世界坐标中)计算视图矩阵 。步骤很简单

1. 计算(在世界坐标中)视图坐标系的 轴的方向 z,作为负的标准化 d 向量

2. 计算(再次在世界坐标中)视图坐标系的 轴的方向 x,方法是

3. 在世界坐标系中计算视图坐标系 轴的方向 y

使用 xyzt,可以轻松确定逆视图矩阵 ,因为该矩阵将原点 (0,0,0) 映射到 t,并将单位向量 (1,0,0)、(0,1,0) 和 (0,0,1) 映射到 xy,z。因此,后一个向量必须位于矩阵 的列中。

但是,我们需要矩阵 ;因此,我们必须计算矩阵 的逆矩阵。请注意,矩阵 的形式为

其中, 是一个 3×3 矩阵,t 是一个 3D 向量。此类矩阵的逆矩阵为

由于在此特定情况下矩阵 是正交的(因为它的列向量是归一化的并且彼此正交), 的逆矩阵只是它的转置,即第四步是计算

  

虽然推导这个结果需要一些线性代数的知识,但最终的计算只需要基本的向量和矩阵运算,并且可以很容易地在任何常见的编程语言中实现。

文艺复兴时期的透视画法:“男人画鲁特琴”由阿尔布雷希特·丢勒,1525年

投影变换和透视除法

[编辑 | 编辑源代码]

首先,投影变换决定投影的类型,例如透视投影或正交投影。透视投影对应于具有缩短的线性透视,而正交投影是没有缩短的正交投影。缩短实际上是通过透视除法完成的;但是,所有控制透视投影的参数都设置在投影变换中。

从技术上讲,投影变换将视图坐标转换为裁剪坐标。(场景中可见部分外部的所有图元部分都在裁剪坐标中被裁剪掉。)它应该是顶点着色器中应用于顶点的最后一个变换,在顶点以 gl_Position 返回之前。然后,这些裁剪坐标通过**透视除法**转换为归一化设备坐标,该除法只是将所有坐标除以第四个坐标。(归一化设备坐标之所以如此命名,是因为它们的取值范围在场景中可见部分的所有点的 -1 到 +1 之间。)

此步骤将物体顶点的 3D 位置转换为屏幕上的 2D 位置。

在顶点着色器中访问投影矩阵

[编辑 | 编辑源代码]

与模型变换和视图变换类似,投影变换由一个 4×4 矩阵表示,称为投影矩阵 。它通常被定义为顶点着色器的uniform变量。(在某些版本的 OpenGL(ES)中,顶点着色器中可以使用内置 uniform 变量 gl_Projection;另请参见“应用矩阵变换”部分。)

计算投影矩阵

[编辑 | 编辑源代码]

与模型视图矩阵类似,GLSL 程序员不必担心投影矩阵的计算。但是,在现代版本的 OpenGL 和 OpenGL ES 或 WebGL 中开发应用程序时,需要计算投影矩阵。在旧版本的 OpenGL 中,这通常使用函数 gluPerspectiveglFrustumglOrtho 来完成。

在这里,我们针对三种情况展示投影矩阵

  • 标准透视投影(对应于 gluPerspective
  • 倾斜透视投影(对应于 glFrustum
  • 正交投影(对应于 glOrtho
角度 的说明,该角度指定了 y 方向的视场。
图示了在 的近裁剪平面和远裁剪平面。

标准透视投影 的特征在于

  • 一个角度 ,它指定了 方向上的视野,如图所示,
  • 到近裁剪平面的距离 和到远裁剪平面的距离 ,如图所示,
  • 近裁剪平面上以中心为原点的矩形的宽高比

连同视点和裁剪平面,这个以中心为原点的矩形定义了视锥体,即对于特定的投影变换可见的 3D 空间区域。所有原语和所有位于视锥体外部的原语部分都会被裁剪掉。近裁剪平面和远裁剪平面是必需的,因为深度值以有限精度存储;因此,不可能覆盖无限大的视锥体。

使用参数 ,透视投影的投影矩阵

  

斜透视投影的参数。

斜透视投影 的特征在于

  • 与标准透视投影情况相同,到裁剪平面的距离为
  • 坐标 (右)、(左)、(上)和 (下),如图所示。这些坐标决定了视锥体的正面矩形的位置;因此,可以指定比使用纵横比 和视野角度 更多的视锥体(例如,偏心)。

给定参数 ,斜透视投影的投影矩阵

正交投影参数。

正交投影(没有透视缩短)如图右侧所示。参数与斜透视投影的情况相同;但是,视锥体(更准确地说,视体积)现在是一个长方体,而不是一个截断的棱锥。

在参数 , , , , , 以及 ,正投影的投影矩阵

视口变换的示意图。

视口变换

[编辑 | 编辑源代码]

投影变换将视坐标映射到裁剪坐标,然后通过裁剪坐标的第四个分量进行透视除法,映射到归一化的设备坐标。在归一化的设备坐标 (ndc) 中,视体积始终是一个以原点为中心的立方体,其中立方体内的坐标介于 -1 和 +1 之间。然后,该立方体通过视口变换映射到屏幕坐标(也称为窗口坐标),如对应图所示。此映射的参数是视口(屏幕上呈现的矩形)左下角的坐标 ,以及它的宽度 和高度 ,以及近裁剪平面和远裁剪平面的深度 。(这些深度介于 0 和 1 之间)。在 OpenGL 和 OpenGL ES 中,这些参数通过两个函数设置

glViewport(GLint , GLint , GLsizei , GLsizei );

glDepthRangef(GLclampf , GLclampf );

视口变换矩阵并不重要,因为它在固定功能阶段自动应用。 然而,为了完整起见,这里提供它。

进一步阅读

[编辑 | 编辑源代码]

此处描述的传统顶点变换在“OpenGL 4.1 Compatibility Profile Specification”的第 2.12 节中进行了详细定义,该规范可在 Khronos OpenGL 网站 上获得。

Dave Shreiner 编写的“OpenGL Programming Guide”一书的第 3 章(关于视图)中给出了对顶点变换更易于理解的描述,该书由 Addison-Wesley 出版。(较旧的版本可在 网上 获得)。


< GLSL 编程

除非另有说明,否则本页上的所有示例源代码均授予公共领域。
华夏公益教科书