开篇的话:终于磨了几天,把光照部分看完了,中间还因为项目问题学习了git的使用,光照这部分的知识还是需要很仔细的理解原理的,最开始这几篇一定要好好理解,多看几遍,不然到后面,各种向量一多,都不知道这个点成那个得出来的是什么结果。
在学习光照这部分的时候,有一个大坑,我会在本片片尾阐述。
(介绍光的内容我就不多说了,初高中物理都学过,我们看到的颜色,是物体所反射的颜色,太阳光是七色光)
我们定义一个太阳光,一个受光物体,将这两个颜色向量作分量相乘:
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);//阳光
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);//受光物体的颜色
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);//反射的颜色
物体的颜色吸收了白色光源中很大一部分的颜色,但它根据自身的颜色值对红、绿、蓝三个分量都做出了一定的反射。这也表现了现实中颜色的工作原理。
把光源改为绿色:
glm::vec3 lightColor(0.0f, 1.0f, 0.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);
可以看到,并没有红色和蓝色的光让我们的玩具来吸收或反射。这个玩具吸收了光线中一半的绿色值,但仍然也反射了一半的绿色值。物体现在看上去是深绿色(Dark-greenish)的。
创建一个光照场景
首先我们需要一个物体来作为被投光(Cast the light)的对象,我们将使用前面教程中的那个(贴着笑脸的)的立方体箱子。
使用之前教程顶点着色器的精简版:
#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);
}
我们还要创建一个表示灯(光源)的立方体,所以我们还要为这个灯创建一个专门的VAO。
unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// 只需要绑定VBO不用再次设置VBO的数据,因为箱子的VBO数据中已经包含了正确的立方体顶点数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 设置灯立方体的顶点属性(对我们的灯来说仅仅只有位置数据)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
(这套东西都很简单了,创建VAO,绑定VBO,设置属性·····)
定义一个fragmentshader:(用于我们创建的受光的BOX)
#version 330 core
out vec4 FragColor;
uniform vec3 objectColor;
uniform vec3 lightColor;
void main()
{
FragColor = vec4(lightColor * objectColor, 1.0);
}
在主程序中为fragmentshader中的两个uniform变量赋值:
// 在此之前不要忘记首先 use 对应的着色器程序(来设定uniform)
lightingShader.use();
lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
光源fragmentshader:(主程序中定义光源颜色,传入shader中即可)
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0); // 将向量的四个分量全部设置为1.0
}
我们创建的光源物体的vertexshader和受光物体的vertexshader一样。
在主程序中我们实例化我们的两个shader:
//受光物体
Shader lightingShader("shader/vertexShader.vs", "shader/fragmentShader.vs");
//光源物体
Shader lampShader("shader/lightvertexShader.vs", "shader/lightfragmentShader.vs");
(好吧,我承认这个名字特别容易搞混!我已经习惯了。)
明一个全局vec3
变量来表示光源在场景的世界空间坐标中的位置:
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
像摄像机一样,定义好我们的三个矩阵:(model,view,projection)
glm::mat4 model, view, projection;
view = camera.GetViewMatrix();
projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
设置光源的位置,并缩放它:
model = glm::mat4();
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f));
绘制两个不同的立方体:(整合以上代码)
glm::mat4 model, view, projection;
view = camera.GetViewMatrix();
projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
//受光cube渲染
lightingShader.use();
lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
lightingShader.setMat4("model", model);
lightingShader.setMat4("view", view);
lightingShader.setMat4("projection", projection);
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
//光源白色cube渲染
lampShader.use();
model = glm::mat4();
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f));
lampShader.setMat4("model", model);
lampShader.setMat4("view", view);
lampShader.setMat4("projection", projection);
glBindVertexArray(lightVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
OK!出图:
第一小节还是十分简单的,贴出我的源码:(shader的源码,上面都给出过,就不贴了)
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#include "camera.h"
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow*window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
float deltaTime = 0.0f;
float lastFrame = 0.0f