1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-22 20:25:28 +08:00

Fix all the titles

This commit is contained in:
Meow J
2016-07-06 01:42:55 +08:00
parent 5dad4f21d8
commit 3ef7a53627
44 changed files with 512 additions and 527 deletions

View File

@@ -8,11 +8,11 @@
我们目前使用的所有光照都来自于一个单独的光源,这是空间中的一个点。它的效果不错,但是在真实世界,我们有多种类型的光,它们每个表现都不同。一个光源把光投射到物体上,叫做投光。这个教程里我们讨论几种不同的投光类型。学习模拟不同的光源是你未来丰富你的场景的另一个工具。
我们首先讨论定向光(directional light),接着是作为之前学到知识的扩展的点光(point light),最后我们讨论聚光(Spotlight)。下面的教程我们会把这几种不同的光类型整合到一个场景中。
我们首先讨论定向光(directional light),接着是作为之前学到知识的扩展的点光(point light),最后我们讨论聚光(Spotlight)。下面的教程我们会把这几种不同的光类型整合到一个场景中。
## 定向光(Directional Light)
# 定向光
当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(也被成为平行光),因为所有的光线都有着同一个方向;它会独立于光源的位置。
当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light),因为所有的光线都有着同一个方向;它会独立于光源的位置。
我们知道的定向光源的一个好例子是,太阳。太阳和我们不是无限远,但它也足够远了,在计算光照的时候,我们感觉它就像无限远。在下面的图片里,来自于太阳的所有的光线都被定义为平行光:
@@ -88,9 +88,9 @@ glUniform3f(lightDirPos, -0.2f, -1.0f, -0.3f);
## 定点光(Point Light)
# 点光源
定向光作为全局光可以照亮整个场景,这非常棒,但是另一方面除了定向光,我们通常也需要几个点光,在场景里发亮。点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。
定向光作为全局光可以照亮整个场景,这非常棒,但是另一方面除了定向光,我们通常也需要几个点光源(Point Light),在场景里发亮。点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。
![](http://www.learnopengl.com/img/lighting/light_casters_point.png)
@@ -98,7 +98,7 @@ glUniform3f(lightDirPos, -0.2f, -1.0f, -0.3f);
如果你把10个箱子添加到之前教程的光照场景中你会注意到黑暗中的每个箱子都会有同样的亮度就像箱子在光照的前面没有公式定义光的距离衰减。我们想让黑暗中与光源比较近的箱子被轻微地照亮。
### 衰减(Attenuation)
## 衰减
随着光线穿越距离的变远使得亮度也相应地减少的现象,通常称之为**衰减(Attenuation)**。一种随着距离减少亮度的方式是使用线性等式。这样的一个随着距离减少亮度的线性方程,可以使远处的物体更暗。然而,这样的线性方程效果会有点假。在真实世界,通常光在近处时非常亮,但是一个光源的亮度,开始的时候减少的非常快,之后随着距离的增加,减少的速度会慢下来。我们需要一种不同的方程来减少光的亮度。
@@ -120,9 +120,7 @@ $$
你可以看到当距离很近的时候光有最强的亮度但是随着距离增大亮度明显减弱大约接近100的时候就会慢下来。这就是我们想要的。
#### 选择正确的值
### 选择正确的值
但是我们把这三个项设置为什么值呢正确的值的设置由很多因素决定环境、你希望光所覆盖的距离范围、光的类型等。大多数场合这是经验的问题也要适度调整。下面的表格展示一些各项的值它们模拟现实某种类型的光源覆盖特定的半径距离。第一栏定义一个光的距离它覆盖所给定的项。这些值是大多数光的良好开始它是来自Ogre3D的维基的礼物
@@ -143,7 +141,7 @@ $$
就像你所看到的,常数项\(K_c\)一直都是1.0。一次项\(K_l\)为了覆盖更远的距离通常很小,二次项\(K_q\)就更小了。尝试用这些值进行实验看看它们在你的实现中各自的效果。我们的环境中32到100的距离对大多数光通常就足够了。
#### 实现衰减
### 实现衰减
为了实现衰减在着色器中我们会需要三个额外数值也就是公式的常量、一次项和二次项。最好把它们储存在之前定义的Light结构体中。要注意的是我们计算`lightDir`,就是在前面的教程中我们所做的,不是像之前的定向光的那部分。
@@ -198,28 +196,26 @@ specular *= attenuation;
定点光就是一个可配的置位置和衰减值应用到光照计算中。还有另一种类型光可用于我们照明库当中。
## 聚光灯(Spotlight)
## 聚光
我们要讨论的最后一种类型光是聚光(Spotlight)。聚光是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光的好例子是路灯或手电筒。
我们要讨论的最后一种类型光是聚光(Spotlight)。聚光是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光的好例子是路灯或手电筒。
OpenGL中的聚光用世界空间位置,一个方向和一个指定了聚光半径的切光角来表示。我们计算的每个片段,如果片段在聚光的切光方向之间(就是在圆锥体内),我们就会把片段照亮。下面的图可以让你明白聚光是如何工作的:
OpenGL中的聚光用世界空间位置一个方向和一个指定了聚光半径的切光角来表示。我们计算的每个片段如果片段在聚光的切光方向之间就是在圆锥体内我们就会把片段照亮。下面的图可以让你明白聚光是如何工作的
![](http://www.learnopengl.com/img/lighting/light_casters_spotlight_angles.png)
* `LightDir`:从片段指向光源的向量。
* `SpotDir`:聚光所指向的方向。
* `Phi`\(\phi\):定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
* `Theta`\(\theta\)`LightDir`向量和`SpotDir`向量之间的角度。\(\theta\)值应该比\(\Phi\)值小,这样才会在聚光内。
* `SpotDir`:聚光所指向的方向。
* `Phi`\(\phi\):定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
* `Theta`\(\theta\)`LightDir`向量和`SpotDir`向量之间的角度。\(\theta\)值应该比\(\Phi\)值小,这样才会在聚光内。
所以我们大致要做的是,计算`LightDir`向量和`SpotDir`向量的点乘(返回两个单位向量的点乘,还记得吗?),然后在和切光角\(\phi\)对比。现在你应该明白聚光是我们下面将创建的手电筒的范例。
所以我们大致要做的是,计算`LightDir`向量和`SpotDir`向量的点乘(返回两个单位向量的点乘,还记得吗?),然后在和切光角\(\phi\)对比。现在你应该明白聚光是我们下面将创建的手电筒的范例。
## 手电筒
手电筒(Flashlight)是一个坐落在观察者位置的聚光,通常瞄准玩家透视图的前面。基本上说,一个手电筒是一个普通的聚光,但是根据玩家的位置和方向持续的更新它的位置和方向。
### 手电筒
手电筒是一个坐落在观察者位置的聚光灯,通常瞄准玩家透视图的前面。基本上说,一个手电筒是一个普通的聚光灯,但是根据玩家的位置和方向持续的更新它的位置和方向。
所以我们需要为片段着色器提供的值,是聚光灯的位置向量(来计算光的方向坐标),聚光灯的方向向量和切光角。我们可以把这些值储存在`Light`结构体中:
所以我们需要为片段着色器提供的值,是聚光的位置向量(来计算光的方向坐标),聚光的方向向量和切光角。我们可以把这些值储存在`Light`结构体中:
```c++
struct Light
@@ -241,7 +237,7 @@ glUniform1f(lightSpotCutOffLoc, glm::cos(glm::radians(12.5f)));
你可以看到,我们为切光角设置一个角度,但是我们根据一个角度计算了余弦值,把这个余弦结果传给了片段着色器。这么做的原因是在片段着色器中,我们计算`LightDir`和`SpotDir`向量的点乘,而点乘返回一个余弦值,不是一个角度,所以我们不能直接把一个角度和余弦值对比。为了获得这个角度,我们必须计算点乘结果的反余弦,这个操作开销是很大的。所以为了节约一些性能,我们先计算给定切光角的余弦值,然后把结果传递给片段着色器。由于每个角度都被表示为余弦了,我们可以直接对比它们,而不用进行任何开销高昂的操作。
现在剩下要做的是计算\(\theta\)值,用它和\(\phi\)值对比,以决定我们是否在或不在聚光的内部:
现在剩下要做的是计算\(\theta\)值,用它和\(\phi\)值对比,以决定我们是否在或不在聚光的内部:
```c++
float theta = dot(lightDir, normalize(-light.direction));
@@ -257,25 +253,25 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f);
!!! Important
你可能奇怪为什么if条件中使用>符号而不是<符号。为了在聚光以内,`theta`不是应该比光的切光值更小吗这没错但是不要忘了角度值是以余弦值来表示的一个0度的角表示为1.0的余弦值当一个角是90度的时候被表示为0.0的余弦值,你可以在这里看到:
你可能奇怪为什么if条件中使用>符号而不是<符号。为了在聚光以内,`theta`不是应该比光的切光值更小吗这没错但是不要忘了角度值是以余弦值来表示的一个0度的角表示为1.0的余弦值当一个角是90度的时候被表示为0.0的余弦值,你可以在这里看到:
![](http://www.learnopengl.com/img/lighting/light_casters_cos.png)
现在你可以看到余弦越是接近1.0角度就越小。这就解释了为什么θ需要比切光值更大了。切光值当前被设置为12.5的余弦它等于0.9978所以θ的余弦值在0.9979和1.0之间,片段会在聚光内,被照亮。
现在你可以看到余弦越是接近1.0角度就越小。这就解释了为什么θ需要比切光值更大了。切光值当前被设置为12.5的余弦它等于0.9978所以θ的余弦值在0.9979和1.0之间,片段会在聚光内,被照亮。
运行应用,在聚光内的片段才会被照亮。这看起来像这样:
运行应用,在聚光内的片段才会被照亮。这看起来像这样:
![](http://www.learnopengl.com/img/lighting/light_casters_spotlight_hard.png)
你可以在这里获得[全部源码](http://learnopengl.com/code_viewer.php?code=lighting/light_casters_spotlight_hard)和[片段着色器的源码](http://learnopengl.com/code_viewer.php?code=lighting/light_casters_spotlight_hard&type=fragment)。
它看起来仍然有点假,原因是聚光有了一个硬边。片段着色器一旦到达了聚光的圆锥边缘,它就立刻黑了下来,却没有任何平滑减弱的过度。一个真实的聚光的光会在它的边界处平滑减弱的。
它看起来仍然有点假,原因是聚光有了一个硬边。片段着色器一旦到达了聚光的圆锥边缘,它就立刻黑了下来,却没有任何平滑减弱的过度。一个真实的聚光的光会在它的边界处平滑减弱的。
## 平滑/软化边缘
为创建聚光的平滑边,我们希望去模拟的聚光有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。
为创建聚光的平滑边,我们希望去模拟的聚光有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。
为创建外圆锥,我们简单定义另一个余弦值,它代表聚光的方向向量和外圆锥的向量等于它的半径的角度。然后如果片段在内圆锥和外圆锥之间就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0如果在外面就是0.0。
为创建外圆锥我们简单定义另一个余弦值它代表聚光的方向向量和外圆锥的向量等于它的半径的角度。然后如果片段在内圆锥和外圆锥之间就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0如果在外面就是0.0。
我们可以使用下面的公式计算这样的值:
@@ -283,7 +279,7 @@ $$
\begin{equation} I = \frac{\theta - \gamma}{\epsilon} \end{equation}
$$
这里\(\epsilon\)是内部(\(\phi\))和外部圆锥(\(\gamma\)\epsilon = \phi - \gamma的差。结果\(I\)的值是聚光在当前片段的亮度。
这里\(\epsilon\)是内部(\(\phi\))和外部圆锥(\(\gamma\)\epsilon = \phi - \gamma的差。结果\(I\)的值是聚光在当前片段的亮度。
很难用图画描述出这个公式是怎样工作的,所以我们尝试使用一个例子:
@@ -300,7 +296,7 @@ $$
就像你看到的那样我们基本是根据θ在外余弦和内余弦之间插值。如果你仍然不明白怎么继续,不要担心。你可以简单的使用这个公式计算,当你更加老道和明白的时候再来看。
由于我们现在有了一个亮度值,当在聚光外的时候是个负的当在内部圆锥以内大于1。如果我们适当地把这个值固定我们在片段着色器中就再不需要if-else了我们可以简单地用计算出的亮度值乘以光的元素
由于我们现在有了一个亮度值当在聚光外的时候是个负的当在内部圆锥以内大于1。如果我们适当地把这个值固定我们在片段着色器中就再不需要if-else了我们可以简单地用计算出的亮度值乘以光的元素
```c++
float theta = dot(lightDir, normalize(-light.direction));
@@ -319,9 +315,9 @@ specular* = intensity;
![](http://www.learnopengl.com/img/lighting/light_casters_spotlight.png)
看起来好多了。仔细看看内部和外部切光角,尝试创建一个符合你求的聚光。可以在这里找到应用源码,以及片段的源代码。
看起来好多了。仔细看看内部和外部切光角,尝试创建一个符合你求的聚光。可以在这里找到应用源码,以及片段的源代码。
这样的一个手电筒/聚光类型的灯光非常适合恐怖游戏,结合定向和点光,环境会真的开始被照亮了。[下一个教程](http://learnopengl-cn.readthedocs.org/zh/latest/02%20Lighting/06%20Multiple%20lights/),我们会结合所有我们目前讨论了的光和技巧。
这样的一个手电筒/聚光类型的灯光非常适合恐怖游戏,结合定向和点光,环境会真的开始被照亮了。[下一个教程](http://learnopengl-cn.readthedocs.org/zh/latest/02%20Lighting/06%20Multiple%20lights/),我们会结合所有我们目前讨论了的光和技巧。
## 练习