1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-23 04:35:28 +08:00

Fix error in 01-08

This commit is contained in:
EnvyJCD
2016-08-26 00:03:00 +08:00
parent 77f0ab6a47
commit 8b294f4398

View File

@@ -31,7 +31,7 @@
2. 将局部坐标转换为世界坐标,世界坐标是作为一个更大空间范围的坐标系统。这些坐标是相对于世界的原点的。 2. 将局部坐标转换为世界坐标,世界坐标是作为一个更大空间范围的坐标系统。这些坐标是相对于世界的原点的。
3. 接下来我们将世界坐标转换为观察坐标,观察坐标是指以摄像机或观察者的角度观察的坐标。 3. 接下来我们将世界坐标转换为观察坐标,观察坐标是指以摄像机或观察者的角度观察的坐标。
4. 在将坐标处理到观察空间之后,我们需要将其投影到裁剪坐标。裁剪坐标是处理-1.0到1.0范围内并判断哪些顶点将会出现在屏幕上。 4. 在将坐标处理到观察空间之后,我们需要将其投影到裁剪坐标。裁剪坐标是处理-1.0到1.0范围内并判断哪些顶点将会出现在屏幕上。
5. 最后,我们需要将裁剪坐标转换为屏幕坐标,我们将这一过程为**视口变换(Viewport Transform)**。视口变换将位于-1.0到1.0范围的坐标转换到由`glViewport`函数所定义的坐标范围内。最后转换的坐标将会送到光栅器,由光栅器将其转化为片段。 5. 最后,我们需要将裁剪坐标转换为屏幕坐标,我们将这一过程为**视口变换(Viewport Transform)**。视口变换将位于-1.0到1.0范围的坐标转换到由`glViewport`函数所定义的坐标范围内。最后转换的坐标将会送到光栅器,由光栅器将其转化为片段。
你可能了解了每个单独的坐标空间的作用。我们之所以将顶点转换到各个不同的空间的原因是有些操作在特定的坐标系统中才有意义且更方便。例如,当修改对象时,如果在局部空间中则是有意义的;当对对象做相对于其它对象的位置的操作时,在世界坐标系中则是有意义的;等等这些。如果我们愿意,本可以定义一个直接从局部空间到裁剪空间的转换矩阵,但那样会失去灵活性。接下来我们将要更仔细地讨论各个坐标系。 你可能了解了每个单独的坐标空间的作用。我们之所以将顶点转换到各个不同的空间的原因是有些操作在特定的坐标系统中才有意义且更方便。例如,当修改对象时,如果在局部空间中则是有意义的;当对对象做相对于其它对象的位置的操作时,在世界坐标系中则是有意义的;等等这些。如果我们愿意,本可以定义一个直接从局部空间到裁剪空间的转换矩阵,但那样会失去灵活性。接下来我们将要更仔细地讨论各个坐标系。
@@ -47,7 +47,7 @@
如果我们想将我们所有的对象导入到程序当中,它们有可能会全挤在世界的原点上(000),然而这并不是我们想要的结果。我们想为每一个对象定义一个位置,从而使对象位于更大的世界当中。世界空间(World Space)中的坐标就如它们听起来那样:是指顶点相对于(游戏)世界的坐标。物体变换到的最终空间就是世界坐标系,并且你会想让这些物体分散开来摆放(从而显得更真实)。对象的坐标将会从局部坐标转换到世界坐标;该转换是由**模型矩阵(Model Matrix)**实现的。 如果我们想将我们所有的对象导入到程序当中,它们有可能会全挤在世界的原点上(000),然而这并不是我们想要的结果。我们想为每一个对象定义一个位置,从而使对象位于更大的世界当中。世界空间(World Space)中的坐标就如它们听起来那样:是指顶点相对于(游戏)世界的坐标。物体变换到的最终空间就是世界坐标系,并且你会想让这些物体分散开来摆放(从而显得更真实)。对象的坐标将会从局部坐标转换到世界坐标;该转换是由**模型矩阵(Model Matrix)**实现的。
模型矩阵是一种转换矩阵,它能通过对对象进行平移、缩放、旋转来将它置于它本应该在的位置或方向。你可以想象一下,我们需要转换一栋房子,通过将它缩小(因为它在局部坐标系中显得太大了)将它往郊区的方向平移然后沿着y轴往坐标旋转。经过这样的变换之后,它将恰好能够与邻居的房子重合。你能够想到上一节讲到的利用模型矩阵将各个箱子放置到这个屏幕上;我们能够将箱子中的局部坐标转换为观察坐标或世界坐标。 模型矩阵是一种转换矩阵,它能通过对对象进行平移、缩放、旋转来将它置于它本应该在的位置或方向。你可以想象一下,我们需要转换一栋房子,通过将它缩小(因为它在局部坐标系中显得太大了)将它往郊区的方向平移然后沿着y轴往左旋转一点。经过这样的变换之后,它将恰好能够与邻居的房子重合。你能够想到上一节讲到的利用模型矩阵将各个箱子放置到这个屏幕上;我们能够将箱子中的局部坐标转换为观察坐标或世界坐标。
## 观察空间 ## 观察空间
@@ -103,7 +103,7 @@ $$
out = \begin{pmatrix} x /w \\ y / w \\ z / w \end{pmatrix} out = \begin{pmatrix} x /w \\ y / w \\ z / w \end{pmatrix}
$$ $$
每个顶点坐标的分量都会除以它的w分量得到一个距离观察者的较小的顶点坐标。这是也是另一个w分量很重要的原因因为它能够帮助我们进行透投影。最后的结果坐标就是处于标准化设备空间内的。如果你对研究正射投影矩阵和透视投影矩阵是如何计算的很感兴趣(且不会对数学感到恐惧的话)我推荐[这篇由Songho写的文章](http://www.songho.ca/opengl/gl_projectionmatrix.html)。 每个顶点坐标的分量都会除以它的w分量得到一个距离观察者的较小的顶点坐标。这是也是另一个w分量很重要的原因因为它能够帮助我们进行透投影。最后的结果坐标就是处于标准化设备空间内的。如果你对研究正射投影矩阵和透视投影矩阵是如何计算的很感兴趣(且不会对数学感到恐惧的话)我推荐[这篇由Songho写的文章](http://www.songho.ca/opengl/gl_projectionmatrix.html)。
在GLM中可以这样创建一个透视投影矩阵 在GLM中可以这样创建一个透视投影矩阵
@@ -111,7 +111,7 @@ $$
glm::mat4 proj = glm::perspective(45.0f, (float)width/(float)height, 0.1f, 100.0f); glm::mat4 proj = glm::perspective(45.0f, (float)width/(float)height, 0.1f, 100.0f);
``` ```
`glm::perspective`所做的其实就是再次创建了一个定义了可视空间的大的**平截头体**,任何在这个平截头体的对象最后都不会出现在裁剪空间体积内,并且将会受到裁剪。一个透视平截头体可以被可视化为一个不均匀形状的盒子,在这个盒子内部的每个坐标都会被映射到裁剪空间的点。一张透视平截头体的照片如下所示: `glm::perspective`所做的其实就是再次创建了一个定义了可视空间的大的**平截头体**,任何在这个平截头体以外的对象最后都不会出现在裁剪空间体积内,并且将会受到裁剪。一个透视平截头体可以被可视化为一个不均匀形状的盒子,在这个盒子内部的每个坐标都会被映射到裁剪空间的点。一张透视平截头体的照片如下所示:
![ perspective_frustum](http://learnopengl.com/img/getting-started/perspective_frustum.png) ![ perspective_frustum](http://learnopengl.com/img/getting-started/perspective_frustum.png)
@@ -121,7 +121,7 @@ glm::mat4 proj = glm::perspective(45.0f, (float)width/(float)height, 0.1f, 100.0
当你把透视矩阵的*near*值设置太大时(如10.0)OpenGL会将靠近摄像机的坐标都裁剪掉(在0.0和10.0之间),这会导致一个你很熟悉的视觉效果:在太过靠近一个物体的时候视线会直接穿过去。 当你把透视矩阵的*near*值设置太大时(如10.0)OpenGL会将靠近摄像机的坐标都裁剪掉(在0.0和10.0之间),这会导致一个你很熟悉的视觉效果:在太过靠近一个物体的时候视线会直接穿过去。
当使用正射投影时,每一个顶点坐标都会直接映射到裁剪空间中而不经过任何精细的透视划分(它仍然进行透视划分只是w分量没有被操作(它保持为1)因此没有起作用)。因为正射投影没有使用透视远处的对象不会显得小以产生神奇的视觉输出。由于这个原因正射投影主要用于二维渲染以及一些建筑或工程的应用或者是那些我们不需要使用投影来转换顶点的情况下。某些如Blender的进行三维建模的软件有时在建模时会使用正射投影因为它在各个维度下都更准确地描绘了每个物体。下面你能够看到在Blender里面使用两种投影方式的对比 当使用正射投影时,每一个顶点坐标都会直接映射到裁剪空间中而不经过任何精细的透视划分(它仍然进行透视划分只是w分量没有被操作(它保持为1)因此没有起作用)。因为正射投影没有使用透视远处的对象不会显得小以产生神奇的视觉输出。由于这个原因正射投影主要用于二维渲染以及一些建筑或工程的应用或者是那些我们不需要使用投影来转换顶点的情况下。某些如Blender的进行三维建模的软件有时在建模时会使用正射投影因为它在各个维度下都更准确地描绘了每个物体。下面你能够看到在Blender里面使用两种投影方式的对比
![perspective_orthographic](http://learnopengl.com/img/getting-started/perspective_orthographic.png) ![perspective_orthographic](http://learnopengl.com/img/getting-started/perspective_orthographic.png)
@@ -162,7 +162,7 @@ model = glm::rotate(model, -55.0f, glm::vec3(1.0f, 0.0f, 0.0f));
- 将摄像机往后移动跟将整个场景往前移是一样的。 - 将摄像机往后移动跟将整个场景往前移是一样的。
这就是观察空间所做的我们以相反于移动摄像机的方向移动整个场景。因为我们想要往后移动并且OpenGL是一个右手坐标系(Right-handed System)所以我们沿着z轴的方向移动。我们会通过将场景沿着z轴方向平移来实现这个。它会给我们一种我们在往后移动的感觉。 这就是观察空间所做的我们以相反于移动摄像机的方向移动整个场景。因为我们想要往后移动并且OpenGL是一个右手坐标系(Right-handed System)所以我们沿着z轴的方向移动。我们会通过将场景沿着z轴方向平移来实现这个。它会给我们一种我们在往后移动的感觉。
!!! Important !!! Important
@@ -240,7 +240,7 @@ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
## 更多的3D ## 更多的3D
到目前为止我们在二维平面甚至在三维空间中画图所以让我们采取大胆的方式来将我们的二维平面扩展为三维立方体。要渲染一个立方体我们一共需要36个顶点(6个面 x 每个面有2个三角形组成 x 每个三角形有3个顶点)这36个顶点的位置你可以[从这里获取](http://learnopengl.com/code_viewer.php?code=getting-started/cube_vertices)。注意,这一次我们省略了颜色值,因为这次我们只在乎顶点的位置和,我们使用纹理贴图 到目前为止我们在二维平面甚至在三维空间中画图所以让我们采取大胆的方式来将我们的二维平面扩展为三维立方体。要渲染一个立方体我们一共需要36个顶点(6个面 x 每个面有2个三角形组成 x 每个三角形有3个顶点)这36个顶点的位置你可以[从这里获取](http://learnopengl.com/code_viewer.php?code=getting-started/cube_vertices)。注意,这一次我们省略了颜色值,因为我们只使用纹理得到最终的颜色值
为了好玩,我们将让立方体随着时间旋转: 为了好玩,我们将让立方体随着时间旋转:
@@ -264,7 +264,7 @@ glDrawArrays(GL_TRIANGLES, 0, 36);
### Z缓冲区 ### Z缓冲区
OpenGL存储它的所有深度信息于Z缓冲区(Z-buffer)中,也被称为深度缓冲区(Depth Buffer)。GLFW会自动为你生成这样一个缓冲区 (就如它有一个颜色缓冲区来存储输出图像的颜色)。深度存储在每个片段里面(作为片段的z值)当片段输出它的颜色时OpenGL会将它的深度值和z缓冲进行比较然后如果当前的片段在其它片段之后它将会被丢弃然后重写。这个过程称为**深度测试(Depth Testing)**并且它是由OpenGL自动完成的。 OpenGL存储它的所有深度信息于Z缓冲区(Z-buffer)中,也被称为深度缓冲区(Depth Buffer)。GLFW会自动为你生成这样一个缓冲区 (就如它有一个颜色缓冲区来存储输出图像的颜色)。深度存储在每个片段里面(作为片段的z值)当片段想要输出它的颜色时OpenGL会将它的深度值和z缓冲进行比较然后如果当前的片段在其它片段之后它将会被丢弃然后重写。这个过程称为**深度测试(Depth Testing)**并且它是由OpenGL自动完成的。
然而如果我们想要确定OpenGL是否真的执行深度测试首先我们要告诉OpenGL我们想要开启深度测试而这通常是默认关闭的。我们通过`glEnable`函数来开启深度测试。`glEnable`和`glDisable`函数允许我们开启或关闭某一个OpenGL的功能。该功能会一直是开启或关闭的状态直到另一个调用来关闭或开启它。现在我们想开启深度测试就需要开启`GL_DEPTH_TEST` 然而如果我们想要确定OpenGL是否真的执行深度测试首先我们要告诉OpenGL我们想要开启深度测试而这通常是默认关闭的。我们通过`glEnable`函数来开启深度测试。`glEnable`和`glDisable`函数允许我们开启或关闭某一个OpenGL的功能。该功能会一直是开启或关闭的状态直到另一个调用来关闭或开启它。现在我们想开启深度测试就需要开启`GL_DEPTH_TEST`