跳到主要内容

6 - 多光源

本文将会对各种各样的光源进行一个综合使用,将会创建一个包含六个光源的场景:一个类似于太阳的定向光、四个分散在场景中的点光源,以及一个手电筒聚光。

多光源

为了在场景中使用多个光源,可以将每类光源的计算分别分装到GLSL函数中,最后把它们加起来。

定向光

定向光结构体如下:

struct DriectionalLight
{
// 方向:光源 -> 着色点
vec3 direction;

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

然后定义一个计算定向光的函数:

vec3 calcDirLight(DriectionalLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(-light.direction);
vec3 half = normalize(viewDir + lightDir);

// 环境光
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;

// 漫反射
vec3 diffuse = light.diffuse * texture(material.diffuse, TexCoords).rgb * max(0.0, dot(normal, lightDir));

// 高光项
vec3 specular = light.specular * texture(material.specular, TexCoords).rgb * pow(max(0.0, dot(normal, half)), material.shininess);

return (ambient + diffuse + specular);
}

点光源

点光源结构体如下:

struct PointLight
{
vec3 position;

float constant;
float linear;
float quadratic;

vec3 ambient;
vec3 diffuse;
vec3 specular;
};
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];

然后定义一个计算点光源的函数:

vec3 calcPointLight(PointLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - FragPos);
vec3 half = normalize(viewDir + lightDir);

// 环境光
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;

// 漫反射
vec3 diffuse = light.diffuse * texture(material.diffuse, TexCoords).rgb * max(0.0, dot(normal, lightDir));

// 镜面光
vec3 specular = light.specular * texture(material.specular, TexCoords).rgb * pow(max(0.0, dot(normal, half)), material.shininess);

// 衰减计算
float distance = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));

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

聚光

聚光结构体如下:

struct SpotLight
{
vec3 position;
vec3 direction;
float cutOff;
float outerCutOff;

vec3 ambient;
vec3 diffuse;
vec3 specular;

float constant;
float linear;
float quadratic;
};
uniform SpotLight spotLight;

然后定义一个计算聚光的函数:

vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - FragPos);
vec3 half = normalize(viewDir + lightDir);

vec3 ambient, diffuse, specular;

ambient = texture(texture_diffuse1, TexCoords).rgb * light.ambient;
diffuse = texture(texture_diffuse1, TexCoords).rgb * light.diffuse * max(0.0, dot(normal, lightDir));
specular = texture(texture_specular1, TexCoords).rgb * light.specular * pow(max(0.0, dot(normal, half)), 32.0);

// 聚光相关计算
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);

// 距离衰减
float dist = length(light.position - FragPos);
float attenuation = 1.0 / (1.0 + light.linear * dist + light.quadratic * (dist * dist));

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

综合

接下来将它们综合到一起,首先修改片段着色器的main函数:

void main()
{
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);

// 定向光
vec3 result = calcDirLight(dirLight, norm, viewDir);
// 点光源
for (int i = 0; i < NR_POINT_LIGHTS; ++i)
result += calcPointLight(pointLights[i], norm, viewDir);
// 聚光
result += calcSpotLight(spotLight, norm, viewDir);

FragColor = vec4(result, 1.0);
}

然后添加数据,这里选择Factory数据,结果如下:

参考资料