上接:https://blog.csdn.net/weixin_44506615/article/details/150411373?spm=1001.2014.3001.5501
完整代码:https://gitee.com/Duo1J/learn-open-gl
一、材质
材质就是用来描述一个物体"应该是什么样的",它具备什么视觉特征、物理特性的一系列参数和纹理的集合
在这之前,我们使用了objectColor变量来定义我们的立方体的颜色,接下来我们将其抽离出来,封装成一个材质结构体,为光照模型的每一个分量提供一个颜色,以便之后我们应用贴图来进行更细颗粒度的控制
直接上代码
FragmentShader.fs
#version 330 core
// 物体材质结构体
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
// 物体受光照强度结构体
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
in vec3 FragPos;
in vec3 Normal;
out vec4 FragColor;
uniform vec3 lightColor;
uniform vec3 viewPos;
uniform Material material;
uniform Light light;
void main()
{
// 环境光
vec3 ambient = lightColor * material.ambient * light.ambient;
// 漫反射
vec3 normal = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
vec3 diffuse = max(dot(normal, lightDir), 0.0) * lightColor * material.diffuse * light.diffuse;
// 镜面反射
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 specular = pow(max(dot(viewDir, reflectDir), 0), material.shininess) * lightColor * material.specular * light.specular;
FragColor = vec4(ambient + diffuse + specular, 1.0f);
}
Main.cpp
// 传入材质参数
shader.SetVec3("material.ambient", glm::vec3(1.0f, 0.5f, 0.31f));
shader.SetVec3("material.diffuse", glm::vec3(1.0f, 0.5f, 0.31f));
shader.SetVec3("material.specular", glm::vec3(0.5f, 0.5f, 0.5f));
shader.SetFloat("material.shininess", 32.0f);
shader.SetVec3("light.ambient", glm::vec3(0.2f, 0.2f, 0.2f));
shader.SetVec3("light.diffuse", glm::vec3(0.5f, 0.5f, 0.5f));
shader.SetVec3("light.specular", glm::vec3(1.0f, 1.0f, 1.0f));
shader.SetVec3("light.position", lightPos);
编译运行,顺利的话可以看见以下图像
二、光照贴图
现在我们的立方体看起来还是一个纯色的塑料小方块,如果我们想要为我们的立方体赋予更多的细节,比如让我们的立方体变成一个木条箱,那么我们就需要用到光照贴图
1. 漫反射贴图 (Diffuse Map)
首先我们需要一张贴图资产,可以右键保存以下图片或是在顶部的git仓库中的Resource目录找到所有需要用到的资产
我们将图片命名为T_Box_Diffuse.png
接下来修改我们的Material结构体以及片段着色器
FragmentShader.fs
struct Material {
// 将ambient和diffuse颜色改为sampler2D采样器
sampler2D diffuse;
vec3 specular;
float shininess;
};
in vec2 TexCoords;
// ...
// 修改环境光和漫反射计算
void main()
{
// 环境光
vec3 ambient = lightColor * vec3(texture(material.diffuse, TexCoords)) * light.ambient;
// 漫反射
vec3 normal = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
vec3 diffuse = max(dot(normal, lightDir), 0.0) * lightColor * vec3(texture(material.diffuse, TexCoords)) * light.diffuse;
// ...
}
在片段着色器中,我们又将用到TexCoords,也就是UV坐标,所以我们需要修改下顶点数组、顶点属性以及顶点着色器
VertexShader.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
TexCoords = aTexCoords;
}
Main.cpp
float vertices[] = {
// positions // normals // texture coords
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
int step = 8, curStep = 0;
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)curStep);
glEnableVertexAttribArray(0);
curStep += 3;
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)(curStep * sizeof(float)));
glEnableVertexAttribArray(1);
curStep += 3;
// UV坐标
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)(curStep * sizeof(float)));
glEnableVertexAttribArray(2);
curStep += 2;
接下来我们需要在c++中加载这张漫反射贴图并传递到着色器中
之后我们会经常处理纹理贴图,方便起见,我们现在来封装一个简单的纹理类
Texture.h 新建
#pragma once
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "stb_image.h"
/**
* 纹理类
*/
class Texture
{
public:
/**
* 纹理宽度
*/
int width = 0;
/**
* 纹理高度
*/
int height = 0;
/**
* 纹理通道数
*/
int channel = 0;
private:
/**
* 纹理ID
*/
unsigned int textureID = 0;
public:
explicit Texture(const char* path);
/**
* 获取纹理ID
*/
unsigned int GetTextureID();
};
Texture.cpp 新建
#include "Texture.h"
Texture::Texture(const char* path)
{
unsigned char* textureData = stbi_load(path, &width, &height, &channel, 0);
glGenTextures(1, &textureID);
if (textureData)
{
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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (channel == 4)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData);
}
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(textureData);
std::cout << "Load texture: " << path << " success, with: " << width << " height: " << height << " channel: " << channel << std::endl;
}
else
{
std::cout << "[Error] Failed to load texture: " << path << std::endl;
}
}
unsigned int Texture::GetTextureID()
{
return textureID;
}
接下来加载贴图并传入到着色器
Main.cpp
Texture boxTex("F:/Scripts/Cpp/LearnOpenGL/learn-open-gl/Resource/T_Box_Diffuse.png");
while (!glfwWindowShouldClose(window))
{
// ...
shader.SetInt("material.diffuse", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, boxTex.GetTextureID());
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// ...
}
编译运行,顺利的话可以看见以下图像
如果遇到什么问题,可以参考顶部的git仓库
2. 镜面反射贴图 (Specular Map)
我们注意到,箱子上的高光部分,无论是金属边框还是中间的木条上都有高光,但是木条是比较粗糙的,我们不希望木条上有这么强烈的高光表现,接下来就要用到镜面反射贴图
同样,你可以右键保存或是在顶部的git仓库的Resource目录找到
这张贴图通过颜色值来表示某一像素是否需要受到高光 (黑或白),以及受到高光的强度 (灰)
首先修改我们的片段着色器
FragmentShader.fs
#version 330 core
// 物体材质结构体
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
// ...
void main()
{
// ...
// 镜面反射
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 specular = pow(max(dot(viewDir, reflectDir), 0), material.shininess) * lightColor * vec3(texture(material.specular, TexCoords)) * light.specular;
// ..
}
接着,加载镜面反射贴图并传递到着色器中
Texture boxSpecularTex("F:/Scripts/Cpp/LearnOpenGL/learn-open-gl/Resource/T_Box_Specular.png");
// ...
while (!glfwWindowShouldClose(window))
{
// ...
shader.SetInt("material.specular", 1);
// ...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, boxSpecularTex.GetTextureID());
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
// ...
}
编译运行,顺利的话可以看见以下图像
只有木箱的边缘金属部分会受到高光表现
完整代码可在顶部git仓库中找到
下接:https://blog.csdn.net/weixin_44506615/article/details/150446490?spm=1001.2014.3001.5502