上一篇文章当中笔者为大家介绍了风氏光照模型,相信大家也发现了光照着色器当中有设置有很多控制光照强度的参数,而所谓的材质系统就是我们可以人为的去调节这些参数,让一个物体的反光效果能够更加接近我们现实生活当中的一些物体。
材质系统
当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(Material Color ):环境光照(Ambient Lighting )、漫反射光照(Diffuse Lighting )和镜面光照(Specular Lighting )。通过为每个分量指定一个颜色,我们就能够对表面的颜色输出有细粒度的控制了。现在,我们再添加一个反光度(Shininess)分量,结合上述的三个颜色,我们就有了全部所需的材质属性了
cpp
#version 450 core
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
如你所见,我们为风氏光照模型的每个分量都定义一个颜色向量。ambient 材质向量定义了在环境光照下这个表面反射的是什么颜色,通常与表面的颜色相同。diffuse 材质向量定义了在漫反射光照下表面的颜色。漫反射颜色(和环境光照一样)也被设置为我们期望的物体颜色。specular 材质向量设置的是表面上镜面高光的颜色(或者甚至可能反映一个特定表面的颜色)。最后,shininess影响镜面高光的散射/半径。
使用材质
我们在片段着色器中创建了一个材质结构体的uniform ,所以下面我们希望修改一下光照的计算来遵从新的材质属性。由于所有材质变量都储存在一个结构体中,我们可以从uniform 变量material中访问它们:
cpp
void main()
{
// 环境光
vec3 ambient = lightColor * material.ambient;
// 漫反射
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightColor * (diff * material.diffuse);
// 镜面光
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);
}
我们可以看到,和之前光照模型几乎没有什么不同的,我们只是通过结构体的方式来访问这些向量,这样做的好处是我们可以人为的控制这些材质的向量了。
cpp
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);
不过到现在还是有一个问题,那就是整个光照模型看上去会特别的亮,这是因为我们还没有设置照射光的属性,我们需要调整光的亮度。
cpp
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
片段着色器修改成如下的代码
cpp
void main()
{
// 环境光
vec3 ambient = light.ambient * material.ambient;
// 漫反射
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * (diff * material.diffuse);
// 镜面光
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 * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
设置光照的属性
cpp
lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // 将光照调暗了一些以搭配场景
lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
好的到了这里,我们材质系统就为大家介绍到这里了,不过通过上面的代码我们知道,这个材质系统影响的是整个立方体的材质状况,但我们也知道现实生活当中这种单一材质物体是很少见的,一般我们看到的物体都是好几种材质组合而来的。要想表现出这种复杂的纹理,我们组好要能控制每个像素颜色,那怎样才能做到这种事了?可能有的朋友已经想到了,那就是使用之前的纹理,就可以控制片段着色器当中的像素颜色了!
纹理贴图
我们使用下面两个纹理图片来制作一个带有贴边的木箱子。
可能有的朋友想问,为什么铁框的贴图的中间的部分是黑色的,其实这个目的就在于减小木制材料部分中间的反光程度。整个光照着色器代码如下:
cpp
#type vertex
#version 450 core
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 color;
layout(location = 2) in vec3 normal;
layout(location = 3) in vec2 texCoord;
layout(location = 0) out vec4 v_Position;
layout(location = 1) out vec3 v_Color;
layout(location = 2) out vec3 v_Normal;
layout(location = 3) out vec2 v_texCoord;
uniform mat4 u_ViewProject;
void main(){
gl_Position = u_ViewProject * position;
v_Position = position;
v_Color = color;
v_Normal = normal;
v_texCoord = texCoord;
}
#type fragment
#version 450 core
layout(location = 0) out vec4 o_Color;
layout(location = 0) in vec4 v_Position;
layout(location = 1) in vec3 v_Color;
layout(location = 2) in vec3 v_Normal;
layout(location = 3) in vec2 v_texCoord;
struct Material {
int diffuse;
int specular;
int shininess;
};
struct Light{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform sampler2D u_Textures[2];
uniform vec3 u_ViewPos;
uniform Material material;
uniform Light light;
void main(){
vec3 v_position3 = vec3(v_Position.x,v_Position.y,v_Position.z);
//环境亮度
vec3 ambient = light.ambient * texture(u_Textures[material.diffuse],v_texCoord).rgb;
//漫反射亮度
vec3 vertex2light = normalize(light.position - v_position3);
float diff = max(dot(vertex2light,normalize(v_Normal)),0.0f);
vec3 diffuse = light.diffuse * diff * texture(u_Textures[material.diffuse],v_texCoord).rgb;
//镜面反射
vec3 viewDir = normalize(u_ViewPos - v_position3);
vec3 reflectDir = reflect(-vertex2light,normalize(v_Normal));
float spec = pow(max(dot(viewDir,reflectDir),0.0f),material.shininess);
vec3 specular = light.specular * spec * texture(u_Textures[material.specular],v_texCoord).rgb;
vec3 result = ambient + diffuse + specular;
o_Color = vec4(result,1.0f);
}
这样我们就可得到一个边框反光的木箱子了,这一章总体是比较简单的,主函数代码贴在下面,如果对主函数当中的一些变量不清楚的话可以到这个网站获取此案例的所有的代码,包括纹理贴图:https://gitee.com/HonyOrange_227/opengl-light-lab
我们来看一下最后的效果
此文章到这里就结束了,希望能帮助到大家
cpp
#include<glad/glad.h>
#include<GLFW/glfw3.h>
#include<iostream>
#include<glm/gtc/matrix_transform.hpp>
#include"Shader.h"
#include"Texture.h"
#include"Camera.h"
static Camera camera(glm::vec3(0.0f, 0.0f, 5.0f));
static bool run = true;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
int main() {
glfwInit();
GLFWwindow* window = glfwCreateWindow(640, 480, "Triangles", NULL, NULL);
glfwMakeContextCurrent(window);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//需要初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
float lightVertexes[] = {
//front surface
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //0
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //1
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //2
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //3
//back surface
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //4
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //5
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //6
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //7
//up surface
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //8
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //9
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //10
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //11
//down surface
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //12
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //13
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //14
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //15
//left surface
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //16
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //17
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //18
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //19
//right surface
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f, //20
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //21
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,1.0f,1.0f, //22
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,1.0f,1.0f //23
};
float vertexes[] = {
//front surface
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,1.0f, 0.0f,0.0f,//0
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,1.0f, 1.0f,0.0f,//1
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,1.0f, 1.0f,1.0f,//2
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,1.0f, 0.0f,1.0f,//3
//back surface
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,-1.0f, 0.0f,0.0f,//4
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,-1.0f, 1.0f,0.0f,//5
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,-1.0f, 1.0f,1.0f,//6
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,0.0f,-1.0f, 0.0f,1.0f,//7
//up surface
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,1.0f,0.0f, 0.0f,0.0f,//8
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,1.0f,0.0f, 1.0f,0.0f,//9
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,1.0f,0.0f, 1.0f,1.0f,//10
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,1.0f,0.0f, 0.0f,1.0f,//11
//down surface
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,-1.0f,0.0f, 0.0f,0.0f,//12
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,-1.0f,0.0f, 1.0f,0.0f,//13
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,-1.0f,0.0f, 1.0f,1.0f,//14
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 0.0f,-1.0f,0.0f, 0.0f,1.0f,//15
//left surface
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, -1.0f,0.0f,0.0f, 0.0f,0.0f,//16
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, -1.0f,0.0f,0.0f, 1.0f,0.0f,//17
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, -1.0f,0.0f,0.0f, 1.0f,1.0f,//18
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, -1.0f,0.0f,0.0f, 0.0f,1.0f,//19
//right surface
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 1.0f,0.0f,0.0f, 0.0f,0.0f,//20
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 1.0f,0.0f,0.0f, 1.0f,0.0f,//21
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f,0.2f, 1.0f,0.0f,0.0f, 1.0f,1.0f,//22
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f,0.2f, 1.0f,0.0f,0.0f, 0.0f,1.0f,//23
};
glm::vec4 originVertexes[24] = {
{-0.5f, -0.5f, 0.5f, 1.0f},
{0.5f, -0.5f, 0.5f, 1.0f},
{0.5f, 0.5f, 0.5f, 1.0f},
{-0.5f, 0.5f, 0.5f, 1.0f},
//back surface
{-0.5f, -0.5f, -0.5f, 1.0f},
{0.5f, -0.5f, -0.5f, 1.0f},
{0.5f, 0.5f, -0.5f, 1.0f},
{-0.5f, 0.5f, -0.5f, 1.0f},
//up surface
{-0.5f, 0.5f, 0.5f, 1.0f},
{0.5f, 0.5f, 0.5f, 1.0f},
{0.5f, 0.5f, -0.5f, 1.0f},
{-0.5f, 0.5f, -0.5f, 1.0f},
//down surface
{-0.5f, -0.5f, 0.5f, 1.0f},
{0.5f, -0.5f, 0.5f, 1.0f},
{0.5f, -0.5f, -0.5f, 1.0f},
{-0.5f, -0.5f, -0.5f, 1.0f},
//left surface
{-0.5f, -0.5f, -0.5f, 1.0f},
{-0.5f, -0.5f, 0.5f, 1.0f},
{-0.5f, 0.5f, 0.5f, 1.0f} ,
{-0.5f, 0.5f, -0.5f, 1.0f},
//right surface
{0.5f, -0.5f, -0.5f, 1.0f},
{0.5f, -0.5f, 0.5f, 1.0f},
{0.5f, 0.5f, 0.5f, 1.0f},
{0.5f, 0.5f, -0.5f, 1.0f}
};
unsigned int indexes[] = {
//front surface
0,1,2,
2,3,0,
//back surface
4,5,6,
6,7,4,
//up surface
8,9,10,
10,11,8,
//down surface
12,13,14,
14,15,12,
//left surface
16,17,18,
18,19,16,
//right surface
20,21,22,
22,23,20
};
glEnable(GL_DEPTH_TEST);
unsigned int buffer = 0, lightbuffer = 0,vertexArray = 0, lightVertexArray = 0,indexBuffer = 0;
glCreateVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);
glCreateBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 12 * sizeof(float), NULL);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(float), (const void*)(4 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(float), (const void*)(7 * sizeof(float)));
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 12 * sizeof(float), (const void*)(10 * sizeof(float)));
glCreateVertexArrays(1, &lightVertexArray);
glBindVertexArray(lightVertexArray);
glCreateBuffers(1, &lightbuffer);
glBindBuffer(GL_ARRAY_BUFFER, lightbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(lightVertexes), lightVertexes, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), NULL);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (const void*)(4 * sizeof(float)));
glCreateBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW);
Shader* pShader = new Shader("assets/shaders/TextureShader.glsl");
Shader* pLightShader = new Shader("assets/shaders/LightShader.glsl");
Texture* pTextureWood = new Texture("assets/Textures/container2.png");
Texture* pTextureSteel = new Texture("assets/Textures/container2_specular.png");
glm::mat4 transform = glm::translate(glm::mat4(1.0), glm::vec3(0.3f, 1.5f, 1.5f));
glm::vec4 orginCenter(0.0f, 0.0f, 0.0f, 1.0f);
int textureIndexes[2] = { 0,1 };
while (!glfwWindowShouldClose(window) && run) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 project = glm::perspective(glm::radians(camera.GetCameraZoom()), 640.0f / 480.0f, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 ViewProject = project * view;
for (int i = 0; i < 24; i++) {
glm::vec4 originPoint = originVertexes[i];
originPoint = transform * glm::scale(glm::mat4(1.0f), glm::vec3(0.5f, 0.5f, 0.5f)) * originPoint;
lightVertexes[i * 7] = originPoint.x;
lightVertexes[i * 7 + 1] = originPoint.y;
lightVertexes[i * 7 + 2] = originPoint.z;
lightVertexes[i * 7 + 3] = originPoint.w;
}
glBindBuffer(GL_ARRAY_BUFFER, lightbuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(lightVertexes), lightVertexes);
pShader->Bind();
pShader->UploadUniformat4("u_ViewProject", ViewProject);
glBindVertexArray(lightVertexArray);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, NULL);
glm::vec4 centerMove = transform * orginCenter;
for (int i = 0; i < 24; i++) {
glm::vec4 originPoint = originVertexes[i];
originPoint = glm::scale(glm::mat4(1.0f), glm::vec3(1.5f, 1.5f, 1.5f)) * originPoint;
vertexes[i * 12] = originPoint.x;
vertexes[i * 12 + 1] = originPoint.y;
vertexes[i * 12 + 2] = originPoint.z;
vertexes[i * 12 + 3] = originPoint.w;
}
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertexes), vertexes);
pTextureWood->Bind(0);
pTextureSteel->Bind(1);
pLightShader->Bind();
pLightShader->UploadUniformat4("u_ViewProject", ViewProject);
pLightShader->UploadUnifromFloat3("u_ViewPos",camera.GetPosition());
pLightShader->UploadUnifromFloat3("light.position", { centerMove.x, centerMove.y, centerMove.z });
pLightShader->UploadUnifromFloat3("light.ambient", { 0.2f,0.2f,0.2f });
pLightShader->UploadUnifromFloat3("light.diffuse", { 0.5f,0.5f,0.5f });
pLightShader->UploadUnifromFloat3("light.specular", { 1.0f,1.0f,1.0f });
pLightShader->UploadUniform1i("material.diffuse", 0);
pLightShader->UploadUniform1i("material.specular", 1);
pLightShader->UploadUniform1i("material.shininess", 64);
pLightShader->UplaodUniform1iv("u_Textures", 2, textureIndexes);
glBindVertexArray(vertexArray);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, NULL);
glfwSwapBuffers(window);
glfwPollEvents();
}
delete pShader;
delete pLightShader;
glfwDestroyWindow(window);
glfwTerminate();
}
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) {
static float lastX = 320.0f, lastY = 240.0f;
static bool firstMouse = true;
float xpos = static_cast<float>(xposIn);
float ypos = static_cast<float>(yposIn);
if (firstMouse) {
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos, lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* window, double xoffsetIn, double yoffsetIn) {
camera.ProcessMouseScroll(static_cast<float>(yoffsetIn));
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
run = false;
}