[OpenGL]多光源

前面学习了投光物,但是实际上一个场景通常会有很多个光源。这节就来搞一搞吧。

在多个光源下,FragmentShader通常如下:

void main()
{
    vec3 result = 计算定向光();

    result += 计算点光();

    result += 计算聚光();

    fragColor = vec4(result, 1.0);
}

 

GLSL和C语言一样,可以有很多个函数。所以可以为不同类型的光各写一个函数。如下

struct DirectionalLight
{
    vec3 direction; // 定向光不需要位置,只需要方向

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform DirectionalLight dirLight;

struct PointLight
{
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant; // 常数项
    float linear;   // 一次项
    float quadratic; // 平方项
};
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];

struct SpotLight
{
    vec3 position;
    vec3 direction;
    float cutOff; // 内边界
    float outterCutOff; // 外边界

    float constant; // 常数项
    float linear;   // 一次项
    float quadratic; // 平方项

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform SpotLight spotLight;
// 计算定向光
vec3 CalcDirLight(DirectionalLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    // 计算漫反射
    float diff = max(dot(normal, lightDir), 0.0);
    // 计算镜面反射
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);

    vec3 diffuseColor = vec3(texture(material.diffuse, v2_TexCoord));
    vec3 ambient = light.ambient * diffuseColor;
    vec3 diffuse = light.diffuse * (diff * diffuseColor);
    vec3 specular = light.specular * spec * vec3(texture(material.specular, v2_TexCoord));

    return (ambient + diffuse + specular);
}
// 计算点光
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - v3_WorldPos);

    float diff = max(dot(normal, lightDir), 0.0);
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);

    vec3 diffuseColor = vec3(texture(material.diffuse, v2_TexCoord));
    vec3 ambient = light.ambient * diffuseColor;
    vec3 diffuse = light.diffuse * (diff * diffuseColor);
    vec3 specular = light.specular * spec * vec3(texture(material.specular, v2_TexCoord));

    float distanceFromLightToFrag = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distanceFromLightToFrag +
        light.quadratic * (distanceFromLightToFrag * distanceFromLightToFrag));

   return ((ambient + diffuse + specular) * attenuation);
}

// 计算聚光
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - v3_WorldPos);

    float diff = max(dot(normal, lightDir), 0.0);
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);

    vec3 diffuseColor = vec3(texture(material.diffuse, v2_TexCoord));
    vec3 ambient = light.ambient * diffuseColor;
    vec3 diffuse = light.diffuse * (diff * diffuseColor);
    vec3 specular = light.specular * spec * vec3(texture(material.specular, v2_TexCoord));

    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outterCutOff;
    float intensity = clamp((theta - light.outterCutOff) / epsilon, 0.0, 1.0);

    float distanceFromLightToFrag = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distanceFromLightToFrag +
        light.quadratic * (distanceFromLightToFrag * distanceFromLightToFrag));

    // 不对环境光做操作,要保持物体不是全黑的
    return (ambient + intensity * attenuation * (diffuse + specular));
}

 

最后再main中调用即可,由于我的三个函数写在main的下方,所以需要在main的上部声明函数

vec3 CalcDirLight(DirectionalLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
void main()
{
    vec3 N = normalize(v3_Normal);
    vec3 viewDir = normalize(u_ViewPos - v3_WorldPos);

    // 定向光
    vec3 result = CalcDirLight(dirLight, N, viewDir);

    // 点光
    for (int i = 0; i < NR_POINT_LIGHTS; i++)
        result += CalcPointLight(pointLights[i], N, v3_WorldPos, viewDir);

    // 聚光
    result += CalcSpotLight(spotLight, N, v3_WorldPos, viewDir);

    fragColor = vec4(result, 1.0);
}

剩下的就是在代码中设置uniform的值就行啦。

// setup()
m_DirLight.direction = glm::vec3(-0.2f, -1.0f, -0.3f);
m_DirLight.ambient = glm::vec3(0.05f, 0.05f, 0.05f);
m_DirLight.diffuse = glm::vec3(0.3f, 0.3f, 0.3f);
m_DirLight.specular = glm::vec3(1.0f, 1.0f, 1.0f);

for (int i = 0; i < 4; i++)
{
    m_PointsLight[i].position = pointLightPositions[i];
    m_PointsLight[i].ambient = glm::vec3(0.05f, 0.05f, 0.05f);
    m_PointsLight[i].diffuse = glm::vec3(0.8f, 0.8f, 0.8f);
    m_PointsLight[i].specular = glm::vec3(0.9f, 0.9f, 0.9f);
    m_PointsLight[i].constant = 1.0f;
    m_PointsLight[i].linear = 0.09f;
    m_PointsLight[i].quadratic = 0.032f; // 50m衰减
}

m_SpotLight.ambient = glm::vec3(0.0f);
m_SpotLight.diffuse = glm::vec3(0.68f, 0.48f, 0.56f);
m_SpotLight.specular = glm::vec3(1.0f);
m_SpotLight.cutOff = glm::cos(glm::radians(12.5f));
m_SpotLight.outterCutOff = glm::cos(glm::radians(15.0f));
m_SpotLight.constant = 1.0f;
m_SpotLight.linear = 0.027f;
m_SpotLight.quadratic = 0.0028f; // 160m衰减

// render()
m_Shader[0]->Bind();
m_Shader[0]->SetVec3("dirLight.direction", m_DirLight.direction);
m_Shader[0]->SetVec3("dirLight.ambient", m_DirLight.ambient);
m_Shader[0]->SetVec3("dirLight.diffuse", m_DirLight.diffuse);
m_Shader[0]->SetVec3("dirLight.specular", m_DirLight.specular);

for (int i = 0; i < 4; i++)
{
    m_Shader[0]->SetVec3(String::Format("pointLights[%d].position", i), m_PointsLight[i].position);
    m_Shader[0]->SetVec3(String::Format("pointLights[%d].ambient", i), m_PointsLight[i].ambient);
    m_Shader[0]->SetVec3(String::Format("pointLights[%d].diffuse", i), m_PointsLight[i].diffuse);
    m_Shader[0]->SetVec3(String::Format("pointLights[%d].specular", i), m_PointsLight[i].specular);
    m_Shader[0]->SetFloat(String::Format("pointLights[%d].constant", i), m_PointsLight[i].constant);
    m_Shader[0]->SetFloat(String::Format("pointLights[%d].linear", i), m_PointsLight[i].linear);
    m_Shader[0]->SetFloat(String::Format("pointLights[%d].quadratic", i), m_PointsLight[i].quadratic);
}

m_SpotLight.position = m_Camera->GetEye();
m_SpotLight.direction = m_Camera->GetAt();
m_Shader[0]->SetVec3("spotLight.position", m_SpotLight.position);
m_Shader[0]->SetVec3("spotLight.direction", m_SpotLight.direction);
m_Shader[0]->SetVec3("spotLight.ambient", m_SpotLight.ambient);
m_Shader[0]->SetVec3("spotLight.diffuse", m_SpotLight.diffuse);
m_Shader[0]->SetVec3("spotLight.specular", m_SpotLight.specular);
m_Shader[0]->SetFloat("spotLight.cutOff", m_SpotLight.cutOff);
m_Shader[0]->SetFloat("spotLight.outterCutOff", m_SpotLight.outterCutOff);
m_Shader[0]->SetFloat("spotLight.constant", m_SpotLight.constant);
m_Shader[0]->SetFloat("spotLight.linear", m_SpotLight.linear);
m_Shader[0]->SetFloat("spotLight.quadratic", m_SpotLight.quadratic);

多光源

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像