此篇博文为个人OpenGL学习笔记,内容参考LearnOpenGL CNGLFW官方文档OpenGL 4 Reference Pages、OpenGL编程指南(原书第9版)

颜色可以数字化的由红色(Red)、绿色(Green)和蓝色(Blue)三个分量组成,它们被缩写为RGB。这三个值可以组合出任意一种颜色。例如定义一个珊瑚红(Coral)色的向量:

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

现实生活中看到一个物体的颜色并不是物体真正拥有的颜色二十它所反射(Reflected)的颜色。那些不能被物体所吸收(Absorb)的颜色就是我们能感知到的物体的颜色。太阳光能被看见的白光是由许多不同颜色组成。如果将白光照在一个蓝色的玩具上,蓝色的玩具会吸收白光中除了蓝色以外的所有子颜色,不被吸收的蓝光被反射到眼中使得让玩具看起来时蓝色的。

珊瑚红的物体以不同的强度反射了多个颜色

白色的阳光实际上时所有可见颜色的集合,物体吸收了其中大部分颜色。它仅反射了代表物体颜色的部分,被反射的颜色的组合就是所能感知到的颜色。

当在OpenGL中创建一个光源时会给光源一个颜色(例如白色)。将光源的颜色与物体的颜色相乘的结果就是这个物体所反射的颜色(被感知的颜色):

glm::vec3 light_color(1.0f, 1.0f, 1.0f);
glm::vec3 object_color(1.0f, 0.5f, 0.31f);
glm::vec3 result = light_color * object_color; // = (1.0f, 0.5f, 0.31f)

物体的颜色吸收了白色光源中很大一部分颜色,但它根据自身的颜色值对红绿蓝三个分量做出了一定的反射。

使用颜色模拟现实中的光照效果。在场景中创建一个立方体箱子,在顶点着色器中保持顶点位置不变(同样需要模型矩阵、视图矩阵和变换矩阵,但不需要纹理坐标)。设置顶点数据、属性指针和着色器保持一致。另外需要创建一个表示光源的立方体,由于光源同样是立方体所以可以直接使用物体的立方体顶点坐标数据(VBO),但由于会频繁地对顶点数据和属性进行修改所以有必要为光源创建新的VAO:

unsigned int light_vao;
glGenVertexArrays(1, &light_vao);
glBindVertexArray(light_vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

片段着色器为光源和箱子着色:

#version 460 core
out vec4 color;

uniform vec3 object_color;
uniform vec3 light_color;

void main() {
    color = vec4(light_color * object_color, 1.0f);
}

将物体颜色设置为珊瑚红色并把光源设置为白色:

light_shader.Use();
light_shader.SetVec3("object_color", 1.0f, 0.5f, 0.31f);
light_shader.SetVec3("light_color", 1.0f, 1.0f, 1.0f);

但当修改顶点或者片段着色器后光源的位置或颜色也会随之改变。但并不希望颜色因光照计算的结果而受到影响,而是希望它能够与其它的计算分离(光源一直保持明亮,不受其它颜色变化的影响)。所以需要为光源的绘制创建另外的一套着色器以保证它能够在其它光照着色器发生改变的时候不受影响。通过片段着色器给光源一个不变的常量白色:

#version 460 core
out vec4 color;

void main() {
    color = vec4(1.0f);
}

当绘制物体时使用定义的光照着色器绘制物体,当绘制光源时使用光源着色器进行绘制。

声明一个全局变量表示光源在场景的世界空间坐标中的位置:

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

将光源进行一些位移、缩小:

model = glm::mat4(1.0f);
model = glm::translate(model, lamp_pos);
model = glm::scale(model, glm::vec3(0.2f));
lamp_shader.SetMat4("model", model);

light_shader.vs:

#version 460 core
layout (location = 0) in vec3 v_pos;

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

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

light_shader.fs:

#version 460 core
out vec4 color;

uniform vec3 object_color;
uniform vec3 light_color;

void main() {
    color = vec4(light_color * object_color, 1.0f);
}

lamp_shader.vs:

#version 460 core
layout (location = 0) in vec3 v_pos;

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

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

lamp_shader.fs:

#version 460 core
out vec4 color;

void main() {
    color = vec4(1.0f);
}

完整代码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include "shader.h"
#include "camera.h"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

const unsigned int window_width = 800;
const unsigned int window_height = 600;

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float mouse_last_x = window_width / 2.0f;
float mouse_last_y = window_height / 2.0f;
bool first_mouse = true;

float delta_time = 0.0f;
float last_frame = 0.0f;

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

void FramebufferSizeCallback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);
}

