定向光
当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light)。
太阳光就有此特征,所以我们只需要知道方向即可。
vec3 lightDir = normalize(-light.direction);
人们通常更习惯定义一个定向光作为一个全局方向,它从光源发出。所以我们必须对全局光的方向向量取反来改变它的方向;它现在是一个方向向量指向光源。同时,确保对向量进行标准化处理
同时,不要忘记定义光源的方向(注意,我们把方向定义为:从光源处发出的方向;在下面,你可以快速看到光的方向的指向):
GLint lightDirPos = glGetUniformLocation(lightingShader.Program, "light.direction");
glUniform3f(lightDirPos, -0.2f, -1.0f, -0.3f);
现象:通过定向光可以照亮整个场景。
点光源
之前的教程我们已经使用了(最简单的)点光。我们指定了一个光源以及其所在的位置,它从这个位置向所有方向发散光线。在大多数3D仿真场景中,我们更希望去模拟一个离光源越近越亮,越远的越暗,而不是照亮整个场景的光源。
衰减
随着光线穿越距离的变远使得亮度也相应地减少的现象,通常称之为衰减(Attenuation)
衰减值:
在这里d代表片段到光源的距离。为了计算衰减值,我们定义3个(可配置)项:常数项Kc,一次项Kl和二次项Kq。
常数项通常是1.0,它的作用是保证分母永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。
一次项用于与距离值相乘,这会以线性的方式减少亮度。
二次项用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大
多种场景下各参数的表:
default.frag
#version 330 core
out vec4 color;
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;
//uniform vec3 lightPos;
//uniform vec3 lightColor;
//uniform vec3 objectColor;
uniform vec3 viewPos;
struct Material
{
sampler2D diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
struct Light
{
vec3 position;
//vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
uniform Light light;
void main()
{
// 环境光
vec3 ambient = light.ambient * vec3(texture(material.diffuse,TexCoords));
// 漫反射光
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position-FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse,TexCoords));
// 镜面高光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * (spec * material.specular);
float distance1=length(light.position-FragPos);//求向量长度
float attenuation=1.0f/(light.constant+light.linear*distance1+light.quadratic*(distance1*distance1));
ambient*=attenuation;
diffuse*=attenuation;
specular*=attenuation;
vec3 result = ambient + diffuse + specular;
color = vec4(result, 1.0f);
}
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.constant"), 1.0f);
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.linear"), 0.09);
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.quadratic"), 0.032);
聚光
类似与手电筒。
LightDir:从片段指向光源的向量。
SpotDir:聚光所指向的方向。
Phiϕ:定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
Thetaθ:LightDir向量和SpotDir向量之间的角度。θ值应该比Φ值小,这样才会在聚光内。
我们需要光源的位置,方向和切光角。
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)));
上面为什么用cos?
我们计算LightDir和SpotDir向量的点乘,而点乘返回一个余弦值,不是一个角度,节约性能
现在剩下要做的是计算θ值,用它和ϕ值对比,以决定我们是否在或不在聚光的内部:
float theta = dot(lightDir, normalize(-light.direction));
if(theta > light.cutOff)
{
// 执行光照计算
}
else // 否则使用环境光,使得场景不至于完全黑暗
color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f);
默认光的方向是从光源作为指向出发点,而我们需要指向光源的方向所以取反,跟片断指向光源的向量点乘,然后角度小于切光角,则照亮,
但cos在0到90度,度数越大,cos值越小,所以用大于号,小于切光角则只有环境光
边缘软化/平滑
为创建聚光的平滑边,我们希望去模拟的聚光有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。
为创建外圆锥,我们简单定义另一个余弦值,它代表聚光的方向向量和外圆锥的向量(等于它的半径)的角度。然后,如果片段在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0,如果在外面就是0.0。
我们可以使用下面的公式计算这样的值:
这里ϵ是内部(ϕ)和外部圆锥(γ)(\epsilon = \phi - \gamma)的差,
分子被减数即切光角的cos值
当在聚光外的时候是个负的,当在内部圆锥以内大于1。如果我们适当地把这个值固定,我们在片段着色器中就再不需要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);
...
// We’ll leave ambient unaffected so we always have a little
light.diffuse* = intensity;
specular* = intensity;
...
现象:
default.frag
#version 330 core
out vec4 color;
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;
//uniform vec3 lightPos;
//uniform vec3 lightColor;
//uniform vec3 objectColor;
uniform vec3 viewPos;
struct Material
{
sampler2D diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
struct Light
{
vec3 position;
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float cutOff;//内圆锥的向量
float outerCutOff;//外圆锥的向量
float constant;
float linear;
float quadratic;
};
uniform Light light;
void main()
{
//切光角
vec3 lightDir = normalize(light.position-FragPos);
float theta=dot(lightDir,normalize(-light.direction));
//平滑参数
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
// 环境光
vec3 ambient = light.ambient * vec3(texture(material.diffuse,TexCoords));
// 漫反射光
vec3 norm = normalize(Normal);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse =light.diffuse * diff * vec3(texture(material.diffuse,TexCoords));
// 镜面高光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * (spec * material.specular);
float distance1=length(light.position-FragPos);
float attenuation=1.0f/(light.constant+light.linear*distance1+light.quadratic*(distance1*distance1));
//ambient*=attenuation;
diffuse*=attenuation;
specular*=attenuation;
diffuse*=intensity;
specular*=intensity;
vec3 result = ambient + diffuse + specular;
color = vec4(result, 1.0f);
//color=vec4( light.ambient * vec3(texture(material.diffuse,TexCoords)),1.0f);
}
shader.cpp(跟frag有关的代码)
ourShader.Use();
//GLint objectColorLoc = glGetUniformLocation(ourShader.Program, "objectColor");
//GLint lightColorLoc = glGetUniformLocation(ourShader.Program, "lightColor");
GLint lightPosLoc = glGetUniformLocation(ourShader.Program, "light.position");
GLint viewPosLoc = glGetUniformLocation(ourShader.Program, "viewPos");
GLint lightSpotdirLoc = glGetUniformLocation(ourShader.Program, "light.direction");
GLint lightSpotCutOffLoc = glGetUniformLocation(ourShader.Program, "light.cutOff");
GLint lightSpotOutCutOffLoc = glGetUniformLocation(ourShader.Program, "light.outerCutOff");
//glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);
//glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f);
glUniform3f(lightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
glUniform3f(viewPosLoc, 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)));
glUniform1f(lightSpotOutCutOffLoc, glm::cos(glm::radians(17.5f)));
//GLint matAmbientLoc = glGetUniformLocation(ourShader.Program, "material.ambient");
//GLint matDiffuseLoc = glGetUniformLocation(ourShader.Program, "material.diffuse");
GLint matSpecularLoc = glGetUniformLocation(ourShader.Program, "material.specular");
GLint matShineLoc = glGetUniformLocation(ourShader.Program, "material.shininess");
//glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
//glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
glUniform1f(matShineLoc, 32.0f);
GLint lightAmbientLoc = glGetUniformLocation(ourShader.Program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(ourShader.Program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(ourShader.Program, "light.specular");
glUniform3f(lightAmbientLoc, 0.1f, 0.1f, 0.1f);
glUniform3f(lightDiffuseLoc, 0.8f, 0.8f, 0.8f);// 让我们把这个光调暗一点,这样会看起来更自然
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
glUniform1f(glGetUniformLocation(ourShader.Program, "light.constant"), 1.0f);
glUniform1f(glGetUniformLocation(ourShader.Program, "light.linear"), 0.09);
glUniform1f(glGetUniformLocation(ourShader.Program, "light.quadratic"), 0.032);
// Create camera transformations
glm::mat4 view;
view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
// Get the uniform locations
GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
GLint viewLoc = glGetUniformLocation(lightingShader.Program, "view");
GLint projLoc = glGetUniformLocation(lightingShader.Program, "projection");
// Pass the matrices to the shader
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Bind diffuse map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);
// Draw the container (using container's vertex attributes)
glm::mat4 model;
glBindVertexArray(containerVAO);
for (GLuint i = 0; i < 10; i++)
{
model = glm::mat4();
model = glm::translate(model, cubePositions[i]);
GLfloat angle = 20.0f * i;
model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glDrawArrays(GL_TRIANGLES, 0, 36);
}