本节将在片段着色器中应用漫反射
文章目录
一些概念
漫反射
概述: 描述的是粗糙表面对光的反射,反射的光线相关各个方向均匀分布,与视角无光
特点:
- 亮度取决于光源的方向和物体表面法向量之间的夹角
- 适合模拟不光滑的表面,例如木材、纸张等
- 视角变化不会影响光的强度
公式(朗伯余弦定律):
I d = k d ⋅ I l i g h t ⋅ m a x ( 0 , L ⋅ N ) I_d = k_d \cdot I_{light} \cdot max(0, L \cdot N) Id=kd⋅Ilight⋅max(0,L⋅N)
- I d I_d Id:物体表面的漫反射亮度
- k d k_d kd:漫反射的反射系数(0到1之间)
- I l i g h t I_{light} Ilight:光源强度
- L:指向光源的单位向量
- N:表面的法向量
实战
简介
怎么在vscode上使用cmake构建项目,具体可以看这篇Windows上如何使用CMake构建项目 - 凌云行者的博客
目的: 使用环境光
- 编译工具链:使用msys2安装的mingw-gcc
- 依赖项:glfw3:x64-mingw-static,glad:x64-mingw-static(通过vcpkg安装)
源码: OpenGL-Learn-Program/008-ambient-light at main · 1037827920/OpenGL-Learn-Program
dependencies
主要改动shader.vs和shader.fs,其他文件看这里
shadervs
立方体的顶点着色器源码:
vs
#version 330 core
layout (location = 0) in vec3 vPos;
layout (location = 1) in vec2 vTexCoord;
layout (location = 2) in vec3 vNormal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec2 TexCoord;
out vec3 Normal;
out vec3 FragPos;
void main()
{
gl_Position = projection * view * model * vec4(vPos, 1.0f);
TexCoord = vTexCoord;
Normal = mat3(transpose(inverse(model))) * vNormal;
FragPos = vec3(model * vec4(vPos, 1.0f));
}
shader.fs
fs
#version 330 core
in vec2 TexCoord;
// 表示片段的法向量,用于确定片段相对于光源的角度
in vec3 Normal;
// 表示片段在世界空间中的位置,用于确定片段相对于光源的距离
in vec3 FragPos;
// 第一个纹理
uniform sampler2D texture0;
// 第二个纹理
uniform sampler2D texture1;
// 混合比例
uniform float blendRatio;
// 立方体本身的环境光 光源颜色
uniform vec3 lightColor;
// 点光源位置
uniform vec3 lightPos;
out vec4 FragColor;
void main()
{
// 环境光强度
float ambientStrength = 0.2;
// 计算环境光分量
vec3 ambient = ambientStrength * lightColor;
// 标准化法向量
vec3 norm = normalize(Normal);
// 计算片段位置和光源位置之间的方向向量
vec3 lightDir = normalize(lightPos - FragPos);
// 计算光源到片段的距离
float distance = length(lightPos - FragPos);
// 计算衰减
float attenuation = 1.0 / (distance * distance);
// 计算漫反射分量
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor * attenuation;
// 最终颜色结果
vec3 lighting = ambient + diffuse;
// 将两个纹理混合并乘以环境光分量
FragColor = mix(texture(texture0, TexCoord), texture(texture1, TexCoord), blendRatio) * vec4(lighting, 1.0);
}
顶点着色器源码的输出会作为片段着色器源码的输入
utils
修改的代码文件只有和Cube.cpp,其他可以看这里
Cube.cpp
cpp
#include "Cube.h"
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
Cube::Cube(GLFWWindowFactory* window) : window(window) {
this->shader = Shader("shader.vs", "shader.fs");
this->lightShader = Shader("lightShader.vs", "lightShader.fs");
// 加载纹理
loadTexture();
// 设置顶点数据
setupVertices();
}
Cube::~Cube() {
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
}
/// Public
void Cube::draw() {
// 存储光源的位置和强度
vector<float> lightPos = { 1.0f, 0.5f, -1.55f, 1.0f };
// 使用立方体的着色器
shader.use();
// 将立方体的VAO绑定到上下文
glBindVertexArray(this->VAO);
// 设置立方体模型矩阵
auto model = glm::mat4(1.0f);
// 设置平移矩阵
model = glm::translate(model, glm::vec3(0.0f, 0.0f, -3.0f));
// 设置旋转矩阵
model = glm::rotate(model, glm::radians(20.0f), glm::vec3(1.0f, 0.0f, 0.0f));
// 将uniform变量传递给着色器
shader.setMat4("model", model);
shader.setMat4("view", this->window->getViewMatrix());
shader.setMat4("projection", this->window->getProjectionMatrix());
shader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
shader.setVec3("lightPos", lightPos[0], lightPos[1], lightPos[2]);
shader.setFloat("blendRatio", 0.5f);
// 绘制立方体
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
// 使用光源的着色器
lightShader.use();
// 光源的VAO仍然使用立方体的VAO
// 设置模型矩阵
model = glm::mat4(1.0f);
// 设置平移矩阵
model = glm::translate(model, glm::vec3(lightPos[0], lightPos[1], lightPos[2]));
// 设置缩放矩阵
model = glm::scale(model, glm::vec3(0.2f));
// 将uniform变量传递给着色器
lightShader.setMat4("model", model);
lightShader.setMat4("view", this->window->getViewMatrix());
lightShader.setMat4("projection", this->window->getProjectionMatrix());
lightShader.setVec3("lightColor", glm::vec3(1.0f, 1.0f, 1.0f));
// 绘制光源
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}
/// Private
// 设置顶点数据
void Cube::setupVertices() {
float vertices[] = {
// Front
-0.75f, -0.75f,0.0f, 0.0f, 0.0f, 0.0f, 0.0f,1.0f, // Bottom-left vertex
0.75f, -0.75f,0.0f, 1.0f, 0.0f, 0.0f, 0.0f,1.0f, // Bottom-right vertex
-0.75f, 0.75f,0.0f, 0.0f, 1.0f, 0.0f, 0.0f,1.0f, // Top-left vertex
0.75f, 0.75f,0.0f, 1.0f, 1.0f, 0.0f, 0.0f,1.0f, // Top-right vertex
// Back
0.75f, -0.75f, -1.5f, 0.0f, 0.0f, 0.0f, 0.0f,-1.0f,// Bottom-left vertex
-0.75f, -0.75f, -1.5f, 1.0f, 0.0f, 0.0f, 0.0f,-1.0f, // Bottom-right vertex
0.75f, 0.75f, -1.5f, 0.0f, 1.0f, 0.0f, 0.0f,-1.0f,// Top-left vertex
-0.75f, 0.75f, -1.5f, 1.0f, 1.0f, 0.0f, 0.0f,-1.0f, // Top-right vertex
// Left
-0.75f, -0.75f, -1.5f, 0.0f, 0.0f, -1.0f,0.0f,0.0f, // Bottom-left vertex
-0.75f, -0.75f, 0.0f, 1.0f, 0.0f, -1.0f,0.0f,0.0f, // Bottom-right vertex
-0.75f, 0.75f, -1.5f, 0.0f, 1.0f, -1.0f,0.0f,0.0f, // Top-left vertex
-0.75f, 0.75f, 0.0f, 1.0f, 1.0f, -1.0f,0.0f,0.0f, // Top-right vertex
// Right
0.75f, -0.75f, 0.0f, 0.0f, 0.0f, 1.0f,0.0f,0.0f, // Bottom-left vertex
0.75f, -0.75f, -1.5f, 1.0f, 0.0f,1.0f,0.0f,0.0f, // Bottom-right vertex
0.75f, 0.75f, 0.0f, 0.0f, 1.0f, 1.0f,0.0f,0.0f, // Top-left vertex
0.75f, 0.75f, -1.5f, 1.0f, 1.0f, 1.0f,0.0f,0.0f, // Top-right vertex
// Top
-0.75f, 0.75f, 0.0f, 0.0f, 0.0f, 0.0f,1.0f,0.0f,// Bottom-left vertex
0.75f, 0.75f, 0.0f, 1.0f, 0.0f, 0.0f,1.0f,0.0f, // Bottom-right vertex
-0.75f, 0.75f, -1.5f, 0.0f, 1.0f, 0.0f,1.0f,0.0f, // Top-left vertex
0.75f, 0.75f, -1.5f, 1.0f, 1.0f, 0.0f,1.0f,0.0f, // Top-right vertex
// Bottom
-0.75f, -0.75f, -1.5f, 0.0f, 0.0f, 0.0f,-1.0f,0.0f, // Bottom-left vertex
0.75f, -0.75f, -1.5f, 1.0f, 0.0f, 0.0f,-1.0f,0.0f, // Bottom-right vertex
-0.75f, -0.75f, 0.0f, 0.0f, 1.0f, 0.0f,-1.0f,0.0f, // Top-left vertex
0.75f, -0.75f, 0.0f, 1.0f, 1.0f , 0.0f,-1.0f,0.0f,// Top-right vertex
};
// 索引数据
int indices[] = {
0, 1, 2,
2, 1, 3,
4, 5, 6,
6, 5, 7,
8, 9, 10,
10, 9, 11,
12, 13, 14,
14, 13, 15,
16, 17, 18,
18, 17, 19,
20, 21, 22,
22, 21, 23
};
// 创建VAO, VBO, EBO
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
// 绑定VAO
glBindVertexArray(VAO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 将顶点数据复制到VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 绑定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// 将索引数据复制到EBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 设置顶点位置属性(这里跟顶点着色器源码强相关,每个属性有多少个元素都是看这个顶点着色器源码是怎么写的)
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 纹理位置属性
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// 法向量属性
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(5 * sizeof(float)));
glEnableVertexAttribArray(2);
// 恢复上下人默认的VAO
glBindVertexArray(0);
}
// 加载纹理
void Cube::loadTexture() {
vector<string> paths = { "teenager.png", "tex.png" };
this->texture.resize(paths.size());
// 绑定纹理
for (int i = 0; i < paths.size(); i++) {
bindTexture(this->texture[i], paths[i].c_str());
}
// 激活纹理
glActiveTexture(GL_TEXTURE0);
// 将纹理绑定到上下文
glBindTexture(GL_TEXTURE_2D, this->texture[0]);
// 激活纹理
glActiveTexture(GL_TEXTURE1);
// 将纹理绑定到上下文
glBindTexture(GL_TEXTURE_2D, this->texture[1]);
// 使用立方体着色器
this->shader.use();
// 为着色器设置uniform变量
this->shader.setInt("texture0", 0);
this->shader.setInt("texture1", 1);
}
// 绑定纹理
void Cube::bindTexture(GLuint& textureId, const char* path) {
// 生成纹理
glGenTextures(1, &textureId);
// 绑定上下文的纹理
glBindTexture(GL_TEXTURE_2D, textureId);
// 设置纹理环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 设置纹理过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载和生成纹理
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0);
if (data) {
GLenum format;
if (nrChannels == 4)
format = GL_RGBA;
else if (nrChannels == 3)
format = GL_RGB;
else
format = GL_RED;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
}
main.cpp
cpp
#include "utils/Cube.h"
#include "utils/windowFactory.h"
int main() {
// 创建一个窗口Factory对象
GLFWWindowFactory myWindow(800, 600, "This is Title");
// 创建一个矩形模型对象
Cube cube(&myWindow);
// 运行窗口,传入一个lambda表达式,用于自定义渲染逻辑
myWindow.run([&]() {
// 绘制矩形
cube.draw();
});
return 0;
}
CMakeLists.txt
cmake
# 设置CMake的最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(Ambient)
# vcpkg集成, 这里要换成你自己的vcpkg工具链文件和共享库路径
set(VCPKG_ROOT D:/software6/vcpkg/)
set(CMAKE_TOOLCHAIN_FILE ${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
set(CMAKE_PREFIX_PATH ${VCPKG_ROOT}/installed/x64-mingw-static/share)
# 查找所需的包
find_package(glad CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(assimp CONFIG REQUIRED)
find_package(yaml-cpp CONFIG REQUIRED)
# 搜索并收集utils文件夹下的所有源文件
file(GLOB UTILS "utils/*.cpp", "utils/*.h")
# 添加可执行文件(还要加入utils文件夹下的源文件)
add_executable(Ambient main.cpp ${UTILS})
# 链接所需的库
target_link_libraries(Ambient PRIVATE glad::glad glfw glm::glm assimp::assimp yaml-cpp::yaml-cpp)
# 检查项目是否有dependeicies目录,如果存在,则在使用add_custom_command命令在构建后将dependencies目录中的文件复制到项目的输出目录
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies")
if(EXISTS ${SOURCE_DIR})
add_custom_command(TARGET Ambient POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${SOURCE_DIR} $<TARGET_FILE_DIR:Ambient>)
endif()