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

现实世界中每个物体对光会产生不同的反应。比如钢看起来通常比陶瓷花瓶更闪闪发光,木头箱子也不会像钢制箱子那样对光产生很强的反射。每个物体对对镜面高光也有不同的反应。有些物体反射光的时候不会有太多的散射(Scatter),因而产生一个较小的高光点,而有些物体则会散射很多,产生一个有着更大半径的高光点。再OpenGL中为每个物体分别定义一个材质(Material)属性模拟多种类型的物体。

当描述一个物体时可以用环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)三个分量定义一个材质颜色(Material Color)。再添加反光度(Shininess)分量到其中就是所需的的所有材料属性:

#version 460 vore

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};

uniform Material material;

在片段着色器中创建一个结构体(Struct)存储物体的材质属性,并使用此结构体类型声明一个uniform变量。

ambient材质向量定义了在环境光照下物体反射的颜色(通常和物体颜色相同),diffuse材质向量定义了在漫反射光照下物体的颜色(和环境光照一样漫反射颜色也设置为物体颜色),specular材质向量设置镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色),shininess影响镜面高光的散射/半径。

通过这四个元素定义的物体材质能够模拟很多现实世界中的材质。

NameAmbientDiffuseSpecularShininess
emerald(0.0215,0.1745,0.0215)(0.07568,0.61424,0.07568)(0.633,0.727811,0.633)0.6
jade(0.135,0.2225,0.1575)(0.54,0.89,0.63)(0.316228,0.316228,0.316228)0.1
obsidian(0.05375,0.05,0.06625)(0.18275,0.17,0.22525)(0.332741,0.328634,0.346435)0.3
pearl(0.25,0.20725,0.20725)(1,0.829,0.829)(0.296648,0.296648,0.296648)0.088
ruby(0.1745,0.01175,0.01175)(0.61424,0.04136,0.04136)(0.727811,0.626959,0.626959)0.6
turquoise(0.1,0.18725,0.1745)(0.396,0.74151,0.69102)(0.297254,0.30829,0.306678)0.1
brass(0.329412,0.223529,0.027451)(0.780392,0.568627,0.113725)(0.992157,0.941176,0.807843)0.21794872
bronze(0.2125,0.1275,0.054)(0.714,0.4284,0.18144)(0.393548,0.271906,0.166721)0.2
chrome(0.25,0.25,0.25)(0.4,0.4,0.4)(0.774597,0.774597,0.774597)0.6
copper(0.19125,0.0735,0.0225)(0.7038,0.27048,0.0828)(0.256777,0.137622,0.086014)0.1
gold(0.24725,0.1995,0.0745)(0.75164,0.60648,0.22648)(0.628281,0.555802,0.366065)0.4
silver(0.19225,0.19225,0.19225)(0.50754,0.50754,0.50754)(0.508273,0.508273,0.508273)0.4
black plastic(0.0,0.0,0.0)(0.01,0.01,0.01)(0.50,0.50,0.50).25
cyan plastic(0.0,0.1,0.06)(0.0,0.50980392,0.50980392)(0.50196078,0.50196078,0.50196078).25
green plastic(0.0,0.0,0.0)(0.1,0.35,0.1)(0.45,0.55,0.45).25
red plastic(0.0,0.0,0.0)(0.5,0.0,0.0)(0.7,0.6,0.6).25
white plastic(0.0,0.0,0.0)(0.55,0.55,0.55)(0.70,0.70,0.70).25
yellow plastic(0.0,0.0,0.0)(0.5,0.5,0.0)(0.60,0.60,0.50).25
black rubber(0.02,0.02,0.02)(0.01,0.01,0.01)(0.4,0.4,0.4).078125
cyan rubber(0.0,0.05,0.05)(0.4,0.5,0.5)(0.04,0.7,0.7).078125
green rubber(0.0,0.05,0.0)(0.4,0.5,0.4)(0.04,0.7,0.04).078125
red rubber(0.05,0.0,0.0)(0.5,0.4,0.4)(0.7,0.04,0.04).078125
white rubber(0.05,0.05,0.05)(0.5,0.5,0.5)(0.7,0.7,0.7).078125
yellow rubber(0.05,0.05,0.0)(0.5,0.5,0.4)(0.7,0.7,0.04).078125

上述表格展示了几种材质属性,它们模拟了现实世界中的真是材质。

几种现实世界的材质对我们的立方体的影响

在片段着色器中创建材质结构体的unifrom变量之后就可以修改光照的计算来顺应新的材质属性:

#version 460 core
out vec4 color;

in vec3 f_normal;
in vec3 f_pos;

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};

uniform Material material;

uniform vec3 lamp_pos;
uniform vec3 view_pos;
uniform vec3 light_color;

void main() {
    vec3 ambient = material.ambient * light_color;
    vec3 norm = normalize(f_normal);
    vec3 light_dir = normalize(lamp_pos - f_pos);
    float diff = max(dot(norm, light_dir), 0.0f);
    vec3 diffuse = light_color * (diff * material.diffuse);
    vec3 view_dir = normalize(view_pos - f_pos);
    vec3 reflect_dir = reflect(-light_dir, norm);
    float spec = pow(max(dot(view_dir, reflect_dir), 0.0f), material.shininess);
    vec3 specular = light_color * (spec * material.specular);
    vec3 result = ambient + diffuse + specular;
    color = vec4(result, 1.0f);
}

