光照篇 终于是来到了光照篇,这一章会比上一章更加复杂一些,代码量也会增加很多,屏住呼吸,我们一起踏上旅程吧~~~!
首先我们要对之前的代码做一些修改,之前加载纹理的代码写得太丑了,我们这里把它写成一个加载、生成纹理的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 unsigned int LoadImageToGPU (const char * filename,GLint internalFormat,GLenum format,int textureSlot) { unsigned int TexBuffer; glGenTextures(1 , &TexBuffer); glActiveTexture(GL_TEXTURE0 + textureSlot); glBindTexture(GL_TEXTURE_2D, TexBuffer); int width, height, nrChannel; unsigned char * data = stbi_load(filename, &width, &height, &nrChannel, 0 ); if (data) { glTexImage2D(GL_TEXTURE_2D, 0 , internalFormat, width, height, 0 , format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); } else { std ::cout << "Load Image Failed" << std ::endl ; } stbi_image_free(data); return TexBuffer; } unsigned int TexbufferA;TexbufferA = LoadImageToGPU("container.jpg" , GL_RGB, GL_RGB, 0 ); unsigned int TexBufferB;TexBufferB = LoadImageToGPU("awesomeface.png" , GL_RGBA, GL_RGBA, 1 ); myShader->use(); glUniform1i(glGetUniformLocation(myShader->ID, "texture1" ), 0 ); glUniform1i(glGetUniformLocation(myShader->ID, "texture2" ), 1 );
颜色 我们可以直接给光源一个三维向量,这样就可以模拟出这个光源的颜色,实际上这三个数值就对应了它的RBG值。颜色和颜色的叠加直接用向量乘法实现就行。
构建光照环境 顶点着色器中我们不需要什么变化,我们只需要在片段着色器中做修改就可以了,定义两个光照的uniform:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #version 330 core in vec4 vertexColor; in vec2 TexCoord;out vec4 FragColor; uniform sampler2D texture1;uniform sampler2D texture2;uniform vec3 objColor;uniform vec3 ambientColor;void main() { FragColor = vec4 (objColor * ambientColor, 1.0 ) * mix (texture (texture1, TexCoord), texture (texture2, TexCoord), 0.2 ); }
然后配置uniform:
1 2 glUniform3f(glGetUniformLocation(myShader->ID, "objColor" ), 1.0f , 0.5f , 0.3f ); glUniform3f(glGetUniformLocation(myShader->ID, "ambientColor" ), 1.0f , 1.0f , 1.0f );
这样就可以得到加了颜色的笑脸方块:
Phong光照模型 现实世界的光照太过复杂,很难直接去计算,在图形学中大部分都是用经验模型去模拟现实的光照,因为只要“看起来是对的,那就是对的”。现在我们就用OpenGL实现一个基础的Phong光照。冯氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照,将这三个叠加,就得到了我们想模拟的现实中的光照。
漫反射光照 计算漫反射光照最重要的就是角度,光的入射角度与法向量的夹角就可以反映出在这个点的光的强度,也就是这束光对这个片段的颜色影响大小。我们要uniform一个光源的位置,然后用物体颜色点去减去光源点,就可以的到这个点的入射光线,然后再用入射光线乘法相向量,就得到了我们想要的diffuse。
先来看看着色器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #version 330 core layout (location = 0 ) in vec3 aPos; layout (location = 3 ) in vec3 aNormal;uniform mat4 modelMat;uniform mat4 viewMat;uniform mat4 projMat;out vec3 FragPos;out vec3 Normal;void main() { gl_Position = projMat * viewMat * modelMat * vec4 (aPos, 1.0 ); FragPos = vec3 (modelMat * vec4 (aPos, 1.0 f)); Normal = mat3 (modelMat) * aNormal; } #version 330 core in vec3 FragPos;in vec3 Normal;out vec4 FragColor; uniform vec3 objColor;uniform vec3 ambientColor;uniform vec3 lightPos;uniform vec3 lightColor;void main() { vec3 lightDir = normalize (lightPos - FragPos); vec3 diffuse = max (dot (lightDir , Normal),0 ) * lightColor ; FragColor = vec4 ((diffuse + ambientColor) * objColor, 1.0 ); } glVertexAttribPointer(0 , 3 , GL_FLOAT, GL_FALSE, 6 * sizeof(float ), (void *)0 ); glEnableVertexAttribArray(0 ); glVertexAttribPointer(3 , 3 , GL_FLOAT, GL_FALSE, 6 * sizeof(float ), (void *)(3 * sizeof(float ))); glEnableVertexAttribArray(3 ); glUniform3f(glGetUniformLocation(myShader->ID, "objColor"), 1.0f, 0.5f, 0.3f);//设置物体颜色 glUniform3f(glGetUniformLocation(myShader->ID, "ambientColor"), 1.0f, 1.0f, 1.0f);//设置环境光 glUniform3f(glGetUniformLocation(myShader->ID, "lightPos"), 10.0f, 10.0f, 5.0f);//设置灯的位置 glUniform3f(glGetUniformLocation(myShader->ID, "lightColor"), 1.0f, 1.0f, 1.0f);//设置光的颜色
然后就可以看到我们单独做出的白光打在立方体上的漫反射光照:
总结一下漫反射光照的计算:dot(lightDir , Normal)* lightColor
镜面反射 镜面反射需要考虑我们的眼睛的所在位置,也就是视线方向,我们通过入射光向量和入射点法向量来计算反射向量,这个用OpenGL的reflect API就可以做到。然后我们计算反射光向量和视线方向的角度差,这个直接用点乘去实现,如果夹角越小,那么镜面光的影响就会越大(我们的眼睛看到的反射光就更多,就感觉更亮)。它的作用效果就是,当我们去看光被物体所反射的那个方向的时候,我们会看到一个高光,这和真实物理世界里的现象是一样的。
我们同样需要uniform一个camera的位置,然后由这个去得到我们的视角方向,顶点着色器没有任何改变,片段着色器如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #version 330 core in vec3 FragPos;in vec3 Normal;out vec4 FragColor; uniform vec3 objColor;uniform vec3 ambientColor;uniform vec3 lightPos;uniform vec3 lightColor;uniform vec3 cameraPos;void main() { vec3 lightDir = normalize (lightPos - FragPos); vec3 reflectDir = reflect (-lightDir , Normal); vec3 viewDir = normalize (cameraPos - FragPos); vec3 specular = pow (max (dot (reflectDir , viewDir),0 ),32 ) * lightColor; vec3 diffuse = max (dot (lightDir , Normal),0 ) * lightColor ; FragColor = vec4 ((diffuse + ambientColor + specular) * objColor, 1.0 ); }
这样我们就完全实现了Phong光照模型,在某一个特定的角度你可以看到镜面高光:
是不是很神奇?模拟出了你在现实世界中的情况,某一个角度可看到镜面反射的高光,这仅仅只是两个向量的点积带来的,一个反射光线向量,一个视角方向。
总结一下高光反射的计算:(dot(reflectDir , viewDir) ^ specular Amount ) * lightColor。
Phong光照模型:(环境光+高光反射+漫反射光) * 物体颜色
材质 在现实中不同的材质对于光照的反射效果是不同的。这里我们就去做一个材质属性,去模拟不同的材质对光照的效果。
我们先做一个Material的材质结构体,我们让他能在最后对关照的效果做一些调整:
1 2 3 4 5 6 7 8 9 10 11 12 struct Material{ vec3 ambient; vec3 diffuse; vec3 specular; float shininess; }; uniform Material material;vec3 specular = material.specular * pow (max (dot (reflectDir , viewDir),0 ),32 ) * lightColor;vec3 diffuse = material.diffuse * max (dot (lightDir , Normal),0 ) * lightColor ;vec3 ambient = material.ambient * ambientColor;
然后在main函数里面给uniform赋值就可以了;
1 2 3 4 glUniform3f(glGetUniformLocation(myShader->ID, "material.ambient" ), 1.0f , 1.0f , 1.0f ); glUniform3f(glGetUniformLocation(myShader->ID, "material.diffuse" ), 1.0f , 1.0f , 1.0f ); glUniform3f(glGetUniformLocation(myShader->ID, "material.specular" ), 1.0f , 1.0f , 1.0f ); glUniform1f(glGetUniformLocation(myShader->ID, "material.shininess" ), 32.0f );
然后我们来做一个class,把这个属性封装成一个类。因为直接这样去赋值太麻烦,而且也不直观,我们直接做一个材质类,在构造函数里面就把这些属性的值给赋了,我们在创建一个材质时就可以直接对于这个材质赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Material { public : Shader* shader; glm::vec3 diffuse; glm::vec3 specular; glm::vec3 ambient; float shininess; Material(Shader* _shader, glm::vec3 _diffuse, glm::vec3 _specular, glm::vec3 _ambient, float _shininess); }; Material::Material(Shader* _shader, glm::vec3 _diffuse, glm::vec3 _specular, glm::vec3 _ambient, float _shininess) : shader(_shader), diffuse(_diffuse), specular(_specular), ambient(_ambient), shininess(_shininess) { }
然后我们在主函数中:
1 2 3 4 5 Material* material = new Material(myShader, glm::vec3(1.0f,1.0f,1.0f), glm::vec3(1.0f,1.0f,1.0f), glm::vec3(1.0f,1.0f,1.0f), 32.0f);
然后为了避免在uniform赋值的时候要把三个向量拆开,我们再去在shader.h里面做一个SetUniform的方法:
1 2 3 4 5 6 7 8 9 void Shader::SetUniform3f(const char* paramNameString, glm::vec3 param) { glUniform3f(glGetUniformLocation(ID, paramNameString), param.x, param.y, param.z); } void Shader::SetUniform1f(const char* paramNameString, float param) { glUniform1f(glGetUniformLocation(ID, paramNameString), param); }
这样就可以很容易在main函数里面直接把一个vec3和float赋给uniform:
1 2 3 4 myMaterial->shader->SetUniform3f("material.ambient" , myMaterial->ambient); myMaterial->shader->SetUniform3f("material.diffuse" , myMaterial->diffuse); myMaterial->shader->SetUniform3f("material.specular" , myMaterial->specular); myMaterial->shader->SetUniform1f("material.shininess" , myMaterial->shininess);
这样我们就有了一个很好的给物体附上材质的结构,不需要在渲染循环里面定义数据,所有的材质属性在这个材质创建出来时就已经赋予了。
光照贴图 在之前我们已经实现了贴图了,在这里我们主要是要重构一下我们上贴图的代码,让他的结构更好,主要的想法是让之前指定数值的方式变一下,让专门的贴图去做镜面光和漫反射光。
现在引入漫反射 和镜面光 贴图(Map)。这允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。所以之前的片段着色器代码我们要做一些改变,所有的vec3我们都要改成用贴图控制的sampler2D,因此我们还需要额外的纹理贴图输入。所以顶点着色器也要大改,首先要新增location,还需要给片段着色器输出贴图坐标。我们首先把之前加载的两个纹理全部取消,让立方体不上任何贴图,然后我们来改我们的代码。。。要改的地方太多,搞了我一节课。。。直接上改动了的代码吧:
顶点着色器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #version 330 core layout (location = 0 ) in vec3 aPos; layout (location = 1 ) in vec3 aNormal;layout (location = 2 ) in vec2 aTexCoord;uniform mat4 modelMat;uniform mat4 viewMat;uniform mat4 projMat;out vec3 FragPos;out vec3 Normal;out vec2 texCoord;void main() { gl_Position = projMat * viewMat * modelMat * vec4 (aPos.xyz, 1.0 ); FragPos = (modelMat * vec4 (aPos.xyz, 1.0 )).xyz; Normal = mat3 (transpose (inverse (modelMat))) * aNormal; texCoord = aTexCoord; }
顶点着色器重新定义之后就直接导致了Attribute要改变:
1 2 3 4 5 6 glVertexAttribPointer(0 , 3 , GL_FLOAT, GL_FALSE, 8 * sizeof (float ), (void *)0 ); glEnableVertexAttribArray(0 ); glVertexAttribPointer(1 , 3 , GL_FLOAT, GL_FALSE, 8 * sizeof (float ), (void *)(3 * sizeof (float ))); glEnableVertexAttribArray(1 ); glVertexAttribPointer(2 , 2 , GL_FLOAT, GL_FALSE, 8 * sizeof (float ), (void *)(6 * sizeof (float ))); glEnableVertexAttribArray(2 );
片段着色器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #version 330 core in vec3 FragPos;in vec3 Normal;in vec2 texCoord;struct Material{ vec3 ambient; sampler2D diffuse; sampler2D specular; float shininess; }; uniform Material material;uniform vec3 objColor;uniform vec3 ambientColor;uniform vec3 lightPos;uniform vec3 lightColor;uniform vec3 cameraPos;out vec4 FragColor; void main() { vec3 lightDir = normalize (lightPos - FragPos); vec3 reflectDir = reflect (-lightDir , Normal); vec3 viewDir = normalize (cameraPos - FragPos); vec3 specular = texture (material.specular,texCoord).rgb * pow (max (dot (reflectDir , viewDir),0 ),32 ) * lightColor; vec3 diffuse = texture (material.diffuse,texCoord).rgb * max (dot (lightDir, Normal),0 ) * lightColor ; vec3 ambient = texture (material.diffuse,texCoord).rgb * ambientColor; FragColor = vec4 ((diffuse + ambient + specular) * objColor, 1.0 ); }
shader:
1 2 3 4 5 6 7 8 9 10 enum Slot { DIFFUSE, SPECULAR }; void use();void SetUniform3f(const char* paramNameString,glm::vec3 param);void SetUniform1f(const char* paramNameString, float param);void SetUniform1i(const char* paramNameString, int slot);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void Shader::SetUniform3f (const char * paramNameString, glm::vec3 param) { glUniform3f(glGetUniformLocation(ID, paramNameString), param.x, param.y, param.z); } void Shader::SetUniform1f (const char * paramNameString, float param) { glUniform1f(glGetUniformLocation(ID, paramNameString), param); } void Shader::SetUniform1i (const char * paramNameString, int slot) { glUniform1i(glGetUniformLocation(ID, paramNameString), slot); }
main:
1 2 3 4 5 6 7 8 9 10 Material* myMaterial = new Material(myShader, LoadImageToGPU("container2.png" ,GL_RGBA,GL_RGBA,Shader::DIFFUSE), LoadImageToGPU("container2_specular.png" , GL_RGBA, GL_RGBA, Shader::SPECULAR), glm::vec3(1.0f ,1.0f ,1.0f ), 64.0f ); myMaterial->shader->SetUniform1i("material.diffuse" , Shader::DIFFUSE); myMaterial->shader->SetUniform1i("material.specular" , Shader::SPECULAR); myMaterial->shader->SetUniform3f("material.ambient" , myMaterial->ambient); myMaterial->shader->SetUniform1f("material.shininess" , myMaterial->shininess);
搞定了,是不是有点累?说实话有点烦,这东西搞了我一节课课。。。不说了,回去看我小ig的比赛了,要是输了这周末狂写码!!!
这就是加上了镜面光贴图后的效果,你可以看到金属一样的反光:
光源 在这里我们会去学习光源,分别对平行光、点光源和聚光灯进行实现。在之前我们做了一个Material的class,我们可以很方便地去使用材质,现在我们也来做一个Light类,以便我们去做出各种各样的灯光,然后去使用它。之前我们只是很简单地去指定灯的颜色和位置,现在我们来做一个大工程吧~~
平行光 首先去做一个平行光的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class LightDirectional { public: LightDirectional(Shader* _shader, glm::vec3 _angles, glm::vec3 _color = glm::vec3 (1.0 f, 1.0 f, 1.0 f)); Shader* shader; glm::vec3 position; glm::vec3 angles; glm::vec3 direction = glm::vec3 (0 , 0 , 1.0 f); glm::vec3 color; void UpdateDirection(); }; LightDirectional::LightDirectional(Shader* _shader,glm::vec3 _angles,glm::vec3 _color): shader(_shader), angles(_angles), color(_color) { UpdateDirection(); } void ::LightDirectional::UpdateDirection() { direction = glm::vec3 (0 , 0 , 1.0 f); direction = glm::rotateZ(direction, angles.z); direction = glm::rotateX(direction, angles.x); direction = glm::rotateY(direction, angles.y); direction = -1.0 f * direction; }
之前我们做的光的方向vec3 lightDir = normalize(lightPos - FragPos)
,其实只是点光源的计算方法而已,并且这种计算方法还有问题。我们现在应该直接喂给光的方向,这样才是真正的平行光,于是在片段着色器里面,我们uniform一个lightDir,然后直接在main里面给它赋值:
1 2 3 4 5 6 7 8 9 10 vec3 reflectDir = reflect (-lightDir , Normal);LightDirectional light = LightDirectional(myShader, glm::vec3 (glm::radians (45.0 f), 0 , 0 )); light.shader->SetUniform3f("lightColor", light.color); light.shader->SetUniform3f("lightDir", light.direction);
这样一来其实和之前的不一样的地方就只有:增加了一个平行光的类,直接给出lightDir的值不让他去和物体像素位置做计算。效果上就是只有一个方向有光,我们从45°打,就只有上面和前面有光:
点光源 点光源和平行光不一样的地方就是,他不管角度,只管位置,但是其他地方他们都十分像。首先我们要把之前的vec3 lightDir = normalize(lightPos - FragPos)
打开,这里需要正常地去计算光的方向,这也和我们最开始做的光一样(一开始我们做的光其实就是一个点光源地效果),直接看点光源类地代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class LightPoint { public : LightPoint(Shader* _shader, glm::vec3 _position, glm::vec3 _color = glm::vec3(1.0f , 1.0f , 1.0f )); Shader* shader; glm::vec3 position; glm::vec3 color; }; LightPoint::LightPoint(Shader* _shader, glm::vec3 _position, glm::vec3 _color): shader(_shader), position(_position), color(_color) { }
就这么简单,点光源就出来了:
但这个和现实差别很大,远处的箱子所受到地光照居然和近处是一样的,这里我们就需要做一个光照衰减。我们直接套用别人的公式就行了,用一个二次多项式去做衰减:
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct LightPoint{ float constant; float linear; float quadratic; }; uniform LightPoint lightPoint;float dist = length (lightPos - FragPos);float attenuation = 1.0 / (lightPoint.constant + lightPoint.linear * dist + lightPoint.quadratic * (dist * dist));FragColor = vec4 ((ambient + (diffuse + specular) * attenuation) * objColor, 1.0 );
初始化三个计算参数,main里面赋值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 LightPoint::LightPoint(Shader* _shader, glm::vec3 _position, glm::vec3 _color): shader(_shader), position(_position), color(_color) { constant = 1.0f ; linear = 0.09f ; quadratic = 0.032f ; } lightpoint.shader->SetUniform3f("lightPos" , lightpoint.position); lightpoint.shader->SetUniform3f("lightColor" , lightpoint.color); lightpoint.shader->SetUniform1f("lightPoint.constant" , lightpoint.constant); lightpoint.shader->SetUniform1f("lightPoint.linear" , lightpoint.linear); lightpoint.shader->SetUniform1f("lightPoint.quadratic" , lightpoint.quadratic);
效果如下:
这下可以很明显地看出,远一点的箱子没有被照得那么亮了。
聚光灯 和舞台上面的聚光灯一样,它只朝一个特定方向而不是所有方向照射光线,只有在聚光方向的特定半径内的物体才会被照亮,其它的物体都会保持黑暗。在实现这个灯时,我们要给出这个灯的最小照射cos值,然后当某个片元与光源的向量夹角小于了这个最大值,那么这个片元就无法被照射出。
首先我们需要计算出一个片元与光源的向量与光源方向的夹角的cos值,然后把这个值与最小的cos进行比较,如果小,就不去计算这个片元被光的照射。需要注意的是因为我们想得到的是cos值,所以我们要注意把得到的向量转换为单位向量。然后为了处理边缘太硬的问题,我们再设置一个内圈然后在这个环里面去做光由亮变暗的渐变:
1 2 3 4 5 6 7 8 9 10 11 12 13 float cosTheta = dot (normalize (FragPos-lightPos),-1 * lightDirUniform);float spotIndex; if (cosTheta>lightSpot.cosMinInner){ spotIndex = 1.0 f; } else if (cosTheta>lightSpot.cosMinOuter){ spotIndex = (cosTheta - lightSpot.cosMinOuter) / (lightSpot.cosMinInner - lightSpot.cosMinOuter); } else { spotIndex = 0 ; } FragColor = vec4 ((ambient + diffuse + specular) * spotIndex * objColor, 1.0 );
创建一个lightSpot类去在创建灯时就初始化它的位置、中心光照射方向、颜色,照射最大范围(最小cos值)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class LightSpot { public : LightSpot(Shader* _shader, glm::vec3 _position, glm::vec3 _angles, float range, glm::vec3 _color = glm::vec3(1.0f , 1.0f , 1.0f )); Shader* shader; glm::vec3 position; glm::vec3 angles; glm::vec3 direction = glm::vec3(0 , 0 , 1.0f ); glm::vec3 color; float cosMinInner; float cosMinOuter = cosMinInner - 0.05f ; void UpdateDirection () ; }; LightSpot::LightSpot(Shader* _shader, glm::vec3 _position , glm::vec3 _angles,float _range,glm::vec3 _color): shader(_shader), position(_position), angles(_angles), color(_color), cosMinInner(_range) { UpdateDirection(); } void LightSpot::UpdateDirection () { direction = glm::vec3(0 , 0 , 1.0f ); direction = glm::rotateZ(direction, angles.z); direction = glm::rotateX(direction, angles.x); direction = glm::rotateY(direction, angles.y); direction = -1.0f * direction; }
main:
1 2 3 4 5 6 7 LightSpot lightSpot = LightSpot(myShader, glm::vec3(0.0f , 5.0f , 0.0f ), glm::vec3(glm::radians(90.0f ), 0 , 0 ), 0.9f ); lightSpot.shader->SetUniform3f("lightPos" , lightSpot.position); lightSpot.shader->SetUniform3f("lightColor" , lightSpot.color); lightSpot.shader->SetUniform3f("lightDirUniform" , lightSpot.direction); lightSpot.shader->SetUniform1f("lightSpot.cosMinInner" , lightSpot.cosMinInner); lightSpot.shader->SetUniform1f("lightSpot.cosMinOuter" , lightSpot.cosMinOuter);
这样就可以得到很好的聚光灯效果:
多光源 现在我们来做多光源,我们需要把之前的代码重构,用一个计算光照的函数去计算所有的光照效果,然后作为颜色的输出。在片段着色器里面我们分别用三个函数去计算,然后再去叠加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 out vec4 FragColor; vec3 CalLightDirectional(LightDirectional light, vec3 uNormal, vec3 dirToCamera){ float diffuseIntensity = max (dot (light.dirToLight , uNormal),0 ); vec3 diffuseColor = diffuseIntensity * light.color * texture (material.diffuse,texCoord).rgb; float specularIntensity = pow (max (dot (normalize (reflect (-light.dirToLight,uNormal)),dirToCamera),0 ),material.shininess); vec3 specularColor = specularIntensity * light.color * texture (material.specular, texCoord).rgb; vec3 result = diffuseColor + specularColor; return result; } vec3 CalLightPoint(LightPoint light, vec3 uNormal, vec3 dirToCamera){ float dist = length (light.pos - FragPos); float attenuation = 1 / (light.constant + light.linear * dist + light.quadratic * dist * dist); float diffuseIntensity = max (dot (normalize (light.pos - FragPos), uNormal),0 ) * attenuation; vec3 diffuseColor = diffuseIntensity * light.color * texture (material.diffuse,texCoord).rgb; float specularIntensity = pow (max (dot (-normalize (reflect (light.pos - FragPos,uNormal)),dirToCamera),0 ),material.shininess); vec3 specularColor = specularIntensity * light.color * texture (material.specular, texCoord).rgb; vec3 result = (diffuseColor + specularColor) * attenuation; return result; } vec3 CalLightSpot(LightSpot light, vec3 uNormal, vec3 dirToCamera){ float diffuseIntensity = max (dot (normalize (light.pos - FragPos), uNormal),0 ); vec3 diffuseColor = diffuseIntensity * light.color * texture (material.diffuse,texCoord).rgb; float specularIntensity = pow (max (dot (-normalize (reflect (light.pos - FragPos,uNormal)),dirToCamera),0 ),material.shininess); vec3 specularColor = specularIntensity * light.color * texture (material.specular, texCoord).rgb; float cosTheta = dot (normalize (FragPos-light.pos),-1 * light.dirToLight); float spotIndex; if (cosTheta>light.cosMinInner){ spotIndex = 1.0 f; } else if (cosTheta>light.cosMinOuter){ spotIndex = (cosTheta - light.cosMinOuter) / (light.cosMinInner - light.cosMinOuter); } else { spotIndex = 0 ; } vec3 result = (diffuseColor + specularColor) * spotIndex * objColor; return result; } void main() { vec3 finalResult = vec3 (0.0 f,0.0 f,0.0 f); vec3 uNormal = normalize (Normal); vec3 dirToCamera = normalize (cameraPos - FragPos); finalResult += CalLightDirectional(lightDirectional, uNormal, dirToCamera); finalResult += CalLightPoint(lightP0, uNormal, dirToCamera); finalResult += CalLightPoint(lightP1, uNormal, dirToCamera); finalResult += CalLightPoint(lightP2, uNormal, dirToCamera); finalResult += CalLightPoint(lightP3, uNormal, dirToCamera); finalResult += CalLightSpot(lightS, uNormal, dirToCamera); FragColor = vec4 (finalResult, 1.0 ); }
在main函数里面进行赋值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 LightDirectional lightD = LightDirectional(myShader, glm::vec3(glm::radians(90.0f ), 0 , 0 )); LightPoint lightP0 = LightPoint(myShader, glm::vec3(1.0f , 0.0f , 0.0f ),glm::vec3(1.0f ,0.0f ,0.0f )); LightPoint lightP1 = LightPoint(myShader, glm::vec3(0.0f , 1.0f , 0.0f ), glm::vec3(0.0f , 1.0f , 0 )); LightPoint lightP2 = LightPoint(myShader, glm::vec3(0.0f , 0.0f , 1.0f ), glm::vec3(0 , 0.0f , 1.0f )); LightPoint lightP3 = LightPoint(myShader, glm::vec3(1.0f , 1.0f , 1.0f ), glm::vec3(1.0f , 1.0f , 1.0f )); LightSpot lightS = LightSpot(myShader, glm::vec3(0 , 5.0f , 0 ), glm::vec3(glm::radians(90.0f ), 0 , 0 ), 0.9f ); lightD.shader->SetUniform3f("lightDirectional.color" , lightD.color); lightD.shader->SetUniform3f("lightDirectional.dirToLight" , lightD.direction); lightP0.shader->SetUniform3f("lightP0.pos" , lightP0.position); lightP0.shader->SetUniform3f("lightP0.color" , lightP0.color); lightP0.shader->SetUniform1f("lightP0.constant" , lightP0.constant); lightP0.shader->SetUniform1f("lightP0.linear" , lightP0.linear); lightP0.shader->SetUniform1f("lightP0.quadratic" , lightP0.quadratic); lightP1.shader->SetUniform3f("lightP1.pos" , lightP1.position); lightP1.shader->SetUniform3f("lightP1.color" , lightP1.color); lightP1.shader->SetUniform1f("lightP1.constant" , lightP1.constant); lightP1.shader->SetUniform1f("lightP1.linear" , lightP1.linear); lightP1.shader->SetUniform1f("lightP1.quadratic" , lightP0.quadratic); lightP2.shader->SetUniform3f("lightP2.pos" , lightP2.position); lightP2.shader->SetUniform3f("lightP2.color" , lightP2.color); lightP2.shader->SetUniform1f("lightP2.constant" , lightP2.constant); lightP2.shader->SetUniform1f("lightP2.linear" , lightP2.linear); lightP2.shader->SetUniform1f("lightP2.quadratic" , lightP2.quadratic); lightP3.shader->SetUniform3f("lightP3.pos" , lightP3.position); lightP3.shader->SetUniform3f("lightP3.color" , lightP3.color); lightP3.shader->SetUniform1f("lightP3.constant" , lightP3.constant); lightP3.shader->SetUniform1f("lightP3.linear" , lightP3.linear); lightP3.shader->SetUniform1f("lightP3.quadratic" , lightP3.quadratic); lightS.shader->SetUniform3f("lightS.pos" , lightS.position); lightS.shader->SetUniform3f("lightS.color" , lightS.color); lightS.shader->SetUniform3f("lightS.dirToLight" , lightS.direction); lightS.shader->SetUniform1f("lightS.cosMinInner" , lightS.cosMinInner); lightS.shader->SetUniform1f("lightS.cosMinOuter" , lightS.cosMinOuter);
这样就得到了多光源照射的效果:
好了,第二章也学习完了,现在OpenGL的基本使用差不多可以了,我想先跳过模型加载的章节,直接进入高级OpenGL和高级光照,最后我们再来做模型加载。