定向光
当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light),因为所有的光线都有着同一个方向;它会独立于光源的位置。
我们知道的定向光源的一个好例子是,太阳。太阳和我们不是无限远,但它也足够远了,在计算光照的时候,我们感觉它就像无限远。在下面的图片里,来自于太阳的所有的光线都被定义为平行光:
![](https://img.haomeiwen.com/i1638260/6d5653b3872619ae.png)
因为所有的光线都是平行的,对于场景中的每个物体光的方向都保持一致,物体和光源的位置保持怎样的关系都无所谓。由于光的方向向量保持一致,光照计算会和场景中的其他物体相似。
我们可以通过定义一个光的方向向量,来模拟这样一个定向光,而不是使用光的位置向量。着色器计算保持大致相同的要求,这次我们直接使用光的方向向量来代替用lightDir向量和position向量的计算:
struct Light
{
// vec3 position; // 现在不在需要光源位置了,因为它是无限远的
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
...
void main()
{
vec3 lightDir = normalize(-light.direction);
...
}
在投光物这章中, 关于定向光的源码, 对着源代码调试, 好像是有些问题的. 源码对Cube做了一些旋转. 总共添加了10个.
最开始以为这里要做的是, 添加10个Cube, 就算旋转的方向各式各样, 但是接受的光源都是从同一个方向的.
运行的结果与实际的有点偏差,研究片元着色器.
void main()
{
vec3 lightDir = normalize(-light.direction);
//环境光ambient
//环境颜色 = 环境光照 × 贴图
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
//漫反射diffuse
//DiffuseFactor = max(0, dot(N, L))
//漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
vec3 norm = normalize(Normal);
float diffuseFactor = max(dot(norm, lightDir),0.0);
vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords));
//镜面反射specular
//R=reflect(L, N)
//SpecularFactor = pow(max(dot(R,V),0.0), shininess)
//镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir , norm);
float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords));
//最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
vec3 result = ambient + diffuse + specular;
color = vec4(result , 1.0f);
}
因为light.direction的值是固定的, 所以, Cube的亮的一面也是固定的. 旋转的话会把亮的一面旋转走了. 这里体现不出来那种一个光源照射下来, 多个Cube体现不同的阴暗面.
现在没有这个效果就先放着放下看.
![](https://img.haomeiwen.com/i1638260/2686aaab8c697997.gif)
点光源
定向光作为全局光可以照亮整个场景,这非常棒,但是另一方面除了定向光,我们通常也需要几个点光源(Point Light),在场景里发亮。点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。
![](https://img.haomeiwen.com/i1638260/88408f6684c3e3b4.png)
之前的教程我们已经使用了(最简单的)点光。我们指定了一个光源以及其所在的位置,它从这个位置向所有方向发散光线。然而,我们定义的光源所模拟光线的强度却不会因为距离变远而衰减,这使得看起来像是光源亮度极强。在大多数3D仿真场景中,我们更希望去模拟一个仅仅能照亮靠近光源点附近场景的光源,而不是照亮整个场景的光源。
如果你把10个箱子添加到之前教程的光照场景中,你会注意到黑暗中的每个箱子都会有同样的亮度,就像箱子在光照的前面;没有公式定义光的距离衰减。我们想让黑暗中与光源比较近的箱子被轻微地照亮
衰减 Fatt=1.0 / (Kc+Kl∗d+Kq∗d2)
在这里d 代表片段到光源的距离。为了计算衰减值,我们定义3个(可配置)项:常数项Kc,一次项Kl 和二次项Kq 。
根据根式来看, 当d, 距离越远, 衰减Fatt的值越小. 也可以理解为看到光衰减的效果越小
- 常数项通常是1.0,它的作用是保证分母永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。
- 一次项用于与距离值相乘,这会以线性的方式减少亮度。
- 二次项用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大。
![](https://img.haomeiwen.com/i1638260/4d89203ff8b194fc.png)
你可以看到当距离很近的时候光有最强的亮度,但是随着距离增大,亮度明显减弱,大约接近100的时候,就会慢下来。这就是我们想要的。
距离 | 常数项 | 一次项 | 二次项 |
---|---|---|---|
7 | 1.0 | 0.7 | 1.8 |
13 | 1.0 | 0.35 | 0.44 |
20 | 1.0 | 0.22 | 0.20 |
32 | 1.0 | 0.14 | 0.07 |
50 | 1.0 | 0.09 | 0.032 |
65 | 1.0 | 0.07 | 0.017 |
100 | 1.0 | 0.045 | 0.0075 |
160 | 1.0 | 0.027 | 0.0028 |
200 | 1.0 | 0.022 | 0.0019 |
325 | 1.0 | 0.014 | 0.0007 |
600 | 1.0 | 0.007 | 0.0002 |
3250 | 1.0 | 0.0014 | 0.000007 |
处理不同的衰减得到的不同的效果
片元着色器Shader
//片元着色器程序
static char *myLightCastersFragmentShaderSrc = SHADER(
\#version 330 core\n
//材质
/**
要记住的是sampler2D也叫做模糊类型,这意味着我们不能以某种类型对它实例化,只能用uniform定义它们。
如果我们用结构体而不是uniform实例化(就像函数的参数那样),GLSL会抛出奇怪的错误;这同样也适用于其他模糊类型。
*/
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;//镜面反射散射因子(半径).
};
//光照强度
struct Light {
vec3 position;//光源位置
vec3 direction;//光源方向
vec3 ambient;//环境光照
vec3 diffuse;//漫反射光照
vec3 specular;//镜面反射光照
//衰减
float constant; //常量
float linear;//一次项
float quadratic;//二次项
};
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;
uniform vec3 viewPos;//镜面反射
uniform Material material;//材质
uniform Light light;
out vec4 color;
void main()
{
// vec3 lightDir = normalize(-light.direction);
vec3 lightDir = normalize(light.position - FragPos);
//环境光ambient
//环境颜色 = 环境光照 × 贴图
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
//漫反射diffuse
//DiffuseFactor = max(0, dot(N, L))
//漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
vec3 norm = normalize(Normal);
float diffuseFactor = max(dot(norm, lightDir),0.0);
vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords));
//镜面反射specular
//R=reflect(L, N)
//SpecularFactor = pow(max(dot(R,V),0.0), shininess)
//镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir , norm);
float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords));
float distance = length(light.position - FragPos);
float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));
//最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
vec3 result = ambient * attenuation + diffuse * attenuation + specular * attenuation;
color = vec4(result , 1.0f);
}
);
程序
//衰减
GLint lightConstantLoc = glGetUniformLocation(myProgram.program, "light.constant");
GLint lightLinearLoc = glGetUniformLocation(myProgram.program, "light.linear");
GLint lightQuadraticLoc = glGetUniformLocation(myProgram.program, "light.quadratic");
glUniform1f(lightConstantLoc, 1.0f);
// //7
// glUniform1f(lightLinearLoc, 0.7f);
// glUniform1f(lightQuadraticLoc, 1.8f);
//
// //13
// glUniform1f(lightLinearLoc, 0.35f);
// glUniform1f(lightQuadraticLoc, 0.44f);
//
// //20
// glUniform1f(lightLinearLoc, 0.22f);
// glUniform1f(lightQuadraticLoc, 0.20f);
//
// //32
// glUniform1f(lightLinearLoc, 0.14f);
// glUniform1f(lightQuadraticLoc, 0.07f);
//
// //50
// glUniform1f(lightLinearLoc, 0.09f);
// glUniform1f(lightQuadraticLoc, 0.032f);
//
// //100
// glUniform1f(lightLinearLoc, 0.045f);
// glUniform1f(lightQuadraticLoc, 0.0075f);
//
// //200
// glUniform1f(lightLinearLoc, 0.022f);
// glUniform1f(lightQuadraticLoc, 0.0019f);
//
// //3250
// glUniform1f(lightLinearLoc, 0.0014f);
// glUniform1f(lightQuadraticLoc, 0.000007f);
![](https://img.haomeiwen.com/i1638260/18ed1524311e93f9.png)
![](https://img.haomeiwen.com/i1638260/75befc41db654438.png)
![](https://img.haomeiwen.com/i1638260/fafb88b774900a14.png)
![](https://img.haomeiwen.com/i1638260/c9ac73db3381c76d.png)
这里简单粗暴理解为 : 当距离越远, 你能看到的光衰减的效果越弱.
聚光
我们要讨论的最后一种类型光是聚光(Spotlight)。聚光是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光的好例子是路灯或手电筒。
OpenGL中的聚光用世界空间位置,一个方向和一个指定了聚光半径的切光角来表示。我们计算的每个片段,如果片段在聚光的切光方向之间(就是在圆锥体内),我们就会把片段照亮。下面的图可以让你明白聚光是如何工作的:
![](https://img.haomeiwen.com/i1638260/914fe1daedfe1054.png)
- LightDir:从片段指向光源的向量。
- SpotDir:聚光所指向的方向。
- Phiϕ:定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
- Thetaθ:LightDir向量和SpotDir向量之间的角度。θ值应该比Φ值小,这样才会在聚光内。
所以我们大致要做的是,计算LightDir向量和SpotDir向量的点乘(返回两个单位向量的点乘,还记得吗?),然后在和切光角ϕ对比。现在你应该明白聚光是我们下面将创建的手电筒的范例。
手电筒
片元着色器
//片元着色器程序
static char *myLightCastersFragmentShaderSrc = SHADER(
\#version 330 core\n
//材质
/**
要记住的是sampler2D也叫做模糊类型,这意味着我们不能以某种类型对它实例化,只能用uniform定义它们。
如果我们用结构体而不是uniform实例化(就像函数的参数那样),GLSL会抛出奇怪的错误;这同样也适用于其他模糊类型。
*/
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;//镜面反射散射因子(半径).
};
//光照强度
struct Light {
vec3 position;//光源位置
vec3 direction;//光源方向
vec3 ambient;//环境光照
vec3 diffuse;//漫反射光照
vec3 specular;//镜面反射光照
//衰减
float constant; //常量
float linear;//一次项
float quadratic;//二次项
//聚光
float cutOff;
};
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;
uniform vec3 viewPos;//镜面反射
uniform Material material;//材质
uniform Light light;
out vec4 color;
void main()
{
// vec3 lightDir = normalize(-light.direction);
vec3 lightDir = normalize(light.position - FragPos);
// Check if lighting is inside the spotlight cone
float theta = dot(lightDir, normalize(-light.direction));
if(theta > light.cutOff) // 执行光照计算.
{
//环境光ambient
//环境颜色 = 环境光照 × 贴图
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
//漫反射diffuse
//DiffuseFactor = max(0, dot(N, L))
//漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
vec3 norm = normalize(Normal);
float diffuseFactor = max(dot(norm, lightDir),0.0);
vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords));
//镜面反射specular
//R=reflect(L, N)
//SpecularFactor = pow(max(dot(R,V),0.0), shininess)
//镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir , norm);
float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords));
float distance = length(light.position - FragPos);
float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));
//最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
vec3 result = ambient * attenuation + diffuse * attenuation + specular * attenuation;
color = vec4(result , 1.0f);
} else {
//使用环境光,使得场景不至于完全黑暗
color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0f);
}
}
);
程序
//光照
GLint lightPosLoc = glGetUniformLocation(myProgram.program, "light.position");
GLint directionPos = glGetUniformLocation(myProgram.program, "light.direction");
GLint lightAmbientLoc = glGetUniformLocation(myProgram.program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(myProgram.program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(myProgram.program, "light.specular");
glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
glUniform3f(directionPos, -0.2f, -1.0f, -0.3f);
glUniform3f(lightAmbientLoc, 0.1f, 0.1f, 0.1f);
glUniform3f(lightDiffuseLoc, 0.9f, 0.9f, 0.9f);
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
//衰减
GLint lightConstantLoc = glGetUniformLocation(myProgram.program, "light.constant");
GLint lightLinearLoc = glGetUniformLocation(myProgram.program, "light.linear");
GLint lightQuadraticLoc = glGetUniformLocation(myProgram.program, "light.quadratic");
glUniform1f(lightConstantLoc, 1.0f);
glUniform1f(lightLinearLoc, 0.09f);
glUniform1f(lightQuadraticLoc, 0.032f);
//聚光
GLint lightCutOffLoc = glGetUniformLocation(myProgram.program, "light.cutOff");
glUniform1f(lightCutOffLoc, glm::cos(glm::radians(44.5f)));
你可以看到,我们为切光角设置一个角度,但是我们根据一个角度计算了余弦值,把这个余弦结果传给了片段着色器。这么做的原因是在片段着色器中,我们计算LightDir和SpotDir向量的点乘,而点乘返回一个余弦值,不是一个角度,所以我们不能直接把一个角度和余弦值对比。为了获得这个角度,我们必须计算点乘结果的反余弦,这个操作开销是很大的。所以为了节约一些性能,我们先计算给定切光角的余弦值,然后把结果传递给片段着色器。由于每个角度都被表示为余弦了,我们可以直接对比它们,而不用进行任何开销高昂的操作。
现在剩下要做的是计算θ值,用它和ϕ值对比,以决定我们是否在或不在聚光的内部:
![](https://img.haomeiwen.com/i1638260/98858466c711d2e0.png)
平滑/软化边缘
为了创建一种看起来边缘平滑的聚光,我们需要模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)。我们可以将内圆锥设置为上一部分中的那个圆锥,但我们也需要一个外圆锥,来让光从内圆锥逐渐减暗,直到外圆锥的边界。
为了创建一个外圆锥,我们只需要再定义一个余弦值来代表聚光方向向量和外圆锥向量(等于它的半径)的夹角。然后,如果一个片段处于内外圆锥之间,将会给它计算出一个0.0到1.0之间的强度值。如果片段在内圆锥之内,它的强度就是1.0,如果在外圆锥之外强度值就是0.0。
我们可以用下面这个公式来计算这个值:
![](https://img.haomeiwen.com/i1638260/e28f65437b3757d1.png)
这里ϵ(Epsilon)是内(ϕ)和外圆锥(γ)之间的余弦值差(ϵ=ϕ−γ)。最终的I值就是在当前片段聚光的强度。
很难去表现这个公式是怎么工作的,所以我们用一些实例值来看看:
![](https://img.haomeiwen.com/i1638260/2863322c7ecd2d85.png)
你可以看到,我们基本是在内外余弦值之间根据θθ插值。如果你仍不明白发生了什么,不必担心,只需要记住这个公式就好了,在你更聪明的时候再回来看看。
我们现在有了一个在聚光外是负的,在内圆锥内大于1.0的,在边缘处于两者之间的强度值了。如果我们正确地约束(Clamp)这个值,在片段着色器中就不再需要if-else了,我们能够使用计算出来的强度值直接乘以光照分量:
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
...
// 将不对环境光做出影响,让它总是能有一点光
diffuse *= intensity;
specular *= intensity;
...
上面的效果像是有一般手电筒然后照在箱子上, 但是边缘没有羽化的效果. 接下来为了更好的体现羽化效果. 重新修改以下Shader和程序
Shader
//片元着色器程序
static char *myLightCastersFragmentShaderSrc = SHADER(
\#version 330 core\n
//材质
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;//镜面反射散射因子(半径).
};
//光照强度
struct Light {
vec3 position;//光源位置
vec3 direction;//光源方向
vec3 ambient;//环境光照
vec3 diffuse;//漫反射光照
vec3 specular;//镜面反射光照
//衰减
float constant; //常量
float linear;//一次项
float quadratic;//二次项
//聚光
float cutOff;
float outerCutOff;
};
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;
uniform vec3 viewPos;//镜面反射
uniform Material material;//材质
uniform Light light;
out vec4 color;
void main()
{
// vec3 lightDir = normalize(-light.direction);
vec3 lightDir = normalize(light.position - FragPos);
//聚光角度
float theta = dot(lightDir, normalize(-light.direction));
//羽化(如果使用clamp 就不要用if-else了)
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
// if(theta > light.cutOff) // 执行光照计算.
// {
//环境光ambient
//环境颜色 = 环境光照 × 贴图
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
//漫反射diffuse
//DiffuseFactor = max(0, dot(N, L))
//漫反射颜色 = 漫反射因子(diffuseFactor) × 漫反射光照 × 贴图
vec3 norm = normalize(Normal);
float diffuseFactor = max(dot(norm, lightDir),0.0);
vec3 diffuse = diffuseFactor * light.diffuse * vec3(texture(material.diffuse, TexCoords)) ;
//镜面反射specular
//R=reflect(L, N)
//SpecularFactor = pow(max(dot(R,V),0.0), shininess)
//镜面反射颜色 = 镜面反射因子(SpecularFactor) × 镜面光照 × 贴图
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir , norm);
float specularFactor = pow(max(dot(viewDir, reflectDir),0.0),material.shininess);
vec3 specular = specularFactor * light.specular * vec3(texture(material.specular, TexCoords)) ;
float distance = length(light.position - FragPos);
float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));
//最终片段颜色:环境颜色+漫反射颜色+镜面反射颜色
vec3 result = ambient * attenuation + diffuse * attenuation * intensity + specular * attenuation* intensity;
color = vec4(result , 1.0f);
// } else {
// //使用环境光,使得场景不至于完全黑暗
// color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0f);
// }
}
);
程序
glm::vec3 cubePositions[] = {
glm::vec3( 0.0f, 0.0f, 0.0f),
};
int cubePositionsCount = sizeof(cubePositions)/(sizeof(cubePositions[0]));
//进行绘制
while(!glfwWindowShouldClose(window)){
glfwPollEvents();
do_movement();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//================================================
glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
GLint myModelLoc = glGetUniformLocation(myProgram.program,"myModel");
GLint myViewLoc = glGetUniformLocation(myProgram.program,"myView");
GLint myProjectionLoc = glGetUniformLocation(myProgram.program,"myProjection");
projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.01f, 100.f);//投影矩阵
glUniformMatrix4fv(myProjectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
view = camera.GetViewMatrix();
glUniform3f(myViewLoc, camera.Position.x, camera.Position.y, camera.Position.z);
glUniformMatrix4fv(myViewLoc, 1, GL_FALSE, glm::value_ptr(view));
glBindVertexArray(VAO);
//================================================
//================================================
//镜面反射半径
GLint matShineLoc = glGetUniformLocation(myProgram.program, "material.shininess");
glUniform1f(matShineLoc, 64.0f);
GLint viewPosLoc = glGetUniformLocation(myProgram.program, "viewPos");
glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
//光照方向
//光照
GLint lightPosLoc = glGetUniformLocation(myProgram.program, "light.position");
GLint directionPosLoc = glGetUniformLocation(myProgram.program, "light.direction");
GLint lightAmbientLoc = glGetUniformLocation(myProgram.program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(myProgram.program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(myProgram.program, "light.specular");
// glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
// glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
glUniform3f(lightPosLoc, 0.0f, 0.0f, 5.0f);
glUniform3f(directionPosLoc, 0.0f,0.0f, -2.0f);
// glUniform3f(directionPosLoc, camera.Front.x, camera.Front.y, camera.Front.z);
glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
glUniform3f(lightDiffuseLoc, 0.9f, 0.9f, 0.9f);
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
//衰减
GLint lightConstantLoc = glGetUniformLocation(myProgram.program, "light.constant");
GLint lightLinearLoc = glGetUniformLocation(myProgram.program, "light.linear");
GLint lightQuadraticLoc = glGetUniformLocation(myProgram.program, "light.quadratic");
glUniform1f(lightConstantLoc, 1.0f);
glUniform1f(lightLinearLoc, 0.09f);
glUniform1f(lightQuadraticLoc, 0.032f);
//聚光
GLint lightCutOffLoc = glGetUniformLocation(myProgram.program, "light.cutOff");
glUniform1f(lightCutOffLoc, glm::cos(glm::radians(12.5f)));
//羽化
GLint lightOuterCutOffLoc = glGetUniformLocation(myProgram.program, "light.outerCutOff");
glUniform1f(lightOuterCutOffLoc, glm::cos(glm::radians(17.5f)));
//================================================
for(GLuint i = 0; i < cubePositionsCount; I++)
{
model = glm::translate(model, cubePositions[I]);
model = glm::scale(model, glm:: vec3(3.0f,3.0f,3.0f));
GLfloat angle = 20.0f * I;
model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
glUniformMatrix4fv(myModelLoc, 1, GL_FALSE, glm::value_ptr(model));
glDrawArrays(GL_TRIANGLES, 0, squareIndicesCount);
}
glBindVertexArray(0);
glfwSwapBuffers(window);
}
重要 :
程序中重新修改了light.position(光源位置)和light.direction(光线方向), 主要为了更好的体现羽化的效果.
如果我们正确地约束(Clamp)这个值,在片段着色器中就不再需要if-else了
羽化前效果
![](https://img.haomeiwen.com/i1638260/65630cbce9f89ef7.png)
羽化后效果
![](https://img.haomeiwen.com/i1638260/2ac35d2b1de9dec4.png)
多个Cube羽化后效果
修改一下程序
GLint lightPosLoc = glGetUniformLocation(myProgram.program, "light.position");
GLint directionPosLoc = glGetUniformLocation(myProgram.program, "light.direction");
glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
glUniform3f(directionPosLoc, camera.Front.x, camera.Front.y, camera.Front.z);
![](https://img.haomeiwen.com/i1638260/f6183c6f9ead7621.gif)
网友评论