在现实生活当中,不同的物体对物体的反应和效果是不同的,比如说刚体比陶瓷更具有光泽,有的物体能展现出更大的高光点而一些不能,为了展示不同的物体对于同阳光照的不同反应,OpenGL为我们定义了材质(material)属性去指明不同的表面是什么样子的。
我们可以分别为三个光照分量定义一个材质颜色。环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过对每个分量指定一个颜色,就可以对物体表面的颜色输出进行更细致的控制。另外我们还定义了一个反光度shininess分量。
在片段着色器当中我们定义了一个material的结构体储存这些变量显得更有条理。
#version 330 core
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
接下来我们试着在这个着色器当中完成这个材质系统。
设置材质
根据我们最新设定的材质结构体,我们可以将片段着色器的渲染进行修改,我们从一个uniform变量结构体当中访问他们。
void main()
{
// ambient
vec3 ambient = lightColor * material.ambient;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightColor * (diff * material.diffuse);
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = lightColor * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
然后我们只需要对结构体内部的变量进行适当赋值即可,这个结构体在着色器当中实际上只是充当了uniform变量们的一个命名空间,实际上,只需要像往常uniform变量那样进行赋值即可
lightingShader.setVec3("material.ambient", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
lightingShader.setFloat("material.shininess", 32.0f);
我们将环境光和散射光等设置为一个适当的值。效果如下
光的属性
但是 可以看到上图最终的光照有点太亮了,因为这三个光照分量都将光源的全部光照强度都进行了应用,但是实际上光源对于这三个分量分别也有不同的强度,我们可以分别对这三个分量声明一个光照强度的分量与原本的分量进行相乘。
vec3 ambient = vec3(0.1) * material.ambient;
可以像环境光照一样去操纵散射和镜面反射光照,然后我们可以再声明一个类似的结构体
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
一般环境被设置为一个较低的数值因为我们不想让他在光照的体现中太明显。而散射光照一般被设置为接近于我们想要的最终效果的值。镜面光照一般都是1.0f将光照的全部强度进行反射。
最终的片段着色器
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
struct Material{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
struct Light{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Material material; //replace objectColor
uniform Light light;//replace lightColor
uniform vec3 viewPos;
void main()
{
vec3 ambient = material.ambient * light.ambient;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * (light.diffuse * material.diffuse);
// specular
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 * (material.specular * spec);
vec3 result = (ambient + diffuse + specular);
FragColor = vec4(result, 1.0);
}
我们可以用这个着色器去渲染一个酷炫的颜色不断变化的正方体
while (!glfwWindowShouldClose(window)) {
// per-frame time logic
// --------------------
float currentFrame = static_cast<float>(glfwGetTime());
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// input
// -----
key_callback(window);
// render
// ------
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 将光线随时间进行改变
Object.use();
glm::vec3 lightColor;
lightColor.x = sin(glfwGetTime() * 2.0f);
lightColor.y = sin(glfwGetTime() * 0.7f);
lightColor.z = sin(glfwGetTime() * 1.3f);
glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);
Object.setvec3("viewPos", MainCamera.Position);
Object.setvec3("material.ambient", glm::vec3(1.0f, 0.5f, 0.31f));
Object.setvec3("material.diffuse", glm::vec3(1.0f, 0.5f, 0.31f));
Object.setvec3("material.specular", glm::vec3(0.5f, 0.5f, 0.5f));
Object.setfloat("material.shininess", 32.0f);
Object.setvec3("light.ambient", ambientColor);
Object.setvec3("light.diffuse", diffuseColor); // darken diffuse light a bit
Object.setvec3("light.specular", glm::vec3(1.0f, 1.0f, 1.0f));//根据给出的表对应于真实世界的模拟,光源反光一般设定为1
Object.setvec3("light.position", lightPos);
// view/projection transformations
glm::mat4 projection = glm::perspective(glm::radians(MainCamera.Zoom), (float)800 / (float)600, 0.1f, 100.0f);
glm::mat4 view = MainCamera.GetMatrix();
Object.setmat4("projection", projection);
Object.setmat4("view", view);
// world transformation
glm::mat4 model = glm::mat4(1.0f);
Object.setmat4("model", model);
// render the cube
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// also draw the lamp object
Light.use();
Light.setmat4("projection", projection);
Light.setmat4("view", view);
model = glm::mat4(1.0f);
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube
Light.setmat4("model", model);
glBindVertexArray(LightVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glfwSwapBuffers(window);
glfwPollEvents();
}