再片段着色器中根据材质的颜色计算最终输出的颜色,物体的每个材质属性都乘上了它们对应的光照分量。

在程序中对结构体每个数据单独的uniform进行设置,完整代码:

#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.0f,  0.0f, -1.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };
    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, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    unsigned int light_vao;
    glGenVertexArrays(1, &light_vao);
    glBindVertexArray(light_vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 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("material.ambient", 1.0f, 0.5f, 0.31f);
        light_shader.SetVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
        light_shader.SetVec3("material.specular", 0.5f, 0.5f, 0.5f);
        light_shader.SetFloat("material.shininess", 32.0f);
        light_shader.SetVec3("light_color", 1.0f, 1.0f, 1.0f);
        light_shader.SetVec3("lamp_pos", lamp_pos);
        light_shader.SetVec3("view_pos", camera.position);
        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;
}

运行效果:

运行效果

但是这样的运行效果由于环境光、漫反射和镜面光这三个颜色对任何一个光源都全力反射而看起来太亮了。光源对环境光、漫反射和镜面光分量也具有不同的强度。

假设light_color变量的值为vec3(1.0)那么光照计算起来就是:

vec3 ambient  = vec3(1.0) * material.ambient;
vec3 diffuse  = vec3(1.0) * (diff * material.diffuse);
vec3 specular = vec3(1.0) * (spec * material.specular);

所以物体的每个材质属性对每一个光照分量都返回了最大强度。对单个光源来说这些vec3(1.0)的值同样可以分别改编。现在物体的环境光分量完全地影响了立方体地颜色可是环境光分量实际上不应该对物体最终的颜色由这么大的影响,所以将光源的环境光强度设置为一个小一点的值从而限制环境光颜色:

vec3 ambient  = vec3(0.1) * material.ambient;

为光照属性同样创建一个结构体:

struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;

一个光源对它的ambient、diffuse和specular光照有着不同的强度。环境光通常会设置为一个比较低的强度(环境光不会太过显眼)。光源的漫反射分量通常设置为光具有的颜色,通常是一个比较明亮的白色。镜面光分量通常会保持为vec3(1.0),以最大强度发光。

更新片段着色器:

#version 460 core
out vec4 color;

in vec3 f_normal;
in vec3 f_pos;

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};

struct Light {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Material material;
uniform Light light;

uniform vec3 view_pos;

void main() {
    vec3 ambient = light.ambient * material.ambient;
    vec3 norm = normalize(f_normal);
    vec3 light_dir = normalize(light.position - f_pos);
    float diff = max(dot(norm, light_dir), 0.0f);
    vec3 diffuse = light.diffuse * (diff * material.diffuse);
    vec3 view_dir = normalize(view_pos - f_pos);
    vec3 reflect_dir = reflect(-light_dir, norm);
    float spec = pow(max(dot(view_dir, reflect_dir), 0.0f), material.shininess);
    vec3 specular = light.specular * (spec * material.specular);
    vec3 result = ambient + diffuse + specular;
    color = vec4(result, 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.0f,  0.0f, -1.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };
    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, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    unsigned int light_vao;
    glGenVertexArrays(1, &light_vao);
    glBindVertexArray(light_vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 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("material.ambient", 1.0f, 0.5f, 0.31f);
        light_shader.SetVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
        light_shader.SetVec3("material.specular", 0.5f, 0.5f, 0.5f);
        light_shader.SetFloat("material.shininess", 32.0f);
        light_shader.SetVec3("light.ambient", 0.2f, 0.2f, 0.2f);
        light_shader.SetVec3("light.diffuse", 0.5f, 0.5f, 0.5f);
        light_shader.SetVec3("light.specular", 1.0f, 1.0f, 1.0f);
        light_shader.SetVec3("light.position", lamp_pos);
        light_shader.SetVec3("view_pos", camera.position);
        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;
}

运行效果:

运行效果

还可以随时间的变化改变物体颜色,完整代码:

#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.0f,  0.0f, -1.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };
    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, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    unsigned int light_vao;
    glGenVertexArrays(1, &light_vao);
    glBindVertexArray(light_vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 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();
        glm::vec3 light_color(1.0f);
        light_color.x = std::sin(glfwGetTime() * 2.0f);
        light_color.y = std::sin(glfwGetTime() * 0.7f);
        light_color.z = std::sin(glfwGetTime() * 1.3f);
        glm::vec3 diffuse_color = light_color * glm::vec3(0.5f);
        glm::vec3 ambient_color = diffuse_color * glm::vec3(0.2f);
        light_shader.SetVec3("light.ambient", ambient_color);
        light_shader.SetVec3("light.diffuse", diffuse_color);
        light_shader.SetVec3("light.specular", 1.0f, 1.0f, 1.0f);
        light_shader.SetVec3("material.ambient", 1.0f, 0.5f, 0.31f);
        light_shader.SetVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
        light_shader.SetVec3("material.specular", 0.5f, 0.5f, 0.5f);
        light_shader.SetFloat("material.shininess", 32.0f);
        light_shader.SetVec3("light.position", lamp_pos);
        light_shader.SetVec3("view_pos", camera.position);
        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 19th, 2020 at 05:56 pm
如果觉得我的文章对你有用,请随意赞赏