mirror of
https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git
synced 2025-08-23 12:45:29 +08:00
校对02/03 修改前后文的“像素着色器”为“片段着色器”
This commit is contained in:
@@ -85,7 +85,7 @@ glEnableVertexAttribArray(0);
|
|||||||
|
|
||||||
发光物着色器顶点数据的不完全使用看起来有点低效,但是这些顶点数据已经从立方体对象载入到GPU的内存里了,所以GPU内存不是必须再储存新数据。相对于重新给发光物分配VBO,实际上却是更高效了。
|
发光物着色器顶点数据的不完全使用看起来有点低效,但是这些顶点数据已经从立方体对象载入到GPU的内存里了,所以GPU内存不是必须再储存新数据。相对于重新给发光物分配VBO,实际上却是更高效了。
|
||||||
|
|
||||||
所有光照的计算需要在片段着色器里进行,所以我们需要把法线向量由顶点着色器传递到像素着色器。我们这么做:
|
所有光照的计算需要在片段着色器里进行,所以我们需要把法线向量由顶点着色器传递到片段着色器。我们这么做:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
out vec3 Normal;
|
out vec3 Normal;
|
||||||
@@ -104,7 +104,7 @@ in vec3 Normal;
|
|||||||
|
|
||||||
### 计算环境光照
|
### 计算环境光照
|
||||||
|
|
||||||
每个顶点现在都有了法线向量,但是我们仍然需要光的位置向量和片段的位置向量。由于光的位置是一个静态变量,我们可以简单的在像素着色器中把它声明为uniform:
|
每个顶点现在都有了法线向量,但是我们仍然需要光的位置向量和片段的位置向量。由于光的位置是一个静态变量,我们可以简单的在片段着色器中把它声明为uniform:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
uniform vec3 lightPos;
|
uniform vec3 lightPos;
|
||||||
@@ -137,7 +137,7 @@ void main()
|
|||||||
in vec3 FragPos;
|
in vec3 FragPos;
|
||||||
```
|
```
|
||||||
|
|
||||||
现在,所有需要的变量都设置好了,我们可以在像素着色器中开始光照的计算了。
|
现在,所有需要的变量都设置好了,我们可以在片段着色器中开始光照的计算了。
|
||||||
|
|
||||||
我们需要做的第一件事是计算光源和片段位置之间的方向向量。前面提到,光的方向向量是光的位置向量与片段的位置向量之间的向量差。你可能记得,在变换教程中,我们简单的通过两个向量相减的方式计算向量差。我们同样希望确保所有相关向量最后都转换为单位向量,所以我们把法线和方向向量这个结果都进行标准化:
|
我们需要做的第一件事是计算光源和片段位置之间的方向向量。前面提到,光的方向向量是光的位置向量与片段的位置向量之间的向量差。你可能记得,在变换教程中,我们简单的通过两个向量相减的方式计算向量差。我们同样希望确保所有相关向量最后都转换为单位向量,所以我们把法线和方向向量这个结果都进行标准化:
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ Normal = mat3(transpose(inverse(model))) * normal;
|
|||||||
|
|
||||||
我们选择在世界空间(world space)进行光照计算,但是大多数人趋向于在观察空间(view space)进行光照计算。在观察空间计算的好处是,观察者的位置总是(0, 0, 0),所以这样你直接就获得了观察者位置。可是,我发现出于学习的目的,在世界空间计算光照更符合直觉。如果你仍然希望在视野空间计算光照的话,那就使用观察矩阵应用到所有相关的需要变换的向量(不要忘记,也要改变法线矩阵)。
|
我们选择在世界空间(world space)进行光照计算,但是大多数人趋向于在观察空间(view space)进行光照计算。在观察空间计算的好处是,观察者的位置总是(0, 0, 0),所以这样你直接就获得了观察者位置。可是,我发现出于学习的目的,在世界空间计算光照更符合直觉。如果你仍然希望在视野空间计算光照的话,那就使用观察矩阵应用到所有相关的需要变换的向量(不要忘记,也要改变法线矩阵)。
|
||||||
|
|
||||||
为了得到观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替(它就是观察者)。所以我们把另一个uniform添加到像素着色器,把相应的摄像机位置坐标传给像素着色器:
|
为了得到观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替(它就是观察者)。所以我们把另一个uniform添加到片段着色器,把相应的摄像机位置坐标传给片段着色器:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
uniform vec3 viewPos;
|
uniform vec3 viewPos;
|
||||||
@@ -283,4 +283,3 @@ color = vec4(result, 1.0f);
|
|||||||
在观察空间中计算冯氏光照:[解决方案](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting-exercise2)。
|
在观察空间中计算冯氏光照:[解决方案](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting-exercise2)。
|
||||||
|
|
||||||
尝试实现一个Gouraud光照来模拟冯氏光照,[解决方案](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting-exercise3)
|
尝试实现一个Gouraud光照来模拟冯氏光照,[解决方案](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting-exercise3)
|
||||||
|
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
本文作者JoeyDeVries,由[Django](http://bullteacher.com/14-materials.html)翻译自[http://learnopengl.com](http://learnopengl.com/#!Lighting/Materials)
|
|
||||||
|
|
||||||
# 材质
|
# 材质
|
||||||
|
|
||||||
在真实世界里,每个物体会对光产生不同的反作用。钢比陶瓷花瓶更闪闪发光。比如一块木头不会和钢一样对光做出相同反作用。每个物体对specular高光也有不同的反应。有些物体不会散射很多光却会反射很多光,结果看起来就有一个较小的高光点,其他物体散射的更多,就会产生一个半径更大的高光。如果我们想要在OpenGL中模拟几种类型的物体,我们必须为每个物体定义材质属性。
|
原文 | [Materials](http://learnopengl.com/#!Lighting/Materials)
|
||||||
|
---|---
|
||||||
|
作者 | JoeyDeVries
|
||||||
|
翻译 | [Django](http://bullteacher.com/)
|
||||||
|
校对 | Geequlim
|
||||||
|
|
||||||
在前面的教程中,我们指定一个物体和一个光的颜色来定义物体的图像输出,并使之结合ambient和specular亮度元素。当描述物体的时候,我们可以为3种光照元素:ambient、diffuse、specular光照分别定义一个材质颜色。通过为每个元素指定一个颜色,我们已经对物体的颜色输出有了精密的控制。现在把一个发亮元素添加到这三个颜色里,这是我们需要的所有材质属性:
|
在真实世界里,每个物体会对光产生不同的作用,钢看起来比陶瓷花瓶更闪闪发光。比如一块木头不会和钢一样对光做出相同反光,每个物体对镜面高光也有不同的反应。有些物体不会散射很多光却会反射很多光,结果看起来就有一个较小的高光点,其他物体散射的更多,就会产生一个半径更大的高光。如果我们想要在OpenGL中模拟几种类型的物体,我们必须为每个物体定义材质属性。
|
||||||
|
|
||||||
|
在前面的教程中,我们指定一个物体和一个光的颜色来定义物体的图像输出,并使之结合ambient和specular亮度元素。当描述物体的时候,我们可以使用3种光照元素:环境光(ambient)、散射光(diffuse)、镜面光(specular)定义一个材质颜色。通过为每个元素指定一个颜色,我们已经对物体的颜色输出有了精密的控制。现在把一个镜面高亮元素添加到这三个颜色里,这是我们需要的所有材质属性:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#version 330 core
|
#version 330 core
|
||||||
@@ -18,11 +22,11 @@ struct Material
|
|||||||
uniform Material material;
|
uniform Material material;
|
||||||
```
|
```
|
||||||
|
|
||||||
在像素着色器中,我们创建一个结构体,来储存物体的材质属性。我们也可以把它们储存为独立的uniform值,但是作为一个结构体来储存可以更有条理。我们首先定义结构体的布局,然后简单声明一个uniform变量,以新近创建的结构体作为它的类型。
|
在片段着色器中,我们创建一个结构体,来储存物体的材质属性。我们也可以把它们储存为独立的uniform值,但是作为一个结构体来储存可以更有条理。我们首先定义结构体的布局,然后简单声明一个uniform变量,以新创建的结构体作为它的类型。
|
||||||
|
|
||||||
就像你所看到的,我们为每个Phong光照的元素都定义一个颜色向量。ambient材质向量定义了在ambient光照下这个物体反射的是什么颜色;通常这是和物体颜色相同的颜色。diffuse材质向量定义了在diffuse光照下物体的颜色。diffuse颜色被设置为(和ambient光照一样)物体要求的颜色。specular材质向量设置的是物体受到的specular光的影响的颜色(或者可能是反射一个物体特定的specular高光颜色)。最后,shininess影响specular高光的散射/半径。
|
就像你所看到的,我们为每个冯氏光照模型的元素都定义一个颜色向量。`ambient`材质向量定义了在环境光照下这个物体反射的是什么颜色;通常这是和物体颜色相同的颜色。`diffuse`材质向量定义了在散射光照下物体的颜色。`diffuse`颜色被设置为(和环境光照一样)物体要求的颜色。`specular`材质向量设置的是物体受到的镜面光的影响的颜色(或者可能是反射一个物体特定的镜面高光颜色)。最后,`shininess`影响镜面高光的半径。
|
||||||
|
|
||||||
这四个元素定义了一个物体的材质,通过它们我们能够模拟很多真实世界的材质。这里有一个列表devernay.free.fr展示了几种材质属性,这些材质属性模拟外部世界的真实材质。下面的图片展示了几种真实世界材质对我们的立方体的影响:
|
这四个元素定义了一个物体的材质,通过它们我们能够模拟很多真实世界的材质。这里有一个列表[devernay.free.fr](http://devernay.free.fr/cours/opengl/materials.html)展示了几种材质属性,这些材质属性模拟外部世界的真实材质。下面的图片展示了几种真实世界材质对我们的立方体的影响:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -35,21 +39,21 @@ uniform Material material;
|
|||||||
|
|
||||||
## 设置材质
|
## 设置材质
|
||||||
|
|
||||||
我们在像素着色器中创建了一个uniform材质结构体,所以下面我们希望改变光照计算来顺应新的材质属性。由于所有材质变量都是储存在结构体中,我们可以从uniform变量material取得它们:
|
我们在片段着色器中创建了一个uniform材质结构体,所以下面我们希望改变光照计算来顺应新的材质属性。由于所有材质元素都储存在结构体中,我们可以从uniform变量`material`取得它们:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
// Ambient
|
// 环境光
|
||||||
vec3 ambient = lightColor * material.ambient;
|
vec3 ambient = lightColor * material.ambient;
|
||||||
|
|
||||||
// Diffuse
|
// 漫反射光
|
||||||
vec3 norm = normalize(Normal);
|
vec3 norm = normalize(Normal);
|
||||||
vec3 lightDir = normalize(lightPos - FragPos);
|
vec3 lightDir = normalize(lightPos - FragPos);
|
||||||
float diff = max(dot(norm, lightDir), 0.0);
|
float diff = max(dot(norm, lightDir), 0.0);
|
||||||
vec3 diffuse = lightColor * (diff * material.diffuse);
|
vec3 diffuse = lightColor * (diff * material.diffuse);
|
||||||
|
|
||||||
// Specular
|
// 镜面高光
|
||||||
vec3 viewDir = normalize(viewPos - FragPos);
|
vec3 viewDir = normalize(viewPos - FragPos);
|
||||||
vec3 reflectDir = reflect(-lightDir, norm);
|
vec3 reflectDir = reflect(-lightDir, norm);
|
||||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
|
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
|
||||||
@@ -62,7 +66,7 @@ void main()
|
|||||||
|
|
||||||
就像你所看到的,我么现在获得所有材质结构体的属性,无论在哪儿我们都需要它们,这次通过材质颜色的帮助,计算结果输出的颜色。物体的每个材质属性都乘以它们对应的光照元素。
|
就像你所看到的,我么现在获得所有材质结构体的属性,无论在哪儿我们都需要它们,这次通过材质颜色的帮助,计算结果输出的颜色。物体的每个材质属性都乘以它们对应的光照元素。
|
||||||
|
|
||||||
通过设置适当的uniform,我们可以在应用中设置物体的材质。当设置uniform时,在GLSL中的一个结构体不会被认为有什么特别之处。一个结构体值扮演uniform变量的封装体,所以如果我们希望填充这个结构体,我们就仍然必须设置单独的uniform,但是这次带有结构体名字作为前缀:
|
通过设置适当的uniform,我们可以在应用中设置物体的材质。当设置uniform时,在GLSL中的一个结构体不会被认为有什么特别之处。一个结构体值扮演uniform变量的封装体,所以如果我们希望填充这个结构体,我们就仍然必须设置结构体中的各个元素的uniform值,但是这次带有结构体名字作为前缀:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
|
GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
|
||||||
@@ -76,7 +80,7 @@ glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
|
|||||||
glUniform1f(matShineLoc, 32.0f);
|
glUniform1f(matShineLoc, 32.0f);
|
||||||
```
|
```
|
||||||
|
|
||||||
我们设置ambient和diffuse元素我们喜欢物体所呈现的颜色,设置物体的specular元素为中等亮度颜色;我们不希望specular元素对这个指定物体产生过于强烈的影响。我们同样保持光亮为32。我们现在可以简单的在应用中影响物体的材质。
|
我们为`ambient`和`diffuse`元素设置成我们喜欢物体所呈现的颜色,设置物体的`specular`元素为中等亮度颜色;我们不希望`specular`元素对这个指定物体产生过于强烈的影响。我们同样保持光亮为32。我们现在可以简单的在应用中影响物体的材质。
|
||||||
|
|
||||||
运行程序,会得到下面这样的结果:
|
运行程序,会得到下面这样的结果:
|
||||||
|
|
||||||
@@ -95,13 +99,13 @@ vec3 diffuse = vec3(1.0f) * (diff * material.diffuse);
|
|||||||
vec3 specular = vec3(1.0f) * (spec * material.specular);
|
vec3 specular = vec3(1.0f) * (spec * material.specular);
|
||||||
```
|
```
|
||||||
|
|
||||||
所以物体的每个材质属性返回了每个光照元素的全亮度。这些vec3(1.0)值可以各自独立的影响每个光源,这通常就是我们想要的。现在物体的ambient元素完全地影响了立方体的颜色,可视ambient元素uyinggai对最终颜色有这么大的影响,所以我们要通过设置光的ambient亮度为一个小一点的值的方式,限制ambient颜色:
|
所以物体的每个材质属性返回了每个光照元素的全亮度。这些vec3(1.0)值可以各自独立的影响每个光源,这通常就是我们想要的。现在物体的`ambient`元素完全地展示了立方体的颜色,可是`ambient`元素不应该对最终颜色有这么大的影响,所以我们要通过设置光的`ambient`亮度为一个小一点的值的方式,限制ambient颜色:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
vec3 result = vec3(0.1f) * material.ambient;
|
vec3 result = vec3(0.1f) * material.ambient;
|
||||||
```
|
```
|
||||||
|
|
||||||
我们可以用同样的方式影响光源的diffuse和specular亮度。这和我们前面教程所做的极为相似;你可以说我们已经创建了一些光的属性各自独立地来影响每个光照元素。我们希望为光的属性创建一些与材质结构体相似的东西:
|
我们可以用同样的方式影响光源的`diffuse`和`specular`亮度。这和我们前面教程所做的极为相似;你可以说我们已经创建了一些光的属性各自独立地来影响每个光照元素。我们希望为光的属性创建一些与材质结构体相似的东西:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
struct Light
|
struct Light
|
||||||
@@ -114,9 +118,9 @@ struct Light
|
|||||||
uniform Light light;
|
uniform Light light;
|
||||||
```
|
```
|
||||||
|
|
||||||
一个光源的ambient、diffuse和specular光都有不同的亮度。ambient光通常设置为一个比较低的亮度,因为饿哦们不希望ambient颜色成为主导。光源的diffuse元素通常设置为我们希望光所具有的颜色;经常是一个明亮的白色。specular元素通常设置为vec3(1.0f)的全亮度的发光度。要记住的是我们同样把光的位置添加到结构体中。
|
一个光源的`ambient`、`diffuse`和`specular`光都有不同的亮度。`ambient`光通常设置为一个比较低的亮度,因为我们不希望`ambient`颜色成为主导。光源的`diffuse`元素通常设置为我们希望光所具有的颜色;经常是一个明亮的白色。`specular`元素通常设置为`vec3(1.0f)`的全亮度的发光度。要记住的是我们同样把光的位置添加到结构体中。
|
||||||
|
|
||||||
就像材质uniform一样,我么你需要更新像素着色器:
|
就像材质uniform一样,需要更新片段着色器:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
vec3 ambient = light.ambient * material.ambient;
|
vec3 ambient = light.ambient * material.ambient;
|
||||||
@@ -132,7 +136,7 @@ GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diff
|
|||||||
GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");
|
GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");
|
||||||
|
|
||||||
glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
|
glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
|
||||||
glUniform3f(lightDiffuseLoc, 0.5f, 0.5f, 0.5f); // Let's darken the light a bit to fit the scene
|
glUniform3f(lightDiffuseLoc, 0.5f, 0.5f, 0.5f);
|
||||||
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
|
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -143,28 +147,32 @@ glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
|
|||||||
现在改变物体的外观相对简单了些。我们做点更有趣的事!
|
现在改变物体的外观相对简单了些。我们做点更有趣的事!
|
||||||
|
|
||||||
|
|
||||||
|
## 不同的光源颜色
|
||||||
|
|
||||||
## 不同的光的颜色
|
目前为止,我们只是使用光的颜色去改变它们的独立元素,选择的是从白到灰到黑范围的颜色,不是动感的物体的真实颜色(只是亮度)。由于我们现在简单的取得了光的属性,我们可以随着时间改变它们的颜色,获得的效果有很意思。由于每件事都已经在片段着色器做好了,改变光的颜色很简单,可以立即创建出一些有趣的效果:
|
||||||
|
|
||||||
目前为止,我们只是使用光的颜色去改变它们的独立元素,选择的是从白到灰到黑范围的颜色,不是动感的物体的真实颜色(只是亮度)。由于我们现在简单的取得了光的属性,我们可以随着时间改变它们的颜色,获得的效果有很意思。由于没见识都已经在像素着色器做好了,改变光的颜色很简单,可以立即创建出一些有趣的效果:
|
|
||||||
|
|
||||||
<video src="http://www.learnopengl.com/video/lighting/materials.mp4" controls="controls">
|
<video src="http://www.learnopengl.com/video/lighting/materials.mp4" controls="controls">
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
就像你所看见的,不同的光的颜色极大地影响了物体的颜色输出。由于光的颜色直接影响物体反射的颜色(你可能想起颜色教程),它对视觉输出有显著的影响。
|
就像你所看见的,不同的光的颜色极大地影响了物体的颜色输出。由于光的颜色直接影响物体反射的颜色(你可能想起在颜色教程中有讨论过),它对视觉输出有显著的影响。
|
||||||
|
|
||||||
利用sin和glfwGetTime,改变光的ambient和diffuse颜色,我们可以随着时间流逝简单的改变光的颜色:
|
利用`sin`和`glfwGetTime`,改变光的`ambient`和`diffus`e颜色,我们可以随着时间流逝简单的改变光源颜色:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
glm::vec3 lightColor; lightColor.x = sin(glfwGetTime() * 2.0f);
|
glm::vec3 lightColor; lightColor.x = sin(glfwGetTime() * 2.0f);
|
||||||
lightColor.y = sin(glfwGetTime() * 0.7f);
|
lightColor.y = sin(glfwGetTime() * 0.7f);
|
||||||
lightColor.z = sin(glfwGetTime() * 1.3f);
|
lightColor.z = sin(glfwGetTime() * 1.3f);
|
||||||
|
|
||||||
glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f); // Decrease the influence
|
glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);
|
||||||
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // Low influence
|
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);
|
||||||
|
|
||||||
glUniform3f(lightAmbientLoc, ambientColor.x, ambientColor.y, ambientColor.z);
|
glUniform3f(lightAmbientLoc, ambientColor.x, ambientColor.y, ambientColor.z);
|
||||||
glUniform3f(lightDiffuseLoc, diffuseColor.x, diffuseColor.y, diffuseColor.z);
|
glUniform3f(lightDiffuseLoc, diffuseColor.x, diffuseColor.y, diffuseColor.z);
|
||||||
```
|
```
|
||||||
|
|
||||||
尝试和实验使用这些光照和材质值,看看它们怎样影响图像输出的。你可以从这里找到应用,和像素着色器的源码。
|
尝试和实验使用这些光照和材质值,看看它们怎样影响图像输出的。你可以从这里找到[应用的源代码](http://learnopengl.com/code_viewer.php?code=lighting/materials),[片段着色器](http://learnopengl.com/code_viewer.php?code=lighting/materials&type=fragment)的源码。
|
||||||
|
|
||||||
|
## 练习
|
||||||
|
|
||||||
|
你能像我们教程一开始那样根据一些材质的属性来模拟一个真实世界的物体吗?
|
||||||
|
注意[材质表](http://devernay.free.fr/cours/opengl/materials.html)中的环境光颜色与漫反射光的颜色可能不一样,因为他们并没有把光照强度考虑进去来模拟,你需要将光照颜色的强度改为vec(1.0f)来输出正确的结果:[解决方案,一个蓝绿色的塑料盒子](http://learnopengl.com/code_viewer.php?code=lighting/materials-exercise1)
|
||||||
|
@@ -35,7 +35,7 @@ struct Material
|
|||||||
in vec2 TexCoords;
|
in vec2 TexCoords;
|
||||||
```
|
```
|
||||||
如果你非把ambient颜色设置为不同的值不可(不同于diffuse值),你可以继续保持ambient的vec3,但是整个物体的ambient颜色会继续保持不变。为了使每个原始像素得到不同ambient值,你需要对ambient值单独使用另一个纹理。
|
如果你非把ambient颜色设置为不同的值不可(不同于diffuse值),你可以继续保持ambient的vec3,但是整个物体的ambient颜色会继续保持不变。为了使每个原始像素得到不同ambient值,你需要对ambient值单独使用另一个纹理。
|
||||||
注意,在像素着色器中我们将会再次需要纹理坐标,所以我们声明一个额外输入变量。然后我们简单地从纹理采样,来获得原始像素的diffuse颜色值:
|
注意,在片段着色器中我们将会再次需要纹理坐标,所以我们声明一个额外输入变量。然后我们简单地从纹理采样,来获得原始像素的diffuse颜色值:
|
||||||
```c++
|
```c++
|
||||||
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
|
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
|
||||||
```
|
```
|
||||||
@@ -43,9 +43,9 @@ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords))
|
|||||||
```c++
|
```c++
|
||||||
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
|
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
|
||||||
```
|
```
|
||||||
这就是diffuse贴图的全部内容了。就像你看到的,这不是什么新的东西,但是它却极大提升了视觉品质。为了让它工作,我们需要用到纹理坐标更新顶点数据,把它们作为顶点属性传递到像素着色器,把纹理加载,把纹理绑定到合适的纹理单元。
|
这就是diffuse贴图的全部内容了。就像你看到的,这不是什么新的东西,但是它却极大提升了视觉品质。为了让它工作,我们需要用到纹理坐标更新顶点数据,把它们作为顶点属性传递到片段着色器,把纹理加载,把纹理绑定到合适的纹理单元。
|
||||||
|
|
||||||
更新的顶点数据可以从这里找到。顶点数据现在包括了顶点位置,法线向量和纹理坐标,每个立方体的顶点都有这些属性。让我们更新顶点着色器来接受纹理坐标作为顶点属性,然后发送到像素着色器:
|
更新的顶点数据可以从这里找到。顶点数据现在包括了顶点位置,法线向量和纹理坐标,每个立方体的顶点都有这些属性。让我们更新顶点着色器来接受纹理坐标作为顶点属性,然后发送到片段着色器:
|
||||||
```c++
|
```c++
|
||||||
#version 330 core
|
#version 330 core
|
||||||
layout (location = 0) in vec3 position;
|
layout (location = 0) in vec3 position;
|
||||||
@@ -83,7 +83,7 @@ glBindTexture(GL_TEXTURE_2D, diffuseMap);
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
一个specular高光的亮度可以通过图片中每个纹理的亮度来获得。specular贴图的每个像素可以显示为一个颜色向量,比如:在那里黑色代表颜色向量vec3(0.0f),灰色是vec3(0.5f)。在像素着色器中,我们采样相应的颜色值,把它乘以光的specular亮度。像素越“白”,乘积的结果越大,物体的specualr部分越亮。
|
一个specular高光的亮度可以通过图片中每个纹理的亮度来获得。specular贴图的每个像素可以显示为一个颜色向量,比如:在那里黑色代表颜色向量vec3(0.0f),灰色是vec3(0.5f)。在片段着色器中,我们采样相应的颜色值,把它乘以光的specular亮度。像素越“白”,乘积的结果越大,物体的specualr部分越亮。
|
||||||
|
|
||||||
由于箱子几乎是由木头组成,木头作为一个材质不会有镜面高光,整个不透部分的diffuse纹理被用黑色覆盖:黑色部分不会包含任何specular高光。箱子的铁边有一个修改的specular亮度,它自身更容易受到镜面高光影响,木纹部分则不会。
|
由于箱子几乎是由木头组成,木头作为一个材质不会有镜面高光,整个不透部分的diffuse纹理被用黑色覆盖:黑色部分不会包含任何specular高光。箱子的铁边有一个修改的specular亮度,它自身更容易受到镜面高光影响,木纹部分则不会。
|
||||||
|
|
||||||
@@ -94,14 +94,14 @@ glBindTexture(GL_TEXTURE_2D, diffuseMap);
|
|||||||
|
|
||||||
###15.2.1 specular贴图采样
|
###15.2.1 specular贴图采样
|
||||||
|
|
||||||
一个specular贴图和其他纹理一样,所以代码和diffuse贴图的代码也相似。确保合理的加载了图片,生成一个纹理对象。由于我们在同样的像素着色器中使用另一个纹理采样器,我们必须为specular贴图使用一个不同的纹理单元(查看纹理),所以在渲染前让我们把它绑定到合适的纹理单元
|
一个specular贴图和其他纹理一样,所以代码和diffuse贴图的代码也相似。确保合理的加载了图片,生成一个纹理对象。由于我们在同样的片段着色器中使用另一个纹理采样器,我们必须为specular贴图使用一个不同的纹理单元(查看纹理),所以在渲染前让我们把它绑定到合适的纹理单元
|
||||||
```c++
|
```c++
|
||||||
glUniform1i(glGetUniformLocation(lightingShader.Program, "material.specular"), 1);
|
glUniform1i(glGetUniformLocation(lightingShader.Program, "material.specular"), 1);
|
||||||
...
|
...
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, specularMap);
|
glBindTexture(GL_TEXTURE_2D, specularMap);
|
||||||
```
|
```
|
||||||
然后更新像素着色器材质属性,接受一个sampler2D作为这个specular部分的类型,而不是vec3:
|
然后更新片段着色器材质属性,接受一个sampler2D作为这个specular部分的类型,而不是vec3:
|
||||||
```c++
|
```c++
|
||||||
struct Material
|
struct Material
|
||||||
{
|
{
|
||||||
@@ -124,6 +124,6 @@ color = vec4(ambient + diffuse + specular, 1.0f);
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
你可以在这里找到全部源码。也对比一下你的顶点着色器和像素着色器。
|
你可以在这里找到全部源码。也对比一下你的顶点着色器和片段着色器。
|
||||||
|
|
||||||
使用diffuse和specular贴图,我们可以给相关但简单物体添加一个极为明显的细节。我们可以使用其他纹理贴图,比如法线/bump贴图或者反射贴图,给物体添加更多的细节。但是这些在后面教程才会涉及。把你的箱子给你所有的朋友和家人看,有一天你会很满足,我们的箱子会比现在更漂亮!
|
使用diffuse和specular贴图,我们可以给相关但简单物体添加一个极为明显的细节。我们可以使用其他纹理贴图,比如法线/bump贴图或者反射贴图,给物体添加更多的细节。但是这些在后面教程才会涉及。把你的箱子给你所有的朋友和家人看,有一天你会很满足,我们的箱子会比现在更漂亮!
|
@@ -72,7 +72,7 @@ else if(lightVector.w == 1.0)
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
你可以在这里获得应用的所有代码,这里是顶点和像素着色器代码。
|
你可以在这里获得应用的所有代码,这里是顶点和片段着色器代码。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ glUniform1f(glGetUniformLocation(lightingShader.Program, "light.linear"), 0.09);
|
|||||||
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.quadratic"), 0.032);
|
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.quadratic"), 0.032);
|
||||||
|
|
||||||
```
|
```
|
||||||
在像素着色器中实现衰减很直接:我们根据公式简单的计算衰减值,在乘以ambient、diffuse和specular元素。
|
在片段着色器中实现衰减很直接:我们根据公式简单的计算衰减值,在乘以ambient、diffuse和specular元素。
|
||||||
|
|
||||||
我们需要光源的距离提供给公式;记得我们能够计算向量的长度吗?我们可以通过获取片段和光源之间的不同向量把向量的长度结果作为距离项。我们可以使用GLSL的内建length函数做这件事:
|
我们需要光源的距离提供给公式;记得我们能够计算向量的长度吗?我们可以通过获取片段和光源之间的不同向量把向量的长度结果作为距离项。我们可以使用GLSL的内建length函数做这件事:
|
||||||
```c++
|
```c++
|
||||||
@@ -198,7 +198,7 @@ OpenGL中的聚光灯用世界空间位置,一个方向和一个指定了聚
|
|||||||
|
|
||||||
手电筒是一个坐落在观察者位置的聚光灯,通常瞄准玩家透视图的前面。基本上说,一个手电筒是一个普通的聚光灯,但是根据玩家的位置和方向持续的更新它的位置和方向。
|
手电筒是一个坐落在观察者位置的聚光灯,通常瞄准玩家透视图的前面。基本上说,一个手电筒是一个普通的聚光灯,但是根据玩家的位置和方向持续的更新它的位置和方向。
|
||||||
|
|
||||||
所以我们需要为像素着色器提供的值,是聚光灯的位置向量(来计算光的方向坐标),聚光灯的方向向量和遮光角。我们可以把这些值储存在Light结构体中:
|
所以我们需要为片段着色器提供的值,是聚光灯的位置向量(来计算光的方向坐标),聚光灯的方向向量和遮光角。我们可以把这些值储存在Light结构体中:
|
||||||
```c++
|
```c++
|
||||||
struct Light
|
struct Light
|
||||||
{
|
{
|
||||||
@@ -214,7 +214,7 @@ glUniform3f(lightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z
|
|||||||
glUniform3f(lightSpotdirLoc, camera.Front.x, camera.Front.y, camera.Front.z);
|
glUniform3f(lightSpotdirLoc, camera.Front.x, camera.Front.y, camera.Front.z);
|
||||||
glUniform1f(lightSpotCutOffLoc, glm::cos(glm::radians(12.5f)));
|
glUniform1f(lightSpotCutOffLoc, glm::cos(glm::radians(12.5f)));
|
||||||
```
|
```
|
||||||
你可以看到,我们为遮光角设置一个角度,但是我们根据一个角度计算了余弦值,把这个余弦结果传给了像素着色器。这么做的原因是在像素着色器中,我们计算LightDir和SpotDir向量的点乘,而点乘返回一个余弦值,不是一个角度,所以我们不能直接把一个角度和余弦值对比。为了获得这个角度,我们必须计算点乘结果的反余弦,这个操作开销是很大的。所以为了节约一些性能,我们先计算给定切光角的余弦值,然后把结果传递给像素着色器。由于每个角度都被表示为余弦了,我们可以直接对比它们,而不用进行任何开销高昂的操作。
|
你可以看到,我们为遮光角设置一个角度,但是我们根据一个角度计算了余弦值,把这个余弦结果传给了片段着色器。这么做的原因是在片段着色器中,我们计算LightDir和SpotDir向量的点乘,而点乘返回一个余弦值,不是一个角度,所以我们不能直接把一个角度和余弦值对比。为了获得这个角度,我们必须计算点乘结果的反余弦,这个操作开销是很大的。所以为了节约一些性能,我们先计算给定切光角的余弦值,然后把结果传递给片段着色器。由于每个角度都被表示为余弦了,我们可以直接对比它们,而不用进行任何开销高昂的操作。
|
||||||
|
|
||||||
现在剩下要做的是计算θ值,用它和φ值对比,以决定我们是否在或不在聚光灯的内部:
|
现在剩下要做的是计算θ值,用它和φ值对比,以决定我们是否在或不在聚光灯的内部:
|
||||||
```c++
|
```c++
|
||||||
@@ -237,9 +237,9 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f);
|
|||||||
|
|
||||||
`[](http://www.learnopengl.com/img/lighting/light_casters_spotlight_hard.png)
|
`[](http://www.learnopengl.com/img/lighting/light_casters_spotlight_hard.png)
|
||||||
|
|
||||||
你可以在这里获得全部源码和像素着色器的源码。
|
你可以在这里获得全部源码和片段着色器的源码。
|
||||||
|
|
||||||
它看起来仍然有点假,大部分原因是聚光灯有了一个硬边。像素着色器一旦到达了聚光灯的圆锥边缘,它就立刻黑了下来,却没有任何平滑减弱的过度。一个真实的聚光灯的光会在它的边界处平滑减弱的。
|
它看起来仍然有点假,大部分原因是聚光灯有了一个硬边。片段着色器一旦到达了聚光灯的圆锥边缘,它就立刻黑了下来,却没有任何平滑减弱的过度。一个真实的聚光灯的光会在它的边界处平滑减弱的。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f);
|
|||||||
0.966|15|0.9978|12.5|0.953|17.5|0.966 - 0.953 = 0.0448|0.966 - 0.953 / 0.0448 = 0.29
|
0.966|15|0.9978|12.5|0.953|17.5|0.966 - 0.953 = 0.0448|0.966 - 0.953 / 0.0448 = 0.29
|
||||||
就像你看到的那样我们基本是根据θ在外余弦和内余弦之间插值。如果你仍然不明白怎么继续,不要担心。你可以简单的使用这个公式计算,当你更加老道和明白的时候再来看。
|
就像你看到的那样我们基本是根据θ在外余弦和内余弦之间插值。如果你仍然不明白怎么继续,不要担心。你可以简单的使用这个公式计算,当你更加老道和明白的时候再来看。
|
||||||
|
|
||||||
由于我们现在有了一个亮度值,当在聚光灯外的时候是个负的,当在内部圆锥以内大于1。如果我们适当地把这个值固定,我们在像素着色器中就再不需要if-else了,我们可以简单地用计算出的亮度值乘以光的元素:
|
由于我们现在有了一个亮度值,当在聚光灯外的时候是个负的,当在内部圆锥以内大于1。如果我们适当地把这个值固定,我们在片段着色器中就再不需要if-else了,我们可以简单地用计算出的亮度值乘以光的元素:
|
||||||
```c++
|
```c++
|
||||||
float theta = dot(lightDir, normalize(-light.direction));
|
float theta = dot(lightDir, normalize(-light.direction));
|
||||||
float epsilon = light.cutOff - light.outerCutOff;
|
float epsilon = light.cutOff - light.outerCutOff;
|
||||||
|
Reference in New Issue
Block a user