我们在前面的教程中已经学习了许多关于OpenGL中光照的知识,其中包括冯氏着色(Phong Shading)、材质(Material)、光照贴图(Lighting Map)以及不同种类的投光物(Light Caster)。在这一节中,我们将结合之前学过的所有知识,创建一个包含六个光源的场景。我们将模拟一个类似太阳的定向光(Directional Light)光源,四个分散在场景中的点光源(Point Light),以及一个手电筒(Flashlight)。
为了在场景中使用多个光源,我们希望将光照计算封装到GLSL函数中。这样做的原因是,每一种光源都需要一种不同的计算方法,而一旦我们想对多个光源进行光照计算时,代码很快就会变得非常复杂。如果我们只在main函数中进行所有的这些计算,代码很快就会变得难以理解。
GLSL中的函数和C函数很相似,它有一个函数名、一个返回值类型,如果函数不是在main函数之前声明的,我们还必须在代码文件顶部声明一个原型。我们对每个光照类型都创建一个不同的函数:定向光、点光源和聚光。
当我们在场景中使用多个光源时,通常使用以下方法:我们需要有一个单独的颜色向量代表片段的输出颜色。对于每一个光源,它对片段的贡献颜色将会加到片段的输出颜色向量上。所以场景中的每个光源都会计算它们各自对片段的影响,并结合为一个最终的输出颜色。大体的结构会像是这样:
out vec4 FragColor;
void main()
{
// 定义一个输出颜色值
vec3 output;
// 将定向光的贡献加到输出中
output += someFunctionToCalculateDirectionalLight();
// 对所有的点光源也做相同的事情
for(int i = 0; i < nr_of_point_lights; i++)
output += someFunctionToCalculatePointLight();
// 也加上其它的光源(比如聚光)
output += someFunctionToCalculateSpotLight();
FragColor = vec4(output, 1.0);
}
定向光
意思是我们可以单独定义一个定向光的结构体,然后定向光的函数,如果场景需要定向光,则加上这个定向光就可以了。
定义定向光的函数和结构体。
定向光主要就是光线的相对物体上每个点的向量方向都是一样的,所以直接取反
#version 330 core
in vec3 to_fragment_normal;
in vec2 to_fragment_texCoord;
in vec3 to_fragment_POS;
uniform vec3 cameraPOS;
out vec4 FragColor;
struct Material{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
uniform Material material;
struct DIRECTLIGHT{
vec3 light_position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform DIRECTLIGHT direcLight;
vec3 CalDirecLight(DIRECTLIGHT _direcLight,vec3 view_position,vec3 normal){//定向光
vec3 res_direc_light;
vec3 lightVetor=normalize(-_direcLight.light_position);
vec3 ambient_light=vec3(texture(material.diffuse,to_fragment_texCoord))*_direcLight.ambient;//忽略掉alpha的变量
vec3 diffuse_light=vec3(texture(material.diffuse,to_fragment_texCoord))*max(dot(lightVetor,normal),0)*_direcLight.diffuse;
vec3 viewVector=view_position-to_fragment_POS;
vec3 h=normalize(viewVector+lightVetor);
vec3 specular_light=pow(max(dot(h,normal),0),material.shininess)*_direcLight.specular*vec3(texture(material.specular,to_fragment_texCoord));
res_direc_light+=spe