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);

最终绘制的效果如下:

参考资料