正文之前补充两个重要知识点:
GLM库中使用如glm::translate(amat, xxx)的写法时,进行的是连续右乘操作!
lightModel = glm::translate(lightModel, lightPos);
lightModel = glm::scale(lightModel, glm::vec3(0.2f));
//lightModel = glm::translate(glm::mat4(1.0f), lightPos) * glm::scale(glm::mat4(1.0f), glm::vec3(0.2f)) * lightModel;
以上两个写法才是等价的。
shader程序中禁止修改in变量!
尽量通过有效的命名来区别开变量。
基础知识
Phone lighting model(有的也叫shading)
ambient、diffuse、specular,三者计算结果相加得到最终结果。
需要利用到shading point坐标(这里会叫做片段坐标)、lightDir、viewDir。
比较简陋的模型要使用物体颜色和光源颜色。
在比较完备的Phone光照模型中,三种光照会分别具有不同的颜色和强度,都需要传入shader。
物体颜色和光照颜色得到的反射颜色,在制作不同材质时将会是不相同的,但是通常会简单使用乘法进行颜色混合。
代码和解释
/// <summary>
/// 实现render loop。
/// </summary>
CameraState::lastTime = 0.0f;
CameraState::lastX = cwindow.width / 2.0;
CameraState::lastY = cwindow.height / 2.0;
CameraState::yaw = -90.0f;
CameraState::pitch = 0.0f;
CameraState::cameraPos = glm::vec3(0.0f, 0.0f, 10.0f);
CameraState::cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
CameraState::cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetFramebufferSizeCallback(window,
[](GLFWwindow* window, int width, int height) {glViewport(0, 0, width, height); });
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glm::vec3 objectColor = glm::vec3(0.5f, 0.5f, 0.0f);
glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
glm::vec3 lightPos = glm::vec3(2.0f, 1.5f, -1.0f);
glm::mat4 model = glm::mat4(1.0f);
glm::mat4 lightModel = glm::mat4(1.0f);
lightModel = glm::translate(lightModel, lightPos);
lightModel = glm::scale(lightModel, glm::vec3(0.2f));
//lightModel = glm::translate(glm::mat4(1.0f), lightPos) * glm::scale(glm::mat4(1.0f), glm::vec3(0.2f)) * lightModel;
glm::mat4 view = glm::mat4(1.0f);
view = glm::lookAt(CameraState::cameraPos, CameraState::cameraPos + CameraState::cameraFront, CameraState::cameraUp);
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), (float)cwindow.width / cwindow.height, 0.1f, 100.0f);
shader.use();
glBindVertexArray(boxVAO);
shader.setVec3("objectColor", glm::value_ptr(objectColor));
shader.setVec3("lightColor", glm::value_ptr(lightColor));
shader.setVec3("lightPos", glm::value_ptr(lightPos));
shader.setVec3("viewPos", glm::value_ptr(CameraState::cameraPos));
shader.setMat4("model", glm::value_ptr(model));
shader.setMat4("view", glm::value_ptr(view));
shader.setMat4("projection", glm::value_ptr(projection));
glDrawArrays(GL_TRIANGLES, 0, 36);
lightShader.use();
glBindVertexArray(lightVAO);
lightShader.setVec3("lightColor", glm::value_ptr(lightColor));
lightShader.setMat4("model", glm::value_ptr(lightModel));
lightShader.setMat4("view", glm::value_ptr(view));
lightShader.setMat4("projection", glm::value_ptr(projection));
glDrawArrays(GL_TRIANGLES, 0, 36);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSwapBuffers(window);
glfwPollEvents();
}
可以发现我们链接了新的程序用来绘制光源物体。各种参数通常是使用uniform变量传入程序的。
//VertexShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 fragPos;
out vec3 normal;
void main()
{
gl_Position = projection *view *model* vec4(aPos, 1.0);
fragPos = vec3(model* vec4(aPos, 1.0f));
normal = aNormal;
}
//FragmentShader.glsl
#version 330 core
in vec3 fragPos;
in vec3 normal;
out vec4 fragColor;
uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
float ambientStrength = 0.1f;
float diffuseStrength = 0.7f;
float specularStrength = 0.5f;
vec3 norm = normalize(normal);
vec3 lightDir = normalize(lightPos - fragPos);
vec3 viewDir = normalize(viewPos - fragPos);
float diffuseCos = max(dot(lightDir, norm), 0.0f);
float specularCos = max(dot(reflect(-lightDir, norm), viewDir), 0.0f);
float ambient = ambientStrength;
float diffuse = diffuseStrength * diffuseCos;
float specular = specularStrength * pow(specularCos, 32);
fragColor = vec4((ambient + diffuse + specular) * lightColor * objectColor, 1.0f);
}
(各部分光照的计算表达式不再打一次了,除了数学问题之外没什么问题,但是忘记截图了所以没图)
使用GLSL结构体表示材质
物体颜色和光照颜色得到的反射颜色,在制作不同材质时将会是不相同的。
使用了“材质”的语言概念,将会改变光照模型的计算方式。
在上面的shader中,其实三部分是同色的,只是强度不同。(每个片段的颜色都是lightColor*objectColor乘以系数)
使用了材质的shader,其各部分颜色为partStrength*lightColor*partColor再乘其他因式,不同部分因式不同。
这能够使三部分各自有各自颜色,形成更丰富的光照效果。
#version 330 core
struct Material{
vec3 ambientCS;
vec3 diffuseCS;
vec3 specularCS;
float shininess;
};
in vec3 FragPos;
in vec3 Normal;
out vec4 FragColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform Material material;
void main()
{
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
vec3 viewDir = normalize(viewPos - FragPos);
// Ambient
vec3 ambient = material.ambientCS;
// Diffuse
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = material.diffuseCS * diff;
// Specular
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = material.specularCS * spec;
// Combine
FragColor = vec4(ambient + diffuse + specular, 1.0);
}
renderloop如果要对shader中的结构体对象传值,需要把对象名像名字空间一样使用。
shader.setFloat("material.shininess", specularShininess);

用同样的方式使用光源结构
虽然这能使自定义程度进一步提高,但是光源和物体都对lighting结果有影响时,其结果不能简单预测。
使用物体颜色作为材质高光、光源颜色作为光源高光部分,结果用乘法计算。

都用光源颜色计算高光部分。

在乘法混合颜色的模型中,通常以一个颜色作为原本的颜色,而另一个颜色视为对原颜色的滤波。
(下一部分的光照模型中,纹理颜色将作为原颜色被光源颜色滤波(ambient、diffuse),而高光部分仅由光源颜色计算不被滤波)
comment 评论区
star_outline 咱快来抢个沙发吧!