void ProcessInput(GLFWwindow* window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
        glfwSetWindowShouldClose(window, true);
    }
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
        camera.ProcessKeyboard(FORWARD, delta_time);
    }
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
        camera.ProcessKeyboard(BACKWARD, delta_time);
    }
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
        camera.ProcessKeyboard(LEFT, delta_time);
    }
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
        camera.ProcessKeyboard(RIGHT, delta_time);
    }
}

void MouseCallback(GLFWwindow* window, double x_pos, double y_pos) {
    if (first_mouse) {
        mouse_last_x = x_pos;
        mouse_last_y = y_pos;
        first_mouse = false;
    }
    float x_offset = x_pos - mouse_last_x;
    float y_offset = mouse_last_y - y_pos;
    mouse_last_x = x_pos;
    mouse_last_y = y_pos;
    camera.ProcessMouseMovement(x_offset, y_offset);
}

void ScrollCallback(GLFWwindow* window, double x_offset, double y_offset) {
    camera.ProcessMouseScroll(y_offset);
}

int main() {
    if (!glfwInit()) {
        std::cout << "Failed to initialize GLFW" << std::endl;
        return -1;
    }
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow* window = glfwCreateWindow(window_width, window_height, "Hello OpenGL", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, FramebufferSizeCallback);
    glfwSetCursorPosCallback(window, MouseCallback);
    glfwSetScrollCallback(window, ScrollCallback);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    glEnable(GL_DEPTH_TEST);
    Shader light_shader("light_shader.vs", "light_shader.fs");
    Shader lamp_shader("lamp_shader.vs", "lamp_shader.fs");
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,
        0.5f, -0.5f, -0.5f,
        0.5f,  0.5f, -0.5f,
        0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        0.5f, -0.5f,  0.5f,
        0.5f,  0.5f,  0.5f,
        0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        0.5f,  0.5f,  0.5f,
        0.5f,  0.5f, -0.5f,
        0.5f, -0.5f, -0.5f,
        0.5f, -0.5f, -0.5f,
        0.5f, -0.5f,  0.5f,
        0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,
        0.5f, -0.5f, -0.5f,
        0.5f, -0.5f,  0.5f,
        0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        0.5f,  0.5f, -0.5f,
        0.5f,  0.5f,  0.5f,
        0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
    };
    unsigned int vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    unsigned int cube_vao;
    glGenVertexArrays(1, &cube_vao);
    glBindVertexArray(cube_vao);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    unsigned int light_vao;
    glGenVertexArrays(1, &light_vao);
    glBindVertexArray(light_vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    while (!glfwWindowShouldClose(window)) {
        float current_frame = glfwGetTime();
        delta_time = current_frame - last_frame;
        last_frame = current_frame;
        ProcessInput(window);
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        light_shader.Use();
        light_shader.SetVec3("object_color", 1.0f, 0.5f, 0.31f);
        light_shader.SetVec3("light_color", 1.0f, 1.0f, 1.0f);
        glm::mat4 projection = glm::perspective(glm::radians(camera.fov), (float)window_width / (float)window_height, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        light_shader.SetMat4("projection", projection);
        light_shader.SetMat4("view", view);
        glm::mat4 model = glm::mat4(1.0f);
        light_shader.SetMat4("model", model);
        glBindVertexArray(cube_vao);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        lamp_shader.Use();
        lamp_shader.SetMat4("projection", projection);
        lamp_shader.SetMat4("view", view);
        model = glm::mat4(1.0f);
        model = glm::translate(model, lamp_pos);
        model = glm::scale(model, glm::vec3(0.2f));
        lamp_shader.SetMat4("model", model);
        glBindVertexArray(light_vao);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glDeleteVertexArrays(1, &cube_vao);
    glDeleteVertexArrays(1, &light_vao);
    glDeleteBuffers(1, &vbo);
    glfwTerminate();
    return 0;
}

运行效果:

运行效果

Last modification:March 18th, 2020 at 10:34 pm
如果觉得我的文章对你有用,请随意赞赏