From 5ccc5e40599840dd68bdfcae869ea2a25b79d42c Mon Sep 17 00:00:00 2001 From: Meow J Date: Thu, 25 Jun 2015 23:17:19 +0800 Subject: [PATCH] Update README.md Update HDR progress img/ dir for images Replace html with picture formula 05 Light casters encoding -> UTF-8 --- 01 Getting started/05 Shaders.md | 2 +- 02 Lighting/05 Light casters.md | 247 ++++++++---------- 05 Advanced Lighting/06 HDR.md | 12 + README.md | 20 +- img/Light_casters1.png | Bin 0 -> 2280 bytes .../pic1.jpg => img/shaders1.jpg | Bin .../pic2.jpg => img/shaders2.jpg | Bin 7 files changed, 125 insertions(+), 156 deletions(-) create mode 100644 img/Light_casters1.png rename 01 Getting started/pic1.jpg => img/shaders1.jpg (100%) rename 01 Getting started/pic2.jpg => img/shaders2.jpg (100%) diff --git a/01 Getting started/05 Shaders.md b/01 Getting started/05 Shaders.md index 639ca3e..e14fa87 100644 --- a/01 Getting started/05 Shaders.md +++ b/01 Getting started/05 Shaders.md @@ -98,7 +98,7 @@ GLSL跟一般的编程语言一样,定义了一些常用的数据类型,基 可以看到在顶点着色器生命了一个向量:*vertexColor* 有out修饰,同时在片段着色器声明了一个*vertexColor* 使用in来修饰,这样片段着色器就可以获取顶点着色器处理的*vertexColor*的结果了。 根据上面shader,可以得出下图的效果: -![shader效果](https://github.com/codeman001/LearnOpenGL-CN/blob/master/01%20Getting%20started/pic1.jpg?raw=true) +![shader效果](https://raw.githubusercontent.com/LearnOpenGL-CN/LearnOpenGL-CN/master/img/shaders1.jpg) **常量** diff --git a/02 Lighting/05 Light casters.md b/02 Lighting/05 Light casters.md index f7faf5c..782d750 100644 --- a/02 Lighting/05 Light casters.md +++ b/02 Lighting/05 Light casters.md @@ -1,22 +1,22 @@ -JoeyDeVries[Django](http://bullteacher.com/16-light-casters.html)[http://learnopengl.com](http://www.learnopengl.com/#!Lighting/Light-casters) +本文作者JoeyDeVries,由[Django](http://bullteacher.com/16-light-casters.html)翻译自[http://learnopengl.com](http://www.learnopengl.com/#!Lighting/Light-casters) -ĿǰʹõйնһĹԴǿռеһ㡣Чʵ磬ж͵Ĺ⣬ÿֶͬһԴѹͶ䵽ϣͶ⡣̳ۼֲͬͶ͡ѧϰģⲻͬĹԴδḻijһߡ +我们目前使用的所有光照都来自于一个单独的光源,这是空间中的一个点。它的效果不错,但是在真实世界,我们有多种类型的光,它们每个表现都不同。一个光源把光投射到物体上,叫做投光。这个教程里我们讨论几种不同的投光类型。学习模拟不同的光源是你未来丰富你的场景的另一个工具。 -۶⣨directional lightΪ֮ǰѧ֪ʶչĵ⣨point light۾۹ƣspot lightĽ̳ǻ⼸ֲͬĹϵһС +我们首先讨论定向光(directional light),接着是作为之前学到知识的扩展的点光(point light),最后我们讨论聚光灯(spot light)。下面的教程我们会把这几种不同的光类型整合到一个场景中。 -##16.1 +##16.1 定向光 -һԴԶʱԹԴÿ߽ӽƽС⿴еĹͬһ͹۲ĶһԴΪԶʱΪ⣬ΪеĹ߶ͬһڹԴλá +当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光,因为所有的光线都有着同一个方向;它会独立于光源的位置。 -֪ĶԴһǣ̫̫DzԶ̫ԶˣڼյʱǸоԶͼƬ̫еĹ߶Ϊƽй⣺ +我们知道的定向光源的一个好例子是,太阳。太阳和我们不是无限远,但它太远了,在计算光照的时候,我们感觉它就像无限远。在下面的图片里,来自于太阳的所有的光线都被定义为平行光: ![](http://www.learnopengl.com/img/lighting/light_casters_directional.png) -ΪеĹ߶ƽеģڳеÿķ򶼱һ£͹ԴλñĹϵνڹķһ£ռͳеơ +因为所有的光线都是平行的,对于场景中的每个物体光的方向都保持一致,物体和光源的位置保持怎样的关系都无所谓。由于光的方向向量保持一致,光照计算会和场景中的其他物体相似。 -ǿͨһķģһ⣬ʹùλɫ㱣ִͬҪֱʹùķlightDirpositionļ㣺 +我们可以通过定义一个光的方向向量,来模拟这样一个定向光,而不是使用光的位置向量。着色器计算保持大致相同的要求,这次我们直接使用光的方向向量来代替用lightDir向量和position向量的计算: ```c++ struct Light { @@ -34,11 +34,11 @@ void main() ... } ``` -ע⣬Ⱥlight.directionĿǰʹõĹռҪķΪһƬγĹԴķ򣬵ͨϰ߶һΪһȫַӹԴDZȫֹķıķһָԴͬʱȷйһΪٶһλDzǵġ +注意,我们首先忽略light.direction向量。目前我们使用的光照计算需要光的方向作为一个来自片段朝向的光源的方向,但是人们通常更习惯定义一个定向光作为一个全局方向,它从光源发出。所以我们必须忽略全局光的方向向量来改变它的方向;它现在是一个方向向量指向光源。同时,确保对向量进行归一处理,因为假定输入的向量就是一个单位向量是不明智的。 -ΪlightDirʹdiffusespecular֮ǰ +作为结果的lightDir向量被使用在diffuse和specular计算之前。 -Ϊǿһ嶼ͬӰ죬ٴηϵ̳̽βֵӳȶ10ͬλãΪÿɲͬģ;ÿģ;Ӧıص任 +为了清晰地强调一个定向光对所有物体都有同样的影响,我们再次访问坐标系教程结尾部分的箱子场景。例子里我们先定义10个不同的箱子位置,为每个箱子生成不同的模型矩阵,每个模型矩阵包含相应的本地到世界变换: ```c++ for(GLuint i = 0; i < 10; i++) { @@ -50,109 +50,66 @@ for(GLuint i = 0; i < 10; i++) glDrawArrays(GL_TRIANGLES, 0, 36); } ``` -ͬʱҪǶԴķע⣬ǰѷΪӹԴķ棬Կٿķָ򣩣 +同时,不要忘记定义光源的方向(注意,我们把方向定义为:从光源处发出的方向;在下面,你可以快速看到光的方向的指向): ```c++ GLint lightDirPos = glGetUniformLocation(lightingShader.Program, "light.direction"); glUniform3f(lightDirPos, -0.2f, -1.0f, -0.3f); ``` -ѾѹλúͷΪvec3ЩȥϲеΪvec4.λΪvec4ʱ򣬰wԪΪ1.0dzҪƽƺͶӰŻıӦáȻһΪvec4ʱDzƽƷãΪdz˴ʲôҲǣǰwԪΪ0.0 +我们已经把光的位置和方向向量传递为vec3,但是有些人去想更喜欢把所有的向量设置为vec4.当定义位置向量为vec4的时候,把w元素设置为1.0非常重要,这样平移和投影才会合理的被应用。然而,当定义一个方向向量为vec4时,我们并不想让平移发挥作用(因为它们除了代表方向,其他什么也不是)所以我们把w元素设置为0.0。 -ʾΪvec4(0.2f, 1.0f, 0.3f, 0.0f)Ϊ򵥼͵ķԼwԪǷ1.0鿴ӵеĹλwǷ0.0һķԸǸ㷽 +方向向量被表示为:vec4(0.2f, 1.0f, 0.3f, 0.0f)。这可以作为简单检查光的类型的方法:你可以检查w元素是否等于1.0,查看我们现在所拥有的光的位置向量,w是否等于0.0,我们有一个光的方向向量,所以根据那个调整计算方法: ```c++ if(lightVector.w == 0.0) // Note: be careful for floating point errors // Do directional light calculations else if(lightVector.w == 1.0) - // Do light calculations using the lights position (like last tutorial) + // Do light calculations using the light’s position (like last tutorial) ``` -ȤʵǾOpenGL̶ʽһԴһ⻹λùԴ޸Ĺա -ڱӦãԾһ̫һĹԴѹ׵ϡԿdiffusespecularԪضˣһԴ𣿿 +有趣的事实:这就是旧OpenGL(固定函数式)决定一个光源是一个定向光还是位置光源,更具这个修改它的光照。 +如果你现在编译应用,飞跃场景,它看起来像有一个太阳一样的光源,把光抛到物体身上。你可以看到diffuse和specular元素都反射了,就像天空上有一个光源吗?看起来就像这样: ![](http://www.learnopengl.com/img/lighting/light_casters_directional_light.png) -Ӧõд룬Ƕɫ롣 +你可以在这里获得应用的所有代码,这里是顶点和像素着色器代码。 -##16.2 +##16.2 点光 -Ϊȫֹdzһ˶⣬ͨҲҪ⣬ڳ﷢һʱλõĹԴз򷢹⣬𽥱䰵ݺͻΪͶǿ԰ݵĽɫ +定向光作为全局光可以照亮整个场景,这非常棒,但是另一方面除了定向光,我们通常也需要几个点光,在场景里发亮。点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。 ![](http://www.learnopengl.com/img/lighting/light_casters_point.png) -֮ǰĽ̳Ѿʹˣ򵥵ģ⡣һλõĹԴλз򷢳ߡȻǶĹԴģߵĴӲ˥ʹԴȼǿڴ3DģУϲģһ֮ǸһΧȷԴ +之前的教程我们已经使用了(最简单的)点光。我们有一个有位置的光源,它从自身的位置向所有方向发出光线。然而,这个我们定义的光源所模拟光线的从不会衰减,这使得它看起来光源亮度极强。在大多数3D模拟中,我们喜欢模拟一个之恩那个照亮一个周围确定区域光源,它不会照亮整个场景。 -10ӵ֮ǰ̵̳ĹճУע⵽ڰеÿӶͬȣڹյǰ棻ûйʽľ˥úڰԴȽϽӱ΢ +如果你把10个箱子添加到之前教程的光照场景中,你会注意到黑暗中的每个箱子都会有同样的亮度,就像箱子在光照的前面;没有公式定义光的距离衰减。我们想让黑暗中与光源比较近的箱子被轻微地照亮。 -##16.3 ˥ +##16.3 衰减 -ŹߴԽԶľӦؼȣͨΪ˥һžȵķʽʹԵʽһžȵԷ̣ʹԶȻԷЧе١ʵ磬ͨڽʱdzһԴȣʼʱٵķdz죬֮žӣٵٶȻҪһֲͬķٹȡ +随着光线穿越更远的距离相应地减少亮度,通常被称为衰减。一种随着距离减少亮度的方式是使用线性等式。这样的一个随着距离减少亮度的线性方程,可以使远处的物体更暗。然而,这样的线性方程效果会有点假。在真实世界,通常光在近处时非常亮,但是一个光源的亮度,开始的时候减少的非常快,之后随着距离的增加,减少的速度会慢下来。我们需要一种不同的方程来减少光的亮度。 -˵һЩѾͰ뵽ˡķ̰һƬεĹȳһѾ˥ֵֵݹԴԶõ - - - - - (1) - - - - F - - a - t - t - - - = - - I - - - K - c - - + - - K - l - - - d - + - - K - q - - - - d - 2 - - - - - - - -IǵǰƬεĹȣdƬεԴľ롣Ϊ˼˥ֵǶ3KcһKlͶKq +幸运的是一些聪明人已经早就把它想到了。下面的方程把一个片段的光的亮度除以一个已经计算出来的衰减值,这个值根据光源的远近得到: -ͨ1.0DZ֤ĹԶ1СΪһľȣӰ쵽Ѱҵġ -һֵƣԵķʽȡ -ƽˣΪԴһȵĶεݼھȽϽʱһһСǵԶʱһ -ڶĹԷʽ٣ָ㹻ʱ򣬾ͻᳬһ֮󣬹Ȼٵĸ졣ЧǹڽʱdzǾԶѸٽͣȽٶٴαͼչʾ100ڵķΧ˥Ч +![Latex Formula](https://raw.githubusercontent.com/LearnOpenGL-CN/LearnOpenGL-CN/master/img/Light_casters1.jpg) + +在这里I是当前片段的光的亮度,d代表片段到光源的距离。为了计算衰减值,我们定义3个项:常数项Kc,一次项Kl和二次项Kq。 + +常数项通常是1.0,它的作用是保证坟墓永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。 +一次项用于与距离值相称,这回以线性的方式减少亮度。 +二次项用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大。 +由于二次项的光会以线性方式减少,指导距离足够大的时候,就会超过一次项,之后,光的亮度会减少的更快。最后的效果就是光在近距离时,非常量,但是距离变远亮度迅速降低,最后亮度降低速度再次变慢。下面的图展示了在100以内的范围,这样的衰减效果。 ![](http://www.learnopengl.com/img/lighting/attenuation.png) -ԿܽʱǿȣžԼԼӽ100ʱ򣬾ͻҪġ +你可以看到当距离很近的时候光有最强的亮度,但是随着距离增大,亮度明显减弱,大约接近100的时候,就会慢下来。这就是我们想要的。 -###16.3.1 ѡȷֵ +###16.3.1 选择正确的值 -ǣǰΪʲôֵأȷֵɺܶؾϣǵľ뷶Χ͵ȡϣǾ⣬ҲҪʶȵıչʾһЩֵģʵij͵ģԴضİ뾶룩һһľ룬ЩֵǴÿʼOgre3Dά +但是,我们把这三个项设置为什么值呢?正确的值的设置由很多因素决定:环境、你希望光所覆盖的距离范围、光的类型等。大多数场合,这是经验的问题,也要适度调整。下面的表格展示一些各项的值,它们模拟现实(某种类型的)光源,覆盖特定的半径(距离)。第一蓝定义一个光的距离,它覆盖所给定的项。这些值是大多数光的良好开始,它是来自Ogre3D的维基的礼物: @@ -235,13 +192,13 @@ else if(lightVector.w == 1.0)
-ģKcһֱ1.0һKlΪ˸ǸԶľͨСKq͸СˡЩֵʵ飬ʵиԵЧǵĻУ32100ľԴͨ㹻ˡ +就像你所看到的,常数项Kc一直都是1.0。一次项Kl为了覆盖更远的距离通常很小,二次项Kq就更小了。尝试用这些值进行实验,看看它们在你的实现中各自的效果。我们的环境中,32到100的距离对大多数光通常就足够了。 -###16.3.2 ʵ˥ +###16.3.2 实现衰减 -Ϊʵ˥ɫǻҪֵҲǹʽijһͶðǴ֮ǰLightṹСҪעǼlightDirǰĽ̳ģ֮ǰĶDz֡ +为了实现衰减,在着色器中我们会需要三个额外数值:也就是公式的常量、一次项和二次项。最好把它们储存在之前定义的Light结构体中。要注意的是我们计算lightDir,就是在前面的教程中我们所做的,不是像之前的定向光的那部分。 ```c++ struct Light { @@ -254,60 +211,60 @@ struct Light float quadratic; }; ``` -ȻOpenGLЩϣ⸲50ľ룬ǻʹıкʵijһͶ +然后,我们在OpenGL中设置这些项:我们希望光覆盖50的距离,所以我们会使用上面的表格中合适的常数项、一次项和二次项: ```c++ glUniform1f(glGetUniformLocation(lightingShader.Program, "light.constant"), 1.0f); glUniform1f(glGetUniformLocation(lightingShader.Program, "light.linear"), 0.09); glUniform1f(glGetUniformLocation(lightingShader.Program, "light.quadratic"), 0.032); ``` -ɫʵ˥ֱӣǸݹʽ򵥵ļ˥ֵڳambientdiffusespecularԪء +在像素着色器中实现衰减很直接:我们根据公式简单的计算衰减值,在乘以ambient、diffuse和specular元素。 -ҪԴľṩʽǵܹijǿͨȡƬκ͹Դ֮IJͬijȽΪǿʹGLSLڽlength£ +我们需要光源的距离提供给公式;记得我们能够计算向量的长度吗?我们可以通过获取片段和光源之间的不同向量把向量的长度结果作为距离项。我们可以使用GLSL的内建length函数做这件事: ```c++ float distance = length(light.position - Position); float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance)); ``` -ȻڹռУͨ˥ֵambientdiffusespecularɫ˥ֵ +然后,我们在光照计算中,通过把衰减值乘以ambient、diffuse和specular颜色,包含这个衰减值。 -ǿԿ԰ambientԪŲ䣬amientվͲž٣ʹö1ĹԴеambientԪػῪʼӣϣambientҲ˥򵥵ĵԳĻ˵õЧ +我们可以可以把ambient元素留着不变,这样amient光照就不会随着距离减少,但是如果我们使用多余1个的光源,所有的ambient元素会开始叠加,因此这种情况,我们希望ambient光照也衰减。简单的调试出对于你的环境来说最好的效果。 ```c++ ambient *= attenuation; diffuse *= attenuation; specular *= attenuation; ``` -ӦúЧ +如果你运行应用后获得这样的效果: light_casters_point_light -Կֻӵǰ汻յһ㶼ûΪǾԴ̫ԶˡҵӦԴƬεĴ롣 +你可以看到现在只有最近处的箱子的前面被照得最亮。后面的箱子一点都没被照亮,因为它们距离光源太远了。你可以在这里找到应用源码和片段的代码。 -һλú˥ֵӦõռСһ͹⵱С +电光就是一个可配的置位置和衰减值应用到光照计算中。还有另一种类型光可用于我们照明库当中。 -##16.4 ۹ +##16.4 聚光灯 -Ҫ۵һ͹Ǿ۹ơ۹һλڻijĹԴз䣬ֻij䡣ֻһ۹䷽ȷ뾶ڵŻᱻĶֺڰ۹Ƶĺ·ƻֵͲ +我们要讨论的最后一种类型光是聚光灯。聚光灯是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光灯照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光灯的好例子是路灯或手电筒。 -OpenGLеľ۹ռλãһһָ˾۹ư뾶йʾǼÿƬΣƬھ۹Ƶй֮ⷽ䣨Բ׶ڣǾͻƬͼ׾۹ιģ +OpenGL中的聚光灯用世界空间位置,一个方向和一个指定了聚光灯半径的切光角来表示。我们计算的每个片段,如果片段在聚光灯的切光方向之间(就是在圆锥体内),我们就会把片段照亮。下面的图可以让你明白聚光灯是如何工作的: ![](http://www.learnopengl.com/img/lighting/light_casters_spotlight_angles.png) -* LightDirƬָԴ -* SpotDir۹ָķ -* Phiգ۹ư뾶йǡÿǶ֮ģ۹ƶ -* ThetaȣLightDirSpotDir֮ĽǶȡֵӦñȦֵСŻھ۹ڡ +* LightDir:从片段指向光源的向量。 +* SpotDir:聚光灯所指向的方向。 +* Phiφ:定义聚光灯半径的切光角。每个落在这个角度之外的,聚光灯都不会照亮。 +* Thetaθ:LightDir向量和SpotDir向量之间的角度。θ值应该比φ值小,这样才会在聚光灯内。 -ǴҪǣLightDirSpotDirĵˣλĵˣǵ𣿣ȻںڹǦնԱȡӦ׾۹潫ֵͲķ +所以我们大致要做的是,计算LightDir向量和SpotDir向量的点乘(返回两个单位向量的点乘,还记得吗?),然后在和遮光角φ对比。现在你应该明白聚光灯是我们下面将创建的手电筒的范例。 -##16.5 ֵͲ +##16.5 手电筒 -ֵͲһڹ۲λõľ۹ƣͨ׼͸ͼǰ档˵һֵͲһͨľ۹ƣǸҵλúͷĸλúͷ +手电筒是一个坐落在观察者位置的聚光灯,通常瞄准玩家透视图的前面。基本上说,一个手电筒是一个普通的聚光灯,但是根据玩家的位置和方向持续的更新它的位置和方向。 -ҪΪɫṩֵǾ۹Ƶλķ꣩۹Ƶķڹǡǿ԰ЩֵLightṹУ +所以我们需要为像素着色器提供的值,是聚光灯的位置向量(来计算光的方向坐标),聚光灯的方向向量和遮光角。我们可以把这些值储存在Light结构体中: ```c++ struct Light { @@ -317,62 +274,62 @@ struct Light ... }; ``` -ǰЩʵֵɫ +下面我们把这些适当的值传给着色器: ```c++ glUniform3f(lightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z); glUniform3f(lightSpotdirLoc, camera.Front.x, camera.Front.y, camera.Front.z); glUniform1f(lightSpotCutOffLoc, glm::cos(glm::radians(12.5f))); ``` -ԿΪڹһǶȣǸһǶȼֵҽɫôԭɫУǼLightDirSpotDirĵˣ˷һֵһǶȣDzֱӰһǶȺֵԱȡΪ˻ǶȣDZ˽ķңǺܴġΪ˽ԼһЩܣȼйǵֵȻѽݸɫÿǶȶʾΪˣǿֱӶԱǣýκο߰IJ +你可以看到,我们为遮光角设置一个角度,但是我们根据一个角度计算了余弦值,把这个余弦结果传给了像素着色器。这么做的原因是在像素着色器中,我们计算LightDir和SpotDir向量的点乘,而点乘返回一个余弦值,不是一个角度,所以我们不能直接把一个角度和余弦值对比。为了获得这个角度,我们必须计算点乘结果的反余弦,这个操作开销是很大的。所以为了节约一些性能,我们先计算给定切光角的余弦值,然后把结果传递给像素着色器。由于每个角度都被表示为余弦了,我们可以直接对比它们,而不用进行任何开销高昂的操作。 -ʣҪǼֵֵͦԱȣԾǷڻھ۹Ƶڲ +现在剩下要做的是计算θ值,用它和φ值对比,以决定我们是否在或不在聚光灯的内部: ```c++ float theta = dot(lightDir, normalize(-light.direction)); if(theta > light.cutOff) { // Do lighting calculations } -else // else, use ambient light so scene isnt completely dark outside the spotlight. +else // else, use ambient light so scene isn’t completely dark outside the spotlight. color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); ``` -ȼlightDir͸ĵˣΪҪָԴǴӹԴΪָ㡣עǰspecular̳ȴ෴ıʾ߿ѡϲı﷽ʽȷ˹һ +我们首先计算lightDir和负方向向量的点乘(它负的是因为我们想要向量指向光源,而不是从光源作为指向出发点。译注:前面的specular教程中作者却用了相反的表示方法,这里读者可以选择喜欢的表达方式)。确保对所有相关向量进行了归一处理。 -Ϊʲôifʹ>Ŷ<šΪھ۹ڣȲӦñȹڹֵСûDzҪˣǶֵֵʾģһ0ȵĽDZʾΪ1.0ֵһ90ȵʱ򱻱ʾΪ0.0ֵ￴ +你可能奇怪为什么if条件中使用>符号而不是<符号。为了在聚光灯以内,θ不是应该比光的遮光值更小吗?这没错,但是不要忘了,角度值是以余弦值来表示的,一个0度的角表示为1.0的余弦值,当一个角是90度的时候被表示为0.0的余弦值,你可以在这里看到: ![](http://www.learnopengl.com/img/lighting/light_casters_cos.png) -ԿԽǽӽ1.0ǶȾԽСͽΪʲôҪйֵˡйֵǰΪ12.5ң0.9978Ԧȵֵ0.99791.0֮䣬Ƭλھ۹ڣ -Ӧãھ۹ڵƬβŻᱻ⿴ +现在你可以看到,余弦越是接近1.0,角度就越小。这就解释了为什么θ需要比切光值更大了。切光值当前被设置为12.5的余弦,它等于0.9978,所以θ的余弦值在0.9979和1.0之间,片段会在聚光灯内,被照亮。 +运行应用,在聚光灯内的片段才会被照亮。这看起来像这样: `[](http://www.learnopengl.com/img/lighting/light_casters_spotlight_hard.png) -ȫԴɫԴ롣 +你可以在这里获得全部源码和像素着色器的源码。 -Ȼе٣󲿷ԭǾ۹һӲߡɫһ˾۹ƵԲ׶Ե̺ȴûκƽĹȡһʵľ۹ƵĹı߽紦ƽġ +它看起来仍然有点假,大部分原因是聚光灯有了一个硬边。像素着色器一旦到达了聚光灯的圆锥边缘,它就立刻黑了下来,却没有任何平滑减弱的过度。一个真实的聚光灯的光会在它的边界处平滑减弱的。 -##16.6 ƽ/Ե +##16.6 平滑/软化边缘 -Ϊ۹Ƶƽߣϣȥģľ۹һԲ׶Բ׶ǿ԰Բ׶Ϊǰ沿ֶԲ׶ϣԲ׶ڱߵ𲽵ı䰵 +为创建聚光灯的平滑边,我们希望去模拟的聚光灯有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。 -ΪԲ׶Ǽ򵥶һֵ۹ƵķԲ׶İ뾶ĽǶȡȻƬԲ׶Բ׶֮䣬ͻһ0.01.0֮ȡƬԲ׶Ⱦ͵1.00.0 +为创建外圆锥,我们简单定义另一个余弦值,它代表聚光灯的方向向量和外圆锥的向量(等于它的半径)的角度。然后,如果片段在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0,如果在外面就是0.0。 -ǿʹĹʽֵ +我们可以使用下面的公式计算这样的值: [math] -ڲգⲿԲ׶[math]IJIֵǾ۹ڵǰƬεȡ +这里ε是内部(φ)和外部圆锥[math]的差。结果I的值是聚光灯在当前片段的亮度。 -ͼʽģdzʹһӣ +很难用图画描述出这个公式是怎样工作的,所以我们尝试使用一个例子: - - - - - - - + + + + + + + @@ -382,8 +339,8 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); - - + + @@ -392,8 +349,8 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); - - + + @@ -402,8 +359,8 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); - - + + @@ -412,8 +369,8 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); - - + + @@ -422,8 +379,8 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); - - + + @@ -432,8 +389,8 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); - - + + @@ -442,30 +399,30 @@ color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f); - - + +
in degrees(inner cutoff)in degrees(outer cutoff)in degreesθθin degreesφ(inner cutoff)φin degreesγ(outer cutoff)γin degreesε I
25 0.82 350.91 C 0.82 = 0.090.87 C 0.82 / 0.09 = 0.560.91 – 0.82 = 0.090.87 – 0.82 / 0.09 = 0.56
0.925 0.82 350.91 C 0.82 = 0.090.9 C 0.82 / 0.09 = 0.890.91 – 0.82 = 0.090.9 – 0.82 / 0.09 = 0.89
0.9725 0.82 350.91 C 0.82 = 0.090.97 C 0.82 / 0.09 = 1.670.91 – 0.82 = 0.090.97 – 0.82 / 0.09 = 1.67
0.9725 0.82 350.91 C 0.82 = 0.090.97 C 0.82 / 0.09 = 1.670.91 – 0.82 = 0.090.97 – 0.82 / 0.09 = 1.67
0.8325 0.82 350.91 C 0.82 = 0.090.83 C 0.82 / 0.09 = 0.110.91 – 0.82 = 0.090.83 – 0.82 / 0.09 = 0.11
0.6425 0.82 350.91 C 0.82 = 0.090.64 C 0.82 / 0.09 = -2.00.91 – 0.82 = 0.090.64 – 0.82 / 0.09 = -2.0
0.96612.5 0.953 17.50.966 C 0.953 = 0.04480.966 C 0.953 / 0.0448 = 0.290.966 – 0.953 = 0.04480.966 – 0.953 / 0.0448 = 0.29
-㿴ǻǸݦҺֵ֮ȻôҪġԼ򵥵ʹʽ㣬ϵ׵ʱ +就像你看到的那样我们基本是根据θ在外余弦和内余弦之间插值。如果你仍然不明白怎么继续,不要担心。你可以简单的使用这个公式计算,当你更加老道和明白的时候再来看。 -һֵھ۹ʱǸģڲԲ׶ڴ1ʵذ̶ֵɫоٲҪif-elseˣǿԼ򵥵üֵԹԪأ +由于我们现在有了一个亮度值,当在聚光灯外的时候是个负的,当在内部圆锥以内大于1。如果我们适当地把这个值固定,我们在像素着色器中就再不需要if-else了,我们可以简单地用计算出的亮度值乘以光的元素: ```c++ float theta = dot(lightDir, normalize(-light.direction)); float epsilon = light.cutOff - light.outerCutOff; float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0); ... -// Well leave ambient unaffected so we always have a little +// We’ll leave ambient unaffected so we always have a little light.diffuse* = intensity; specular* = intensity; ... ``` -ע⣬ʹclampѵһ̶0.01.0֮䡣Ᵽֵ֤ᳬ[0, 1]⡣ +注意,我们使用了clamp函数,它把第一个参数固定在0.0和1.0之间。这保证了亮度值不会超出[0, 1]以外。 -ȷouterCutOffֵӵLightṹ壬ӦuniformֵͼƬڲڹ12.5fⲿڹ17.5f +确定你把outerCutOff值添加到了Light结构体,并在应用中设置了它的uniform值。对于下面的图片,内部遮光角12.5f,外部遮光角是17.5f: ![](http://www.learnopengl.com/img/lighting/light_casters_spotlight.png) -öˡϸڲⲿڹǣԴһľ۹ơҵӦԴ룬ԼƬεԴ롣 +看起来好多了。仔细看看内部和外部遮光角,尝试创建一个符合你求的聚光灯。可以在这里找到应用源码,以及片段的源代码。 -һֵͲ/۹͵ĵƹdzʺϿֲϷ϶͵⣬Ŀʼˡһ̳̣ǻĿǰ˵Ĺͼɡ \ No newline at end of file +这样的一个手电筒/聚光灯类型的灯光非常适合恐怖游戏,结合定向和点光,环境会真的开始被照亮了。下一个教程,我们会结合所有我们目前讨论了的光和技巧。 \ No newline at end of file diff --git a/05 Advanced Lighting/06 HDR.md b/05 Advanced Lighting/06 HDR.md index 33091a4..163a2d6 100644 --- a/05 Advanced Lighting/06 HDR.md +++ b/05 Advanced Lighting/06 HDR.md @@ -2,4 +2,16 @@ ##HDR +当存在帧缓冲区(Framebuffer)时,亮度和颜色的值是默认被限制在0.0到1.0之间的. 这个看起来无辜的语句使我们一直将亮度与颜色的值设置在这个范围内,尝试着与场景契合. 这样是能够运行的,也能给出还不错的效果. 但是如果我们遇上了一个特定的区域,其中有多个亮光源使这些数值总和超过了1.0,又会发生什么呢? 答案是这些片段中超过1.0的亮度或者颜色值会被限制在1.0, 从而导致场景混成一片,难以分辨: + +![](http://learnopengl.com/img/advanced-lighting/hdr_clamped.png) + +这是由于大量片段的颜色值都非常接近1.0,在很大一个区域内每一个亮的片段都有相同的白色. 这损失了很多的细节,使场景看起来非常假. + +解决这个问题的一个方案是减小光源的强度从而保证场景内没有一个片段亮于1.0. 然而这并不是一个好的方案,因为你需要使用不切实际的光照参数. 一个更好的方案是让颜色暂时超过1.0,然后将其转换至0.0到1.0的区间内,从而防止损失细节. + +显示器被限制为只能显示值为0.0到1.0间的颜色,但是在光照方程中却没有这个限制. 通过使片段的颜色超过1.0,我们有了一个更大的颜色范围,这也被称作HDR.(High Dynamic Range, 高动态范围) 有了HDR,亮的东西可以变得非常亮,暗的东西可以变得非常暗,而且充满细节. + +--- + #WIP \ No newline at end of file diff --git a/README.md b/README.md index 76c453b..7290c80 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # LearnOpenGL-CN learnopengl.com系列教程的中文翻译,目前正在翻译中。 -###当前翻译进度:54% (31/57) +**当前翻译进度:** +![Progress](http://progressed.io/bar/54?title=31/57) -![Progress](http://progressed.io/bar/54) -###翻译组成员请注意:Advanced Lighting之前的教程已经翻译完成,请认领Advanced Lighting及其以后的教程。 +**翻译组成员请注意:Advanced Lighting之前的教程已经翻译完成,请认领Advanced Lighting及其以后的教程。** ---- -####如何认领翻译? +##如何认领翻译? -由于我们的志愿者来自五湖四海,为了避免冲突。请志愿者们先clone这个Repository 。同步到本地后找到要翻译的文章,创建一个如下所示的只包含作者、翻译者和原文链接信息的MarkDown文件: +由于我们的志愿者来自五湖四海,为了避免冲突。请志愿者们先clone这个Repository 。同步到本地后找到要翻译的文章,创建一个如下所示的只包含作者、翻译者和原文链接信息的Markdown文件: 本文作者JoeyDeVries,由Geequlim翻译自http://learnopengl.com @@ -20,15 +19,16 @@ learnopengl.com系列教程的中文翻译,目前正在翻译中。 例如: - 05 Advanced Lighting/03 Shadows/02 Point Shadows.md + 05 Advanced Lighting/03 Shadows/02 Point Shadows.md 加入LearnOpenGL-CN组织,然后提交并push您的文章。 ###欢迎志愿者们加入翻译组交流QQ群:383745868 ---- -专业词汇对照表 ---- + + +##专业词汇对照表 + 1. [《游戏引擎架构》中英词汇索引表](http://www.cnblogs.com/miloyip/p/GameEngineArchitectureIndex.html) diff --git a/img/Light_casters1.png b/img/Light_casters1.png new file mode 100644 index 0000000000000000000000000000000000000000..c17df013a593cab05acf687ee5baaf24942749f5 GIT binary patch literal 2280 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 RQF~tz0Bz%ps