【OpenGL】LearnOpenGL学习笔记11 - 多光源
上接:https://blog.csdn.net/weixin_44506615/article/details/150446490?spm=1001.2014.3001.5501
完整代码:https://gitee.com/Duo1J/learn-open-gl
在上一节中,我们都是用单一光源来进行绘制的,接下来我们将三种光源都使用起来
直接上代码
1. 平行光
这里我们用到了GLSL的函数,其定义和使用方式和C一样
我们在CalcDirectionalLight
中计算了平行光的各分量,并加起来返回
// 平行光
struct DirLight {vec3 ambient;vec3 diffuse;vec3 specular;vec3 direction;
};uniform DirLight dirLight;// 计算平行光
vec3 CalcDirectionalLight(DirLight light, vec3 normal, vec3 viewDir)
{vec3 lightDir = normalize(-light.direction);// 环境光vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));// 漫反射float diff = max(dot(normal, lightDir), 0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));// 镜面反射vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(reflectDir, viewDir), 0), material.shininess);vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));return ambient + diffuse + specular;
}
2. 点光源
我们将会使用4个点光源,使用uniform声明数组
// 点光源
struct PointLight {vec3 ambient;vec3 diffuse;vec3 specular;vec3 position;float constant;float linear;float quadratic;
};#define POINT_LIGHT_NUM 4
uniform PointLight pointLight[POINT_LIGHT_NUM];// 计算点光源
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 viewDir, vec3 fragPos)
{vec3 lightDir = normalize(light.position - fragPos);// 衰减系数float d = length(light.position - fragPos);float attenuation = 1 / (light.constant + light.linear * d + light.quadratic * d * d);// 环境光vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)) * attenuation;// 漫反射float diff = max(dot(normal, lightDir), 0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)) * attenuation;// 镜面反射vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(reflectDir, viewDir), 0), material.shininess);vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)) * attenuation;return ambient + diffuse + specular;
}
3. 聚光灯
// 聚光灯
struct SpotLight {vec3 ambient;vec3 diffuse;vec3 specular;vec3 position;vec3 direction;float cutOff;float cutOffOuter;float constant;float linear;float quadratic;
};uniform SpotLight spotLight;// 计算聚光灯
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir, vec3 fragPos)
{vec3 lightDir = normalize(light.position - fragPos);// 强度float theta_cos = dot(lightDir, -normalize(light.direction));float intensity = clamp((theta_cos - light.cutOffOuter) / (light.cutOff - light.cutOffOuter), 0, 1);// 衰减float d = length(light.position - FragPos);float attenuation = 1 / (light.constant + light.linear * d + light.quadratic * d * d);// 环境光vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)) * attenuation * intensity;// 漫反射float diff = max(dot(normal, lightDir), 0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)) * attenuation * intensity;// 镜面反射vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(reflectDir, viewDir), 0), material.shininess);vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)) * attenuation * intensity;return ambient + diffuse + specular;
}
4. 合并
void main()
{vec3 normal = normalize(Normal);vec3 viewDir = normalize(viewPos - FragPos);// 平行光vec3 result = CalcDirectionalLight(dirLight, normal, viewDir);// 点光源for (int i = 0; i < POINT_LIGHT_NUM; i++){result += CalcPointLight(pointLight[i], normal, viewDir, FragPos);}// 聚光灯result += CalcSpotLight(spotLight, normal, viewDir, FragPos);FragColor = vec4(result, 1.0f);
}
5. C++ 传递数据
首先声明点光源位置数组
// 点光源位置
glm::vec3 pointLightPositions[] = {glm::vec3(0.7f, 0.2f, 2.0f),glm::vec3(2.3f, -3.3f, -4.0f),glm::vec3(-4.0f, 2.0f, -12.0f),glm::vec3(0.0f, 0.0f, -3.0f)
};
传递平行光数据
shader.SetVec3("dirLight.ambient", glm::vec3(0.05f));
shader.SetVec3("dirLight.diffuse", glm::vec3(0.4f));
shader.SetVec3("dirLight.specular", glm::vec3(0.5f));
shader.SetVec3("dirLight.direction", glm::vec3(-0.2f, -1.0f, -0.3f));
传递点光源数据
for (int i = 0; i < 4; i++)
{std::stringstream ss;shader.SetVec3("pointLight[" + std::to_string(i) + "].ambient", glm::vec3(0.05f));shader.SetVec3("pointLight[" + std::to_string(i) + "].diffuse", glm::vec3(0.8f));shader.SetVec3("pointLight[" + std::to_string(i) + "].specular", glm::vec3(1.0f));shader.SetVec3("pointLight[" + std::to_string(i) + "].position", glm::vec3(pointLightPositions[i]));shader.SetFloat("pointLight[" + std::to_string(i) + "].constant", 1.0f);shader.SetFloat("pointLight[" + std::to_string(i) + "].diffuse", 0.09f);shader.SetFloat("pointLight[" + std::to_string(i) + "].quadratic", 0.032f);
}
传递聚光灯数据
shader.SetVec3("spotLight.ambient", glm::vec3(0));
shader.SetVec3("spotLight.diffuse", glm::vec3(1));
shader.SetVec3("spotLight.specular", glm::vec3(1));shader.SetVec3("spotLight.position", camera.transform.position);
shader.SetVec3("spotLight.direction", camera.transform.front);shader.SetFloat("spotLight.cutOff", glm::cos(glm::radians(12.5f)));
shader.SetFloat("spotLight.cutOffOuter", glm::cos(glm::radians(17.5f)));
shader.SetFloat("spotLight.constant", 1.0f);
shader.SetFloat("spotLight.linear", 0.09f);
shader.SetFloat("spotLight.quadratic", 0.032f);
绘制点光源模型,方便观察
for (int i = 0; i < 4; i++)
{lightShader.Use();glm::mat4 model = glm::mat4(1.0f);model = glm::translate(model, glm::vec3(pointLightPositions[i]));model = glm::scale(model, glm::vec3(0.1f));lightShader.SetMat4("model", model);lightShader.SetMat4("view", view);lightShader.SetMat4("projection", projection);lightShader.SetVec3("objectColor", lightColor);glBindVertexArray(lightVAO);glDrawArrays(GL_TRIANGLES, 0, 36);
}
编译运行,顺利的话可以看见以下图像
平行光 + 点光源 + 聚光灯
只有平行光
平行光 + 点光源
平行光 + 聚光灯
完整代码可在顶部git仓库中找到