1 - 颜色

本节将会深入讨论什么是颜色,并为以后的光照部分创建一个场景。

颜色

颜色由RGB三个分量组成,例如要想创建一个珊瑚红色(Coral),只需定义颜色向量如下:

glm::vec3 coral(1.0f, 0.5f, 0.31f);	

实际上,我们在现实生活中看到某一物体的颜色并不是它所真正拥有的,而是它所 反射(Reflected)的颜色。如下图,将白光照到珊瑚红色的表面上,只有部分红色被反射:

光源和物体表面颜色综合的过程如下:

glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 objColor(1.0f, 0.5f, 0.31f);
// 结果
glm::vec3 result = lightColor * objColor;

可以使用不同的光源颜色来让物体显现出意想不到的颜色。有创意地利用颜色其实并不难。

创建光照场景

我们需要创建一个可见的光源和一个被光投射(Cast)的物体,用于测试模拟光照的效果。

创建测试物体

我们使用一个立方体来代表被投射的物体,为方便,顶点数据还是用之前的。

// 定义顶点和索引信息
float vertices[] = {
    -0.5f, -0.5f, -0.5f, // 0
     0.5f, -0.5f, -0.5f, // 1
     0.5f,  0.5f, -0.5f, // 2
    -0.5f,  0.5f, -0.5f, // 3
    -0.5f, -0.5f,  0.5f, // 4
     0.5f, -0.5f,  0.5f, // 5
     0.5f,  0.5f,  0.5f, // 6
    -0.5f,  0.5f,  0.5f  // 7
};

unsigned int indices[] = {
    // 注意索引从0开始! 
    0,1,2,2,3,0,
    4,5,6,6,7,4,
    7,3,0,0,4,7,
    6,2,1,1,5,6,
    0,1,5,5,4,0,
    3,2,6,6,7,3
};

然后为该物体绑定VAO,VBO,EBO,解析顶点信息:

// ---------------被测试物体-------------------
// 绑定VAO
glBindVertexArray(cubeVAO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO); 
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 绑定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 解析顶点位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 解绑VAO
glBindVertexArray(0);
// -------------------------------------------

创建可见光源

为图省事,光源也用立方体来绘制:

// ---------------光源------------------------
// 绑定VAO
glBindVertexArray(lightVAO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 绑定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 解析顶点位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 解绑VAO
glBindVertexArray(0);
// -------------------------------------------

然后声明光源的位置:

glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

配置着色器

接下来配置顶点着色器来绘制立方体,这次没有纹理坐标了,因此写的简单点:

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

然后是立方体片段着色器:

#version 330 core
out vec4 FragColor;

uniform vec3 objectColor;
uniform vec3 lightColor;

void main()
{
    FragColor = vec4(lightColor * objectColor, 1.0);
}

需要注意的是,光源得一直是白色,因此它得有自己的片段着色器:

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // 将向量的四个分量全部设置为1.0
}

绘制物体

配置好MVP矩阵后(M直接用初始值),首先是绘制物体:

cubeShader.use();
cubeShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
cubeShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
cubeShader.setMat4("model", model);
cubeShader.setMat4("view", view);
cubeShader.setMat4("projection", proj);
glBindVertexArray(cubeVAO);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

然后是绘制光源:

lightShader.use();
// 这里将光源缩放平移一下
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f));
lightShader.setMat4("model", model);
lightShader.setMat4("view", view);
lightShader.setMat4("projection", proj);
glBindVertexArray(lightVAO);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

最终绘制的效果如下:

参考资料

  • 颜色 - LearnOpenGL CN (learnopengl-cn.github.io)