mirror of
https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git
synced 2025-08-23 04:35:28 +08:00
fix issue:高级光照(阴影,法线贴图,视差贴图)问题汇总(翻译错误,图片链接缺失...) #276
This commit is contained in:
@@ -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));
|
||||
```
|
||||
|
||||
二者相结合为我们提供了一个光空间的变换矩阵,它将每个世界空间坐标变换到光源处所见到的那个空间;这正是我们渲染深度贴图所需要的。
|
||||
|
@@ -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:
|
||||
|
||||
|
@@ -68,7 +68,6 @@ void main()
|
||||
|
||||

|
||||
|
||||
你可以在这里找到这个简单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");
|
||||
```
|
||||
|
||||
|
||||
|
@@ -24,7 +24,7 @@
|
||||
|
||||
视差贴图背后的思想是修改纹理坐标使一个fragment的表面看起来比实际的更高或者更低,所有这些都根据观察方向和高度贴图。为了理解它如何工作,看看下面砖块表面的图片:
|
||||
|
||||
[](../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)
|
||||

|
||||
|
||||
你可以看到大部分和陡峭视差映射一样,不一样的地方是有个额外的步骤,两个深度层的纹理坐标围绕着交叉点的线性插值。这也是近似的,但是比陡峭视差映射更精确。
|
||||
|
||||
@@ -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)着色器。
|
||||
|
||||
视差贴图是提升场景细节非常好的技术,但是使用的时候还是要考虑到它会带来一点不自然。大多数时候视差贴图用在地面和墙壁表面,这种情况下查明表面的轮廓并不容易,同时观察角度往往趋向于垂直于表面。这样视差贴图的不自然也就很难能被注意到了,对于提升物体的细节可以祈祷难以置信的效果。
|
||||
视差贴图是提升场景细节非常好的技术,但是使用的时候还是要考虑到它会带来一点不自然。大多数时候视差贴图用在地面和墙壁表面,这种情况下查明表面的轮廓并不容易,同时观察角度往往趋向于垂直于表面。这样视差贴图的不自然也就很难能被注意到了,对于提升物体的细节可以起到难以置信的效果。
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user