冯氏光照模型:
环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。
main.cpp
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "shader.h"
#include "stb_image.h"
#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
#include <gtc/type_ptr.hpp>
#include "Camera.h"
GLfloat vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f
};
Camera* myCamera = new Camera(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, 1.0f, 0.0f));
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
myCamera->mouseCb(xpos, ypos);
};
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
myCamera->scrollCb(xoffset, yoffset);
}
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void processInput(GLFWwindow* window) {
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
myCamera->pressKeyW(deltaTime);
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
myCamera->pressKeyS(deltaTime);
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
myCamera->pressKeyA(deltaTime);
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
myCamera->pressKeyD(deltaTime);
}
}
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);//声明全局变量表示光源在场景的坐标
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "test", nullptr, nullptr);
if (window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
glfwTerminate();
return -1;
}
glViewport(0, 0, 800, 600);
GLuint VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
GLuint VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3* sizeof(GLfloat)));
glEnableVertexAttribArray(1);
GLuint lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// 只需要绑定VBO不用再次设置VBO的数据,因为容器(物体)的VBO数据中已经包含了正确的立方体顶点数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
glm::mat4 model;
glm::mat4 view;
glm::mat4 projection;
glEnable(GL_DEPTH_TEST);
Shader* testShader = new Shader("test.vert", "test.frag");
//glm::vec3 lightColor(1.0f, 1.0f, 1.0f);//定义一个白色光源
//glm::vec3 toyColor(1.0f, 0.5f, 0.31f);//定义一个珊瑚色物体,吸收除珊瑚色以外的所有颜色,反射珊瑚色
//glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);
可以定义物体的颜色为这个物体从一个光源反射各个颜色分量的多少
//glm::vec3 lightColor(0.0f, 1.0f, 0.0f);//定义绿光
//glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
//glm::vec3 result = lightColor * toyColor;
= (0.0f, 0.5f, 0.0f);没有红蓝色光吸收,只吸收了光线中一半的绿色,反射一半的绿光。呈暗绿色
glm::vec3 lightColor(0.33f, 0.42f, 0.18f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);
//保持灯不受其他光照影响一直明亮,创建另外一套着色器。
Shader* lightShader = new Shader("test.vert", "light.frag");
while (!glfwWindowShouldClose(window))
{
processInput(window);
glfwPollEvents();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
testShader->Use();
glUniform3f(glGetUniformLocation(testShader->getProgram(), "objectColor"), 1.0f, 0.5f, 0.31f);// 设置物体颜色珊瑚红
glUniform3f(glGetUniformLocation(testShader->getProgram(), "lightColor"), 1.0f, 1.0f, 1.0f); // 把光源设置为白色
model = glm::mat4();
glUniformMatrix4fv(glGetUniformLocation(testShader->getProgram(), "model"), 1, GL_FALSE, glm::value_ptr(model));
view = myCamera->getViewMat4();
glUniformMatrix4fv(glGetUniformLocation(testShader->getProgram(), "view"), 1, GL_FALSE, glm::value_ptr(view));
projection = glm::perspective(glm::radians(myCamera->getFov()), 800.0f / 600.0f, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(testShader->getProgram(), "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniform3f(glGetUniformLocation(testShader->getProgram(), "lightPos"), lightPos.x, lightPos.y, lightPos.z);
glm::vec3 viewPos = myCamera->getCameraPos();
glUniform3f(glGetUniformLocation(testShader->getProgram(), "viewPos"), viewPos.x, viewPos.y, viewPos.z);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(lightVAO);
//平移灯源位置到lightPos
model = glm::mat4();
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f));
lightShader->Use();
glUniformMatrix4fv(glGetUniformLocation(lightShader->getProgram(), "model"), 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(glGetUniformLocation(lightShader->getProgram(), "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(lightShader->getProgram(), "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// 绘制灯立方体对象
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
顶点着色器
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal; //面的法向量
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 Normal;
out vec3 FragPos; //片段位置
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
FragPos = vec3(model * vec4(position, 1.0f));//顶点位置属性乘以模型矩阵转换到世界空间坐标
Normal = mat3(transpose(inverse(model))) * normal;
//正规矩阵:模型矩阵左上角的逆矩阵的转置矩阵。inverse逆矩阵,transpose转置矩阵
//如果模型矩阵执行了不等比缩放,法向量将不再垂直表面,光照会被扭曲。通关正规矩阵移除对法向量的错误缩放
//实际使用时,在绘制前,最好先cpu算出正规矩阵,再通过uniform传给着色器。着色器使用逆矩阵是一种较大开销
}
片段着色器
#version 330 core
out vec4 color;
uniform vec3 objectColor;
uniform vec3 lightColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos; //光源位置
uniform vec3 viewPos; //观察者位置、摄像机坐标
void main()
{
//漫反射光照
vec3 norm = normalize(Normal); //转为单位向量
vec3 lightDir = normalize(lightPos - FragPos); //片段位置指向光源位置的单位向量
float diff = max(dot(norm, lightDir), 0.0); //计算点乘,作为光对片段的实际散射影响。负数颜色没有意义。
vec3 diffuse = diff * lightColor; //散射光颜色:结果乘光的颜色,得到散射因子。两个向量角度越大,散射因子越小。
//添加环境光照
float ambientStrength = 0.1f; //设置一个很小的常量因子
vec3 ambient = ambientStrength * lightColor;//环境光照颜色
//vec3 result = ambient * objectColor;
//最终显示冯氏光照的第一个阶段,物体非常暗,但并不全黑
//镜面光照
//计算反射向量和视线方向的角度,角度越小,则镜面光作用越大
//观察向量通关观察者世界空间位置和片段的位置来计算
//如果再观察空间计算光照,就使用观察矩阵应用到所有相关的需要变换的向量(包括正规矩阵)。这样观察者位置总是0,0,0,默认就是观察者位置
float specularStrength = 0.5f; //镜面强度
vec3 viewDir = normalize(viewPos - FragPos); //片段位置指向观察位置的单位向量
vec3 reflectDir = reflect(-lightDir, norm); //reflect第一个参数要求光源指向片段位置的向量,第二个参数为方向量
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); //2,4,8,16,32,64,128,256
//32是高光的发光值,物体的发光值越高,反射光的能力越强,散射的越少,高光点越小
vec3 specular = specularStrength * spec * lightColor; //计算镜面亮度分量
//vec3 result = (ambient + diffuse) * objectColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
color = vec4(result, 1.0f);
}
练习三:
顶点着色器
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal; //面的法向量
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 Normal;
out vec3 FragPos; //片段位置
out vec3 LightPos;
uniform vec3 lightPos;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
FragPos = vec3(view * model * vec4(position, 1.0f));
Normal = mat3(transpose(inverse(view * model))) * normal;
LightPos = vec3(view * vec4(lightPos, 1.0f));
}
片段着色器
#version 330 core
out vec4 color;
uniform vec3 objectColor;
uniform vec3 lightColor;
in vec3 Normal;
in vec3 FragPos;
in vec3 LightPos;
void main()
{
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(LightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
float ambientStrength = 0.1f;
vec3 ambient = ambientStrength * lightColor;
float specularStrength = 0.5f;
vec3 viewDir = normalize(- FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
color = vec4(result, 1.0f);
}
练习四:
顶点着色器
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal; //面的法向量
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 lightPos;
uniform vec3 lightColor;
out vec3 LightColor;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
vec3 FragPos = vec3(view * model * vec4(position, 1.0f));
vec3 Normal = mat3(transpose(inverse(view * model))) * normal;
vec3 LightPos = vec3(view * vec4(lightPos, 1.0f));
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(LightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
float ambientStrength = 0.1f;
vec3 ambient = ambientStrength * lightColor;
float specularStrength = 0.5f;
vec3 viewDir = normalize(- FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
LightColor = ambient + diffuse + specular;
}
片段着色器
#version 330 core
out vec4 color;
in vec3 LightColor;
uniform vec3 objectColor;
void main()
{
color = vec4(LightColor * objectColor, 1.0);
}