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

Merge pull request #280 from SuperAoao/new-theme

fix issue: 入门->你好 三角形 格式错误 #263, 正确加载NDC图片和相应描述内容
This commit is contained in:
Gary Wang
2024-01-11 14:23:54 +08:00
committed by GitHub
5 changed files with 19 additions and 23 deletions

View File

@@ -86,14 +86,13 @@ float vertices[] = {
**标准化设备坐标(Normalized Device Coordinates, NDC)**
一旦你的顶点坐标已经在顶点着色器中处理过,它们就应该是**标准化设备坐标**了标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。下面你会看到我们定义的在标准化设备坐标中的三角形(忽略z轴)
<img alt="NDC" src="../../img/01/04/ndc.png" class="noborder" />
一旦你的顶点坐标已经在顶点着色器中处理过,它们就应该是**标准化设备坐标**了标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。下面你会看到我们定义的在标准化设备坐标中的三角形(忽略z轴)
![NDC](../img/01/04/ndc.png "NDC")
与通常的屏幕坐标不同y轴正方向为向上(0, 0)坐标是这个图像的中心,而不是左上角。最终你希望所有(变换过的)坐标都在这个坐标空间中,否则它们就不可见了。
通过使用由<fun>glViewport</fun>函数提供的数据,进行<def>视口变换</def>(Viewport Transform)<def>标准化设备坐标</def>(Normalized Device Coordinates)会变换为<def>屏幕空间坐标</def>(Screen-space Coordinates)。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。
与通常的屏幕坐标不同y轴正方向为向上(0, 0)坐标是这个图像的中心,而不是左上角。最终你希望所有(变换过的)坐标都在这个坐标空间中,否则它们就不可见了。
通过使用由<fun>glViewport</fun>函数提供的数据,进行<def>视口变换</def>(Viewport Transform)<def>标准化设备坐标</def>(Normalized Device Coordinates)会变换为<def>屏幕空间坐标</def>(Screen-space Coordinates)。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。
定义这样的顶点数据以后我们会把它作为输入发送给图形渲染管线的第一个处理阶段顶点着色器。它会在GPU上创建内存用于储存我们的顶点数据还要配置OpenGL如何解释这些内存并且指定其如何发送给显卡。顶点着色器接着会处理我们在内存中指定数量的顶点。
我们通过<def>顶点缓冲对象</def>(Vertex Buffer Objects, VBO)管理这个内存它会在GPU内存通常被称为显存中储存大量顶点。使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上而不是每个顶点发送一次。从CPU把数据发送到显卡相对较慢所以只要可能我们都要尝试尽量一次性发送尽可能多的数据。当数据发送至显卡的内存中后顶点着色器几乎能立即访问顶点这是个非常快的过程。

View File

@@ -121,7 +121,7 @@ glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane,
为了创建一个视图矩阵来变换每个物体把它们变换到从光源视角可见的空间中我们将使用glm::lookAt函数这次从光源的位置看向场景中央。
```c++
glm::mat4 lightView = glm::lookAt(glm::vec(-2.0f, 4.0f, -1.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 lightView = glm::lookAt(glm::vec3(-2.0f, 4.0f, -1.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
```
二者相结合为我们提供了一个光空间的变换矩阵,它将每个世界空间坐标变换到光源处所见到的那个空间;这正是我们渲染深度贴图所需要的。

View File

@@ -122,17 +122,17 @@ glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), aspect, near, far);
```c++
std::vector<glm::mat4> shadowTransforms;
shadowTransforms.push_back(shadowProj *
glm::lookAt(lightPos, lightPos + glm::vec3(1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0));
glm::lookAt(lightPos, lightPos + glm::vec3(1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0)));
shadowTransforms.push_back(shadowProj *
glm::lookAt(lightPos, lightPos + glm::vec3(-1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0));
glm::lookAt(lightPos, lightPos + glm::vec3(-1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0)));
shadowTransforms.push_back(shadowProj *
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,1.0,0.0), glm::vec3(0.0,0.0,1.0));
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,1.0,0.0), glm::vec3(0.0,0.0,1.0)));
shadowTransforms.push_back(shadowProj *
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,-1.0,0.0), glm::vec3(0.0,0.0,-1.0));
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,-1.0,0.0), glm::vec3(0.0,0.0,-1.0)));
shadowTransforms.push_back(shadowProj *
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,0.0,1.0), glm::vec3(0.0,-1.0,0.0));
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,0.0,1.0), glm::vec3(0.0,-1.0,0.0)));
shadowTransforms.push_back(shadowProj *
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,0.0,-1.0), glm::vec3(0.0,-1.0,0.0));
glm::lookAt(lightPos, lightPos + glm::vec3(0.0,0.0,-1.0), glm::vec3(0.0,-1.0,0.0)));
```
这里我们创建了6个视图矩阵把它们乘以投影矩阵来得到6个不同的光空间变换矩阵。glm::lookAt的target参数是它注视的立方体贴图的面的一个方向。
@@ -327,7 +327,7 @@ float ShadowCalculation(vec3 fragPos)
}
```
在这里我们得到了fragment的位置与光的位置之间的不同的向量使用这个向量作为一个方向向量去对立方体贴图进行采样。方向向量不需要是单位向量所以无需对它进行标准化。最后的closestDepth是光源和它最接近的可见fragment之间的标准化的深度值。
在这里我们得到了fragment的位置与光的位置之间的向量使用这个向量作为一个方向向量去对立方体贴图进行采样。方向向量不需要是单位向量所以无需对它进行标准化。最后的closestDepth是光源和它最接近的可见fragment之间的标准化的深度值。
closestDepth值现在在0到1的范围内了所以我们先将其转换回0到far_plane的范围这需要把他乘以far_plane

View File

@@ -68,7 +68,6 @@ void main()
![](../img/05/04/normal_mapping_correct.png)
你可以在这里找到这个简单demo的源代码及其顶点和像素着色器。
然而有个问题限制了刚才讲的那种法线贴图的使用。我们使用的那个法线贴图里面的所有法线向量都是指向正z方向的。上面的例子能用是因为那个平面的表面法线也是指向正z方向的。可是如果我们在表面法线指向正y方向的平面上使用同一个法线贴图会发生什么
@@ -84,7 +83,7 @@ void main()
## 切线空间
法线贴图中的法线向量定义在切线空间中在切线空间中法线永远指着正z方向。切线空间是位于三角形表面之上的空间法线相对于单个三角形的本地参考框架。它就像法线贴图向量的本地空间它们都被定义为指向正z方向无论最终变换到什么方向。使用一个特定的矩阵我们就能将本地/切线空间中的法线向量转成世界或视图空间下,使它们转向到最终的贴图表面的方向。
法线贴图中的法线向量定义在切线空间中在切线空间中法线永远指着正z方向。切线空间是位于三角形表面之上的空间法线相对于单个三角形的局部坐标系。它就像法线贴图向量的局部空间它们都被定义为指向正z方向无论最终变换到什么方向。使用一个特定的矩阵我们就能将本地/切线空间中的法线向量转成世界或视图空间下,使它们转向到最终的贴图表面的方向。
我们可以说上个部分那个朝向正y的法线贴图错误的贴到了表面上。法线贴图被定义在切线空间中所以一种解决问题的方式是计算出一种矩阵把法线从切线空间变换到一个不同的空间这样它们就能和表面法线方向对齐了法线向量都会指向正y方向。切线空间的一大好处是我们可以为任何类型的表面计算出一个这样的矩阵由此我们可以把切线空间的z方向和表面的法线方向对齐。
@@ -338,7 +337,7 @@ void main()
```c++
glm::mat4 model;
model = glm::rotate(model, (GLfloat)glfwGetTime() * -10, glm::normalize(glm::vec3(1.0, 0.0, 1.0)));
glUniformMatrix4fv(modelLoc 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
RenderQuad();
```
@@ -373,9 +372,7 @@ vertex.Tangent = vector;
然后你还必须更新模型加载器用以从带纹理模型中加载法线贴图。wavefront的模型格式.obj导出的法线贴图有点不一样Assimp的aiTextureType_NORMAL并不会加载它的法线贴图而aiTextureType_HEIGHT却能所以我们经常这样加载它们
```c++
vector<Texture> specularMaps = this->loadMaterialTextures(
material, aiTextureType_HEIGHT, "texture_normal"
);
vector normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
```

View File

@@ -24,7 +24,7 @@
视差贴图背后的思想是修改纹理坐标使一个fragment的表面看起来比实际的更高或者更低所有这些都根据观察方向和高度贴图。为了理解它如何工作看看下面砖块表面的图片
[](../img/05/05/parallax_mapping_plane_height.png)
![](../img/05/05/parallax_mapping_plane_height.png)
这里粗糙的红线代表高度贴图中的数值的立体表达,向量\(\color{orange}{\bar{V}}\)代表观察方向。如果平面进行实际位移,观察者会在点\(\color{blue}B\)看到表面。然而我们的平面没有实际上进行位移,观察方向将在点\(\color{green}A\)与平面接触。视差贴图的目的是,在\(\color{green}A\)位置上的fragment不再使用点\(\color{green}A\)的纹理坐标而是使用点\(\color{blue}B\)的。随后我们用点\(\color{blue}B\)的纹理坐标采样,观察者就像看到了点\(\color{blue}B\)一样。
@@ -46,7 +46,7 @@
## 视差贴图
我们将使用一个简单的2D平面在把它发送给GPU之前我们先计算它的切线和副切线向量和法线贴图教程做的差不多。我们将向平面贴diffuse纹理、法线贴图以及一个位移贴图你可以点击链接下载。这个例子中我们将视差贴图和法线贴图连用。因为视差贴图生成表面位移了的幻觉当光照不匹配时这种幻觉就被破坏了。法线贴图通常根据高度贴图生成法线贴图和高度贴图一起用能保证光照能和位移匹配。
我们将使用一个简单的2D平面在把它发送给GPU之前我们先计算它的切线和副切线向量和法线贴图教程做的差不多。我们将向平面贴diffuse纹理、法线贴图以及一个位移贴图你可以点击链接下载。这个例子中我们将视差贴图和法线贴图连用。因为视差贴图生成表面位移了的幻觉当光照不匹配时这种幻觉就被破坏了。法线贴图通常根据高度贴图生成法线贴图和高度贴图一起用能保证光照能和位移匹配。
你可能已经注意到,上面链接上的那个位移贴图和教程一开始的那个高度贴图相比是颜色是相反的。这是因为使用反色高度贴图(也叫深度贴图)去模拟深度比模拟高度更容易。下图反映了这个轻微的改变:
@@ -266,7 +266,7 @@ float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir
视差遮蔽映射(Parallax Occlusion Mapping)和陡峭视差映射的原则相同,但不是用触碰的第一个深度层的纹理坐标,而是在触碰之前和之后,在深度层之间进行线性插值。我们根据表面的高度距离啷个深度层的深度层值的距离来确定线性插值的大小。看看下面的图片就能了解它是如何工作的:
[](../img/05/05/parallax_mapping_parallax_occlusion_mapping_diagram.png)
![](../img/05/05/parallax_mapping_parallax_occlusion_mapping_diagram.png)
你可以看到大部分和陡峭视差映射一样,不一样的地方是有个额外的步骤,两个深度层的纹理坐标围绕着交叉点的线性插值。这也是近似的,但是比陡峭视差映射更精确。
@@ -297,7 +297,7 @@ return finalTexCoords;
你可以在这里找到[源代码](http://www.learnopengl.com/code_viewer.php?code=advanced-lighting/parallax_mapping),及其[顶点](http://www.learnopengl.com/code_viewer.php?code=advanced-lighting/parallax_mapping&type=vertex)和[像素](http://www.learnopengl.com/code_viewer.php?code=advanced-lighting/parallax_mapping_occlusion&type=fragment)着色器。
视差贴图是提升场景细节非常好的技术,但是使用的时候还是要考虑到它会带来一点不自然。大多数时候视差贴图用在地面和墙壁表面,这种情况下查明表面的轮廓并不容易,同时观察角度往往趋向于垂直于表面。这样视差贴图的不自然也就很难能被注意到了,对于提升物体的细节可以祈祷难以置信的效果。
视差贴图是提升场景细节非常好的技术,但是使用的时候还是要考虑到它会带来一点不自然。大多数时候视差贴图用在地面和墙壁表面,这种情况下查明表面的轮廓并不容易,同时观察角度往往趋向于垂直于表面。这样视差贴图的不自然也就很难能被注意到了,对于提升物体的细节可以起到难以置信的效果。