mirror of
https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git
synced 2025-08-23 04:35:28 +08:00
Fix all the titles
This commit is contained in:
@@ -49,7 +49,7 @@ glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);
|
||||
|
||||
目前有了这些颜色相关的理论已经足够了,接下来我们将创建一个场景用来做更多的实验。
|
||||
|
||||
## 创建一个光照场景
|
||||
# 创建一个光照场景
|
||||
|
||||
在接下来的教程中,我们将通过模拟真实世界中广泛存在的光照和颜色现象来创建有趣的视觉效果。现在我们将在场景中创建一个看得到的物体来代表光源,并且在场景中至少添加一个物体来模拟光照。
|
||||
|
||||
@@ -166,4 +166,4 @@ glBindVertexArray(0);
|
||||
|
||||
如果你在把上述代码片段放到一起编译遇到困难,可以去认真地看看我的[源代码](http://learnopengl.com/code_viewer.php?code=lighting/colors_scene)。你好最自己实现一遍这些操作。
|
||||
|
||||
现在我们有了一些关于颜色的知识,并且创建了一个基本的场景能够绘制一些漂亮的光线。你现在可以阅读[下一个教程](http://learnopengl-cn.readthedocs.org/zh/latest/02%20Lighting/02%20Basic%20Lighting/),真正的魔法即将开始!
|
||||
现在我们有了一些关于颜色的知识,并且创建了一个基本的场景能够绘制一些漂亮的光线。你现在可以阅读[下一节](http://learnopengl-cn.readthedocs.org/zh/latest/02%20Lighting/02%20Basic%20Lighting/),真正的魔法即将开始!
|
||||
|
@@ -16,13 +16,13 @@
|
||||
|
||||
为了创建有趣的视觉场景,我们希望模拟至少这三种光照元素。我们将以最简单的一个开始:**环境光照**。
|
||||
|
||||
## 环境光照(Ambient Lighting)
|
||||
# 环境光照
|
||||
|
||||
光通常都不是来自于同一光源,而是来自散落于我们周围的很多光源,即使它们可能并不是那么显而易见。光的一个属性是,它可以向很多方向发散和反弹,所以光最后到达的地点可能并不是它所临近的直射方向;光能够像这样**反射(Reflect)**到其他表面,一个物体的光照可能受到来自一个非直射的光源影响。考虑到这种情况的算法叫做**全局照明(Global Illumination)**算法,但是这种算法既开销高昂又极其复杂。
|
||||
|
||||
因为我们不是复杂和昂贵算法的死忠粉丝,所以我们将会使用一种简化的全局照明模型,叫做环境光照。如你在前面章节所见,我们使用一个(数值)很小的常量(光)颜色添加进物体**片段**(Fragment,指当前讨论的光线在物体上的照射点)的最终颜色里,这看起来就像即使没有直射光源也始终存在着一些发散的光。
|
||||
因为我们不是复杂和昂贵算法的死忠粉丝,所以我们将会使用一种简化的全局照明模型,叫做环境光照(Ambient Lighting)。如你在前面章节所见,我们使用一个(数值)很小的常量(光)颜色添加进物体**片段**(Fragment,指当前讨论的光线在物体上的照射点)的最终颜色里,这看起来就像即使没有直射光源也始终存在着一些发散的光。
|
||||
|
||||
把环境光添加到场景里非常简单。我们用光的颜色乘以一个(数值)很小常量环境因子,再乘以物体的颜色,然后使用它作为片段的颜色:
|
||||
把环境光照添加到场景里非常简单。我们用光的颜色乘以一个(数值)很小常量环境因子,再乘以物体的颜色,然后使用它作为片段的颜色:
|
||||
|
||||
```c++
|
||||
void main()
|
||||
@@ -38,9 +38,9 @@ void main()
|
||||
|
||||

|
||||
|
||||
## 漫反射光照(Diffuse Lighting)
|
||||
# 漫反射光照
|
||||
|
||||
环境光本身不提供最明显的光照效果,但是漫反射光照会对物体产生显著的视觉影响。漫反射光使物体上与光线排布越近的片段越能从光源处获得更多的亮度。为了更好的理解漫反射光照,请看下图:
|
||||
环境光本身不提供最明显的光照效果,但是漫反射光照(Diffuse Lighting)会对物体产生显著的视觉影响。漫反射光使物体上与光线排布越近的片段越能从光源处获得更多的亮度。为了更好的理解漫反射光照,请看下图:
|
||||
|
||||

|
||||
|
||||
@@ -59,9 +59,9 @@ void main()
|
||||
- 法向量:一个垂直于顶点表面的向量。
|
||||
- 定向的光线:作为光的位置和片段的位置之间的向量差的方向向量。为了计算这个光线,我们需要光的位置向量和片段的位置向量。
|
||||
|
||||
### 法向量(Normal Vector)
|
||||
## 法向量
|
||||
|
||||
法向量是垂直于顶点表面的(单位)向量。由于顶点自身并没有表面(它只是空间中一个独立的点),我们利用顶点周围的顶点计算出这个顶点的表面。我们能够使用叉乘这个技巧为立方体所有的顶点计算出法线,但是由于3D立方体不是一个复杂的形状,所以我们可以简单的把法线数据手工添加到顶点数据中。更新的顶点数据数组可以在[这里](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting_vertex_data)找到。试着去想象一下,这些法向量真的是垂直于立方体的各个面的表面的(一个立方体由6个面组成)。
|
||||
法向量(Normal Vector)是垂直于顶点表面的(单位)向量。由于顶点自身并没有表面(它只是空间中一个独立的点),我们利用顶点周围的顶点计算出这个顶点的表面。我们能够使用叉乘这个技巧为立方体所有的顶点计算出法线,但是由于3D立方体不是一个复杂的形状,所以我们可以简单的把法线数据手工添加到顶点数据中。更新的顶点数据数组可以在[这里](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting_vertex_data)找到。试着去想象一下,这些法向量真的是垂直于立方体的各个面的表面的(一个立方体由6个面组成)。
|
||||
|
||||
因为我们向顶点数组添加了额外的数据,所以我们应该更新光照的顶点着色器:
|
||||
|
||||
@@ -102,7 +102,7 @@ void main()
|
||||
in vec3 Normal;
|
||||
```
|
||||
|
||||
### 计算漫反射光照
|
||||
## 计算漫反射光照
|
||||
|
||||
每个顶点现在都有了法向量,但是我们仍然需要光的位置向量和片段的位置向量。由于光的位置是一个静态变量,我们可以简单的在片段着色器中把它声明为uniform:
|
||||
|
||||
@@ -174,7 +174,7 @@ color = vec4(result, 1.0f);
|
||||
|
||||
如果你遇到很多困难,可以对比[完整的源代码](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting_diffuse)以及[片段着色器](http://learnopengl.com/code_viewer.php?code=lighting/basic_lighting_diffuse&type=fragment)代码。
|
||||
|
||||
### 最后一件事
|
||||
## 最后一件事
|
||||
|
||||
现在我们已经把法向量从顶点着色器传到了片段着色器。可是,目前片段着色器里,我们都是在世界空间坐标中进行计算的,所以,我们不是应该把法向量转换为世界空间坐标吗?基本正确,但是这不是简单地把它乘以一个模型矩阵就能搞定的。
|
||||
|
||||
@@ -202,11 +202,11 @@ Normal = mat3(transpose(inverse(model))) * normal;
|
||||
|
||||
对于着色器来说,逆矩阵也是一种开销比较大的操作,因此,无论何时,在着色器中只要可能就应该尽量避免逆操作,因为它们必须为你场景中的每个顶点进行这样的处理。以学习的目的这样做很好,但是对于一个对于效率有要求的应用来说,在绘制之前,你最好用CPU计算出正规矩阵,然后通过uniform把值传递给着色器(和模型矩阵一样)。
|
||||
|
||||
## 镜面光照(Specular Lighting)
|
||||
# 镜面光照
|
||||
|
||||
如果你还没被这些光照计算搞得精疲力尽,我们就再把镜面高光(Specular Highlight)加进来,这样冯氏光照才算完整。
|
||||
|
||||
和环境光照一样,镜面光照同样依据光的方向向量和物体的法向量,但是这次它也会依据观察方向,例如玩家是从什么方向看着这个片段的。镜面光照根据光的反射特性。如果我们想象物体表面像一面镜子一样,那么,无论我们从哪里去看那个表面所反射的光,镜面光照都会达到最大化。你可以从下面的图片看到效果:
|
||||
和环境光照一样,镜面光照(Specular Lighting)同样依据光的方向向量和物体的法向量,但是这次它也会依据观察方向,例如玩家是从什么方向看着这个片段的。镜面光照根据光的反射特性。如果我们想象物体表面像一面镜子一样,那么,无论我们从哪里去看那个表面所反射的光,镜面光照都会达到最大化。你可以从下面的图片看到效果:
|
||||
|
||||

|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# 材质(Material)
|
||||
# 材质
|
||||
|
||||
原文 | [Materials](http://learnopengl.com/#!Lighting/Materials)
|
||||
---|---
|
||||
@@ -37,7 +37,7 @@ uniform Material material;
|
||||
让我们试试在着色器中实现这样的一个材质系统。
|
||||
|
||||
|
||||
## 设置材质
|
||||
# 设置材质
|
||||
|
||||
我们在片段着色器中创建了一个uniform材质结构体,所以下面我们希望改变光照计算来顺应新的材质属性。由于所有材质元素都储存在结构体中,我们可以从uniform变量`material`取得它们:
|
||||
|
||||
@@ -89,7 +89,7 @@ glUniform1f(matShineLoc, 32.0f);
|
||||
看起来很奇怪不是吗?
|
||||
|
||||
|
||||
### 光的属性
|
||||
## 光的属性
|
||||
|
||||
这个物体太亮了。物体过亮的原因是环境、漫反射和镜面三个颜色任何一个光源都会去全力反射。光源对环境、漫反射和镜面元素同时具有不同的强度。前面的教程,我们通过使用一个强度值改变环境和镜面强度的方式解决了这个问题。我们想做一个相同的系统,但是这次为每个光照元素指定了强度向量。如果我们想象`lightColor`是`vec3(1.0)`,代码看起来像是这样:
|
||||
|
||||
@@ -147,7 +147,7 @@ glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
|
||||
现在改变物体的外观相对简单了些。我们做点更有趣的事!
|
||||
|
||||
|
||||
### 不同的光源颜色
|
||||
## 不同的光源颜色
|
||||
|
||||
目前为止,我们使用光源的颜色仅仅去改变物体各个元素的强度(通过选用从白到灰到黑范围内的颜色),并没有影响物体的真实颜色(只是强度)。由于现在能够非常容易地访问光的属性了,我们可以随着时间改变它们的颜色来获得一些有很意思的效果。由于所有东西都已经在片段着色器做好了,改变光的颜色很简单,我们可以立即创建出一些有趣的效果:
|
||||
|
||||
|
@@ -12,7 +12,7 @@
|
||||
|
||||
所以,前面的材质系统对于除了最简单的模型以外都是不够的,所以我们需要扩展前面的系统,我们要介绍diffuse和specular贴图。它们允许你对一个物体的diffuse(而对于简洁的ambient成分来说,它们几乎总是是一样的)和specular成分能够有更精确的影响。
|
||||
|
||||
## 漫反射贴图
|
||||
# 漫反射贴图
|
||||
|
||||
我们希望通过某种方式对每个原始像素独立设置diffuse颜色。有可以让我们基于物体原始像素的位置来获取颜色值的系统吗?
|
||||
|
||||
@@ -90,11 +90,11 @@ glBindTexture(GL_TEXTURE_2D, diffuseMap);
|
||||
你可以在这里得到应用的[全部代码](http://learnopengl.com/code_viewer.php?code=lighting/lighting_maps_diffuse)。
|
||||
|
||||
|
||||
## 镜面贴图
|
||||
# 镜面贴图
|
||||
|
||||
你可能注意到,specular高光看起来不怎么样,由于我们的物体是个箱子,大部分是木头,我们知道木头是不应该有镜面高光的。我们通过把物体设置specular材质设置为vec3(0.0f)来修正它。但是这样意味着铁边会不再显示镜面高光,我们知道钢铁是会显示一些镜面高光的。我们会想要控制物体部分地显示镜面高光,它带有修改了的亮度。这个问题看起来和diffuse贴图的讨论一样。这是巧合吗?我想不是。
|
||||
|
||||
我们同样用一个纹理贴图,来获得镜面高光。这意味着我们需要生成一个黑白(或者你喜欢的颜色)纹理来定义specular亮度,把它应用到物体的每个部分。下面是一个[specular贴图](http://learnopengl.com/img/textures/container2_specular.png)的例子:
|
||||
我们同样用一个纹理贴图,来获得镜面高光。这意味着我们需要生成一个黑白(或者你喜欢的颜色)纹理来定义specular亮度,把它应用到物体的每个部分。下面是一个[镜面贴图(Specular Map)](http://learnopengl.com/img/textures/container2_specular.png)的例子:
|
||||
|
||||

|
||||
|
||||
@@ -107,7 +107,7 @@ glBindTexture(GL_TEXTURE_2D, diffuseMap);
|
||||
使用Photoshop或Gimp之类的工具,通过将图片进行裁剪,将某部分调整成黑白图样,并调整亮度/对比度的做法,可以非常容易将一个diffuse纹理贴图处理为specular贴图。
|
||||
|
||||
|
||||
### 镜面贴图采样
|
||||
## 镜面贴图采样
|
||||
|
||||
一个specular贴图和其他纹理一样,所以代码和diffuse贴图的代码也相似。确保合理的加载了图片,生成一个纹理对象。由于我们在同样的片段着色器中使用另一个纹理采样器,我们必须为specular贴图使用一个不同的纹理单元(参见[纹理](http://learnopengl-cn.readthedocs.org/zh/latest/01%20Getting%20started/06%20Textures/)),所以在渲染前让我们把它绑定到合适的纹理单元
|
||||
|
||||
|
@@ -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),在场景里发亮。点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。
|
||||
|
||||

|
||||
|
||||
@@ -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中的聚光用世界空间位置,一个方向和一个指定了聚光半径的切光角来表示。我们计算的每个片段,如果片段在聚光的切光方向之间(就是在圆锥体内),我们就会把片段照亮。下面的图可以让你明白聚光是如何工作的:
|
||||
|
||||

|
||||
|
||||
* `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的余弦值,你可以在这里看到:
|
||||
|
||||

|
||||
|
||||
现在你可以看到,余弦越是接近1.0,角度就越小。这就解释了为什么θ需要比切光值更大了。切光值当前被设置为12.5的余弦,它等于0.9978,所以θ的余弦值在0.9979和1.0之间,片段会在聚光灯内,被照亮。
|
||||
现在你可以看到,余弦越是接近1.0,角度就越小。这就解释了为什么θ需要比切光值更大了。切光值当前被设置为12.5的余弦,它等于0.9978,所以θ的余弦值在0.9979和1.0之间,片段会在聚光内,被照亮。
|
||||
|
||||
运行应用,在聚光灯内的片段才会被照亮。这看起来像这样:
|
||||
运行应用,在聚光内的片段才会被照亮。这看起来像这样:
|
||||
|
||||

|
||||
|
||||
你可以在这里获得[全部源码](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://learnopengl-cn.readthedocs.org/zh/latest/02%20Lighting/06%20Multiple%20lights/),我们会结合所有我们目前讨论了的光和技巧。
|
||||
这样的一个手电筒/聚光类型的灯光非常适合恐怖游戏,结合定向和点光,环境会真的开始被照亮了。[下一个教程](http://learnopengl-cn.readthedocs.org/zh/latest/02%20Lighting/06%20Multiple%20lights/),我们会结合所有我们目前讨论了的光和技巧。
|
||||
|
||||
## 练习
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# 多光源(Multiple lights)
|
||||
# 多光源
|
||||
|
||||
原文 | [Multiple lights](http://learnopengl.com/#!Lighting/Multiple-lights)
|
||||
---|---
|
||||
@@ -35,9 +35,9 @@ void main()
|
||||
|
||||
即使对每一种光源的运算实现不同,但此算法的结构一般是与上述出入不大的。我们将定义几个用于计算各个光源的函数,并将这些函数的结算结果(返回颜色)添加到输出颜色向量中。例如,靠近被照射物体的光源计算结果将返回比远离背照射物体的光源更明亮的颜色。
|
||||
|
||||
## 平行光(Directional light)
|
||||
## 平行光
|
||||
|
||||
我们要在片段着色器中定义一个函数用来计算平行光在对应的照射点上的光照颜色,这个函数需要几个参数并返回一个计算平行光照结果的颜色。
|
||||
我们要在片段着色器中定义一个函数用来计算平行光(Directional light)在对应的照射点上的光照颜色,这个函数需要几个参数并返回一个计算平行光照结果的颜色。
|
||||
|
||||
首先我们需要设置一系列用于表示平行光的变量,正如上一节中所讲过的,我们可以将这些变量定义在一个叫做**DirLight**的结构体中,并定义一个这个结构体类型的uniform变量。
|
||||
|
||||
@@ -83,9 +83,9 @@ vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
|
||||
|
||||
我们从之前的教程中复制了代码,并用两个向量来作为函数参数来计算出平行光的光照颜色向量,该结果是一个由该平行光的环境反射、漫反射和镜面反射的各个分量组成的一个向量。
|
||||
|
||||
## 定点光(Point light)
|
||||
## 点光源
|
||||
|
||||
和计算平行光一样,我们同样需要定义一个函数用于计算定点光照。同样的,我们定义一个包含定点光源所需属性的结构体:
|
||||
和计算平行光一样,我们同样需要定义一个函数用于计算点光源(Point Light)。同样的,我们定义一个包含点光源所需属性的结构体:
|
||||
|
||||
```c++
|
||||
struct PointLight {
|
||||
@@ -103,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
|
||||
|
||||
@@ -144,7 +144,7 @@ vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
|
||||
|
||||
## 把它们放到一起
|
||||
|
||||
我们现在定义了用于计算平行光和定点光的函数,现在我们把这些代码放到一起,写入文开始的一般结构中:
|
||||
我们现在定义了用于计算平行光和点光源的函数,现在我们把这些代码放到一起,写入文开始的一般结构中:
|
||||
|
||||
```c++
|
||||
void main()
|
||||
|
@@ -18,8 +18,8 @@
|
||||
|
||||
在[下一个教程](http://learnopengl-cn.readthedocs.org/zh/latest/03%20Model%20Loading/01%20Assimp/)当中,我们将加入更高级的形状到我们的场景中,这些形状将会在我们之前讨论过的光照模型中非常好看。
|
||||
|
||||
词汇表
|
||||
--------
|
||||
## 词汇表
|
||||
|
||||
|
||||
- **颜色向量(Color Vector)**:一个通过红绿蓝(RGB)分量的组合描绘大部分真实颜色的向量. 一个对象的颜色实际上是该对象不能吸收的反射颜色分量。
|
||||
- **冯氏光照模型(Phong Lighting Model)**:一个通过计算环境,漫反射,和镜面反射分量的值来估计真实光照的模型。
|
||||
|
Reference in New Issue
Block a user