mirror of
https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git
synced 2025-08-23 12:45:29 +08:00
校对光照部分完成
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
##多光源(Multiple lights)
|
||||
# 多光源(Multiple lights)
|
||||
|
||||
本文作者JoeyDeVries,由Geequlim翻译自[http://learnopengl.com](http://learnopengl.com/#!Lighting/Multiple-lights)
|
||||
原文 | [Multiple lights](http://learnopengl.com/#!Lighting/Multiple-lights)
|
||||
---|---
|
||||
作者 | JoeyDeVries
|
||||
翻译 | [Geequlim](http://geequlim.com)
|
||||
校对 | [Geequlim](http://geequlim.com)
|
||||
|
||||
我们在前面的教程中已经学习了许多关于OpenGL 光照的知识,其中包括冯氏照明(Phong shading)、光照材质(Materials)、光照图(Lighting maps)以及各种投光物。本教程将结合上述所学的知识,创建一个包含六个光源的场景。我们将模拟一个类似阳光的平行光(Directional light)和4个定点光(Point lights)以及一个手电筒(Flashlight).
|
||||
我们在前面的教程中已经学习了许多关于OpenGL 光照的知识,其中包括冯氏照明模型(Phong shading)、光照材质(Materials)、光照图(Lighting maps)以及各种投光物(Light casters)。本教程将结合上述所学的知识,创建一个包含六个光源的场景。我们将模拟一个类似阳光的平行光(Directional light)和4个定点光(Point lights)以及一个手电筒(Flashlight).
|
||||
|
||||
要在场景中使用多光源我们需要封装一些GLSL函数用来计算光照。如果我们对每个光源都去些一遍光照计算的代码,这将是一件令人恶心的事情,并且这些放在main函数中的代码将难以理解,所以我们将一些操作封装为函数。
|
||||
要在场景中使用多光源我们需要封装一些GLSL函数用来计算光照。如果我们对每个光源都去写一遍光照计算的代码,这将是一件令人恶心的事情,并且这些放在main函数中的代码将难以理解,所以我们将一些操作封装为函数。
|
||||
|
||||
GLSL中的函数与C语言的非常相似,它需要一个函数名、一个返回值类型。并且在调用前必须提前声明。接下来我们将为下面的每一种光照来写一个函数。
|
||||
|
||||
@@ -31,7 +35,7 @@ void main()
|
||||
|
||||
即使对每一种光源的运算实现不同,但此算法的结构一般是与上述出入不大的。我们将定义几个用于计算各个光源的函数,并将这些函数的结算结果(返回颜色)添加到输出颜色向量中。例如,靠近被照射物体的光源计算结果将返回比远离背照射物体的光源更明亮的颜色。
|
||||
|
||||
###平行光(Directional light)
|
||||
## 平行光(Directional light)
|
||||
|
||||
我们要在片段着色器中定义一个函数用来计算平行光在对应的照射点上的光照颜色,这个函数需要几个参数并返回一个计算平行光照结果的颜色。
|
||||
|
||||
@@ -47,7 +51,8 @@ struct DirLight {
|
||||
};
|
||||
uniform DirLight dirLight;
|
||||
```
|
||||
之后我们可以将 *dirLight*这个uniform变量作为下面这个函数原型的参数。
|
||||
|
||||
之后我们可以将`dirLight`这个uniform变量作为下面这个函数原型的参数。
|
||||
|
||||
```c++
|
||||
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
|
||||
@@ -57,7 +62,7 @@ vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
|
||||
|
||||
和C/C++一样,我们调用一个函数的前提是这个函数在调用前已经被声明过(此例中我们是在main函数中调用)。通常情况下我们都将函数定义在main函数之后,为了能在main函数中调用这些函数,我们就必须在main函数之前声明这些函数的原型,这就和我们写C语言是一样的。
|
||||
|
||||
你已经知道,这个函数需要一个**DirLight**和两个其他的向量作为参数来计算光照。如果你看过之前的教程的话,你会觉得下面的函数定义得一点也不意外:
|
||||
你已经知道,这个函数需要一个`DirLight`和两个其他的向量作为参数来计算光照。如果你看过之前的教程的话,你会觉得下面的函数定义得一点也不意外:
|
||||
|
||||
```c++
|
||||
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
|
||||
@@ -78,7 +83,7 @@ vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
|
||||
|
||||
我们从之前的教程中复制了代码,并用两个向量来作为函数参数来计算出平行光的光照颜色向量,该结果是一个由该平行光的环境反射、漫反射和镜面反射的各个分量组成的一个向量。
|
||||
|
||||
###定点光(Point light)
|
||||
## 定点光(Point light)
|
||||
|
||||
和计算平行光一样,我们同样需要定义一个函数用于计算定点光照。同样的,我们定义一个包含定点光源所需属性的结构体:
|
||||
|
||||
@@ -98,7 +103,7 @@ struct PointLight {
|
||||
uniform PointLight pointLights[NR_POINT_LIGHTS];
|
||||
```
|
||||
|
||||
如你所见,我们在GLSL中使用预处理器指令来定义定点光源的数目。之后我们使用这个**NR_POINT_LIGHTS**常量来创建一个**PointLight**结构体的数组。和C语言一样,GLSL也是用一对中括号来创建数组的。现在我们有了4个**PointLight**结构体对象了。
|
||||
如你所见,我们在GLSL中使用预处理器指令来定义定点光源的数目。之后我们使用这个`NR_POINT_LIGHTS`常量来创建一个`PointLight`结构体的数组。和C语言一样,GLSL也是用一对中括号来创建数组的。现在我们有了4个`PointLight`结构体对象了。
|
||||
|
||||
!!! Important
|
||||
|
||||
@@ -106,10 +111,9 @@ uniform PointLight pointLights[NR_POINT_LIGHTS];
|
||||
|
||||
```c++
|
||||
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
|
||||
|
||||
```
|
||||
|
||||
这个函数将所有用得到的数据作为它的参数并使用一个vec3作为它的返回值类表示一个顶点光的结算结果。我们再一次聪明地从之前的教程中复制代码来把它定义成下面的样子:
|
||||
这个函数将所有用得到的数据作为它的参数并使用一个`vec3`作为它的返回值类表示一个顶点光的结算结果。我们再一次聪明地从之前的教程中复制代码来把它定义成下面的样子:
|
||||
|
||||
```c++
|
||||
// 计算定点光在确定位置的光照颜色
|
||||
@@ -138,7 +142,7 @@ vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
|
||||
|
||||
有了这个函数我们就可以在main函数中调用它来代替写很多个计算点光源的代码了。通过循环调用此函数就能实现同样的效果,当然代码更简洁。
|
||||
|
||||
###把它们放到一起
|
||||
## 把它们放到一起
|
||||
|
||||
我们现在定义了用于计算平行光和定点光的函数,现在我们把这些代码放到一起,写入文开始的一般结构中:
|
||||
|
||||
@@ -161,10 +165,9 @@ void main()
|
||||
}
|
||||
```
|
||||
|
||||
每一个光源的运算结果都添加到了输出颜色上,输出颜色包含了此场景中的所有光源的影响。
|
||||
如果你想实现手电筒的光照效果,同样的把计算结果添加到输出颜色上。我在这里就把CalcSpotLight的实现留作个读者们的练习吧。
|
||||
每一个光源的运算结果都添加到了输出颜色上,输出颜色包含了此场景中的所有光源的影响。如果你想实现手电筒的光照效果,同样的把计算结果添加到输出颜色上。我在这里就把`CalcSpotLight`的实现留作个读者们的练习吧。
|
||||
|
||||
设置平行光结构体的uniform值和之前所讲过的方式没什么两样,但是你可能想知道如何设置场景中**PointLight**结构体的uniforms变量数组。我们之前并未讨论过如何做这件事。
|
||||
设置平行光结构体的uniform值和之前所讲过的方式没什么两样,但是你可能想知道如何设置场景中`PointLight`结构体的uniforms变量数组。我们之前并未讨论过如何做这件事。
|
||||
|
||||
庆幸的是,这并不是什么难题。设置uniform变量数组和设置单个uniform变量值是相似的,只需要用一个合适的下标就能够检索到数组中我们想要的uniform变量了。
|
||||
|
||||
@@ -172,11 +175,9 @@ void main()
|
||||
glUniform1f(glGetUniformLocation(lightingShader.Program, "pointLights[0].constant"), 1.0f);
|
||||
```
|
||||
|
||||
这样我们检索到pointLights数组中的第一个 PointLight 结构体元素,同时也可以获取到该结构体中的各个属性变量。不幸的是这一位置我们还需要手动
|
||||
对这个四个光源的每一个属性都进行设置,这样手动设置这28个uniform变量是相当乏味的工作。你可以尝试去定义个光源类来为你设置这些uniform属性来减少你的工作,
|
||||
但这依旧不能改变去设置每个uniform属性的事实。
|
||||
这样我们检索到`pointLights`数组中的第一个`PointLight`结构体元素,同时也可以获取到该结构体中的各个属性变量。不幸的是这一位置我们还需要手动对这个四个光源的每一个属性都进行设置,这样手动设置这28个uniform变量是相当乏味的工作。你可以尝试去定义个光源类来为你设置这些uniform属性来减少你的工作,但这依旧不能改变去设置每个uniform属性的事实。
|
||||
|
||||
别忘了,我们还需要为每个光源设置它们的位置。这里,我们定义一个glm::vec3类的数组来包含这些点光源的坐标:
|
||||
别忘了,我们还需要为每个光源设置它们的位置。这里,我们定义一个`glm::vec3`类的数组来包含这些点光源的坐标:
|
||||
|
||||
```c++
|
||||
glm::vec3 pointLightPositions[] = {
|
||||
@@ -201,7 +202,7 @@ glm::vec3 pointLightPositions[] = {
|
||||
|
||||
相信你现在已经对OpenGL的光照有很好的理解了。有了这些知识我们便可以创建丰富有趣的环境和氛围了。快试试改变所有的属性的值来创建你的光照环境吧!
|
||||
|
||||
###练习
|
||||
## 练习
|
||||
|
||||
* 创建一个表示手电筒光的结构体Spotlight并实现CalcSpotLight(...)函数:[解决方案](http://learnopengl.com/code_viewer.php?code=lighting/multiple_lights-exercise1)
|
||||
* 你能通过调节不同的光照属性来重新创建一个不同的氛围吗?[解决方案](http://learnopengl.com/code_viewer.php?code=lighting/multiple_lights-exercise2)
|
Reference in New Issue
Block a user