OpenGL基础40:Uniform缓冲

 

前置:OpenGL基础39:GLSL内建变量与接口块

想想之前代码,glUniform()和glGetUniformLocation()的使用数量是不是过于频繁了,对于每个着色器的每一个uniform变量,都需要特意去设置,不但代码特别长,而且大多都是重复的

一、Uniform对象

没错,就是UBO,这不知道是第几个缓冲对象(xBO)了,不过也好,这样不用去查都可以大致知道它的意思和作用

UBO很重要以至于要专门开一章记录,并且篇幅不小

在之前的例子中,几乎所有的物体着色器中都有使用投影矩阵和观察矩阵,值也是相同的(都处于一个世界中怎么可能不一样嘛),但是它们的着色器却可能不相同,一个一个去用glUniform()设置就比较麻烦,在这时通过UBO就可以很方便的设置所有着色器中的相同数据了

uniform的定义和上一章中的接口块很像:

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
out vec3 normalIn;
out vec3 fragPosIn;
uniform mat4 model;
layout (std140) uniform Matrices
{
    mat4 view;
    mat4 projection;
};
void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);
    fragPosIn = vec3(model * vec4(position, 1.0f));
    normalIn = mat3(transpose(inverse(model))) * normal;
}

这里声明了一个叫做Matrices的uniform块,它储存两个4×4矩阵。在uniform块中的变量可以直接获取,而不用使用block名作为前缀,不过多了一个layout布局设置

 

二、Uniform块布局

之前学过C++或C#的话,应该都了解结构体的内存布局,uniform也有一定的内存布局规则,不同的布局规则如下:

布局控制修饰符
sharedUniform Block 在多个 Shader 程序之间是共享的(默认布局)
packed最小化内存的使用,通常禁用 Shader 程序之间的共享
std140用于 Uniform Blocks 或 Shader Storage Buffer Blocks 的标准布局
std430用于 Buffer Blocks 的标准布局
row_major使得 Uniform Blocks 中的矩阵按行主序存储
coloum_major使得 Uniform Blocks 中的矩阵按列主序存储(默认顺序)

这是第一次设置,在此之前GLSL默认使用的共享布局(shared layout),使用共享布局,GLSL可以为了优化而重新放置uniform变量,只要变量的顺序保持完整,但由于不知道每个uniform变量的偏移量是多少,所以我想要精确地填充uniform缓冲就会比较困难,尽管可以通过glGetUniformIndices这样的函数来查询信息

而对于std140,一系列的规则的规范声明了它们各自的偏移量,每个变量都有一个基线对齐(base alignment),它等于在一个uniform块中这个变量所占的空间(包含边距),因此对于每个变量很容易得到它的对齐偏移(aligned offset),这是一个变量从块(block)开始处的字节偏移量,变量对齐的字节偏移一定等于它的基线对齐的倍数

std140布局规范
类型布局规范
像int和bool这样的标量每个标量的基线为N
向量每个向量的基线是2N或4N大小。这意味着vec3的基线为4N
标量与向量数组每个元素的基线与vec4的相同
矩阵被看做是存储着大量向量的数组,每个元素的基数与vec4相同
结构体根据以上规则计算其各个元素,并且间距必须是vec4基线的倍数

一个代码的例子如下:

layout (std140) uniform ExampleBlock
{
                     // 基线                       // 对齐偏移
    float value;     // 4                          // 0
    vec3 vector;     // 16                         // 16 (必须是16的倍数,因此 4->16)
    mat4 matrix;     // 16                         // 32  (第 0 行)
                     // 16                         // 48  (第 1 行)
                     // 16                         // 64  (第 2 行)
                     // 16                         // 80  (第 3 行)
    float values[3]; // 16 (数组中的标量与vec4相同)  // 96 (values[0])
                     // 16                         // 112 (values[1])
                     // 16                         // 128 (values[2])
    bool boolean;    // 4                          // 144
    int integer;     // 4                          // 148
};

很好理解,一般情况下,都是设置为std140

 

三、Uniform缓冲

着色器部分搞定了,接下来看怎么使用它

先是UBO的定义部分:

  • glGetUniformBlockIndex():得到 Uniform Block 变量的索引
  • glUniformBlockBinding():把对应的 Uniform Block 设置到一个特定的绑定点上
GLuint UBO;
GLuint uniformCameraM = glGetUniformBlockIndex(shaderMirror.Program, "Matrices");
GLuint uniformCameraM2 = glGetUniformBlockIndex(shaderObj.Program, "Matrices");
GLuint uniformCameraM4 = glGetUniformBlockIndex(shaderStone.Program, "Matrices");
glUniformBlockBinding(shaderMirror.Program, uniformCameraM, 0);
glUniformBlockBinding(shaderMirror.Program, uniformCameraM2, 0);
glUniformBlockBinding(shaderMirror.Program, uniformCameraM4, 0);
glGenBuffers(1, &UBO);
glBindBuffer(GL_UNIFORM_BUFFER, UBO);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, UBO, 0, 2 * sizeof(glm::mat4));

绑定点(binding points):

在OpenGL环境中,定义了若干绑定点,正是通过绑定点将着色器中uniform块和我们创建的uniform缓冲链接起来的,处于同一绑定点的数据共享,原理如下图(参考于openGL.cn):

对于上面的代码,先是拿到了3个着色器的uniform块索引,它们的uniform内容和数据一致,接下来都将他们绑定到0绑定点上,这样子,在之后如果我们用glBufferSubData更新对应内存的数据,那么那3个着色器对应的uniform块数据都会被同时更新

在OpenGL4.2版本后,就可以直接在着色器中通过添加另一个布局标识符来储存一个uniform块的绑定点,就不再需要调用glGetUniformBlockIndex和glUniformBlockBinding了,非常的方便:

layout(std140, binding = 2) uniform Param { ... };

接下来就是更新数据对象:

  • glBindBufferBase():接收三个参数,分别为缓冲目标、绑定点索引和uniform缓冲对象,这个函数负责将uboExampleBlock链接到绑定点上面,在此之后绑定点两端链接完毕
  • glBindBufferRange():和glBindBufferBase一样,只不过它多接受两个参数,分别为偏移量和大小,可以指定内存范围
  • glBufferSubData():在《OpenGL基础38:数据存储》这一章中有详细介绍
glBindBuffer(GL_UNIFORM_BUFFER, UBO);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(view));
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(projection));
glBindBuffer(GL_UNIFORM_BUFFER, 0);

到此就完全搞定了,完整主代码:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include"Camera.h"
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Mesh.h"
#include"Model.h"
#include<opengl/freeglut.h>
#include<SOIL.h>

bool keys[1024];
Camera camera;
GLfloat lastX, lastY;
bool firstMouse = true;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
GLuint loadCubemap(vector<const GLchar*> faces);
GLuint getAttachmentTexture();
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
GLuint loadTexture(const GLchar* path, GLboolean alpha);
void cameraMove();
const GLuint WIDTH = 800, HEIGHT = 600;

int main()
{
    //------------------------------------------------------初始化---------------------------------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glewExperimental = GL_TRUE;
    glewInit();

    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);

    Shader shaderObj("ObjVShader.vert", "ObjFShader.frag");
    Shader shaderLight("LightVShader.vert", "LightFShader.frag");
    Shader shaderStone("StoneVShader.vert", "StoneFShader.frag");
    Shader shaderSkyBox("SkyboxVShader.vert", "SkyboxFShader.frag");
    Shader shaderScreen("ScreenVShader.vert", "ScreenFShader.frag");
    Shader shaderMirror("MirrorVShader.vert", "MirrorFShader.frag");

    //------------------------------------------------------数据定义---------------------------------------------------------
    GLfloat vertices[] = 
    {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    GLfloat stoneVertices[] = 
    {
        0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
        0.0f, -0.5f,  0.0f,  0.0f,  1.0f,
        1.0f, -0.5f,  0.0f,  1.0f,  1.0f,
        0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
        1.0f, -0.5f,  0.0f,  1.0f,  1.0f,
        1.0f,  0.5f,  0.0f,  1.0f,  0.0f
    };
    glm::vec3 positions[] = 
    {
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 0.89f, 0.0f),
        glm::vec3(0.0f, 1.78f, 0.0f),
        glm::vec3(-2.0f, 0.0f, 0.0f),
        glm::vec3(-2.0f, 0.89f, 0.0f),
        glm::vec3(-3.0f, 0.0f, 0.0f),
        glm::vec3(-2.0f, 0.0f, 1.0f),
        glm::vec3(-1.0f, 0.0f, -4.0f),
    };
    glm::vec3 pointLightPositions[] = 
    {
       glm::vec3(-1.0f, 1.8f, -2.0f),
       glm::vec3(0.0f, 0.8f, 2.0f),
       glm::vec3(-5.0f, 0.8f, 1.0f),
    };
    GLfloat quadVertices[] = 
    {
        -1.0f, 1.0f, 0.0f, 1.0f,
        -1.0f, -1.0f, 0.0f, 0.0f,
         1.0f, -1.0f, 1.0f, 0.0f,
        -1.0f, 1.0f, 0.0f, 1.0f,
         1.0f, -1.0f, 1.0f, 0.0f,
         1.0f, 1.0f, 1.0f, 1.0f
    };
    GLfloat skyboxVertices[] = 
    {
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

        -1.0f,  1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f,  1.0f
    };
    vector<glm::vec3> vegetation;
    vegetation.push_back(glm::vec3(-1.5f, -0.01f, -0.48f));
    vegetation.push_back(glm::vec3(1.5f, -0.01f, 0.51f));
    vegetation.push_back(glm::vec3(0.0f, -0.01f, 0.7f));
    vegetation.push_back(glm::vec3(-0.3f, -0.01f, -2.3f));
    vegetation.push_back(glm::vec3(0.5f, -0.01f, -0.6f));

    //------------------------------------------------------VAO & VBO---------------------------------------------------------
    GLuint lightVAO, lightVBO;
    glGenVertexArrays(1, &lightVAO);
    glGenBuffers(1, &lightVBO);
    glBindVertexArray(lightVAO);
    glBindBuffer(GL_ARRAY_BUFFER, lightVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    GLuint quadVAO, quadVBO;
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    GLuint skyboxVAO, skyboxVBO;
    glGenVertexArrays(1, &skyboxVAO);
    glGenBuffers(1, &skyboxVBO);
    glBindVertexArray(skyboxVAO);
    glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    GLuint stoneVAO, stoneVBO;
    glGenVertexArrays(1, &stoneVAO);
    glGenBuffers(1, &stoneVBO);
    glBindVertexArray(stoneVAO);
    glBindBuffer(GL_ARRAY_BUFFER, stoneVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(stoneVertices), stoneVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    GLuint stone = loadTexture("Texture/stone3.png", true);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    //------------------------------------------------------UBO---------------------------------------------------------
    GLuint UBO;
    /*--已在着色器中设置
    GLuint uniformCameraM = glGetUniformBlockIndex(shaderMirror.Program, "Matrices");
    GLuint uniformCameraM2 = glGetUniformBlockIndex(shaderObj.Program, "Matrices");
    GLuint uniformCameraM4 = glGetUniformBlockIndex(shaderStone.Program, "Matrices");
    glUniformBlockBinding(shaderMirror.Program, uniformCameraM, 0);
    glUniformBlockBinding(shaderMirror.Program, uniformCameraM2, 0);
    glUniformBlockBinding(shaderMirror.Program, uniformCameraM4, 0);
    */
    glGenBuffers(1, &UBO);
    glBindBuffer(GL_UNIFORM_BUFFER, UBO);
    glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    glBindBufferRange(GL_UNIFORM_BUFFER, 0, UBO, 0, 2 * sizeof(glm::mat4));

    //------------------------------------------------------帧缓冲---------------------------------------------------------
    GLuint FBO, RBO;
    glGenFramebuffers(1, &FBO);
    glBindFramebuffer(GL_FRAMEBUFFER, FBO);
    GLuint textureColorbuffer = getAttachmentTexture();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
    glGenRenderbuffers(1, &RBO);
    glBindRenderbuffer(GL_RENDERBUFFER, RBO);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO);
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    //------------------------------------------------------设置状态、加载模型和天空盒---------------------------------------------------------
    glEnable(GL_CULL_FACE);
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);             //模板测试和深度测试都成功时,将对应像素的模板值设置为用glStencilFunc函数设置的ref值
    glEnable(GL_BLEND);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    Model wood("Object/wood/file.fbx", "Object/wood/file.fbx");
    Model ground("Object/ground/ground.fbx", "Object/ground");
    Model mirror("Object/mirror/file.fbx", "Object/mirror");

    vector<const GLchar*> faces;
    faces.push_back("Texture/Skybox/StarryNight1024/Right.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Left.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Up.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Down.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Back.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Front.jpg");
    GLuint cubemapTexture = loadCubemap(faces);

    //------------------------------------------------------渲染ing---------------------------------------------------------
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
        glEnable(GL_DEPTH_TEST);
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glStencilMask(0xFF);                  //设置模板缓冲区可写入,如果设置为不可写入之后清空模板缓冲区,将会清空失败!毕竟不可写入了
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        cameraMove();

        shaderLight.Use();
        glm::mat4 view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        glm::mat4 model = glm::mat4(1.0f);
        GLint modelLoc = glGetUniformLocation(shaderLight.Program, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glBindBuffer(GL_UNIFORM_BUFFER, UBO);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(view));
        glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(projection));
        glBindBuffer(GL_UNIFORM_BUFFER, 0);

        glBindVertexArray(lightVAO);
        glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);                   //无论模板测试如何,一定可以绘制;
        glUniform3f(glGetUniformLocation(shaderLight.Program, "lightColor"), 1.0f, 1.0f, 1.0f);
        for (int i = 0; i <= 2; i++)
        {
            model = glm::translate(glm::mat4(1.0f), pointLightPositions[i]);
            model = glm::scale(model, glm::vec3(0.2f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glStencilFunc(GL_NOTEQUAL, 0xFF, 0xFF);                 //对应像素模板值若等于256,则对应像素不绘制
        glStencilMask(0x00);                                    //模板缓冲区不再可写
        glUniform3f(glGetUniformLocation(shaderLight.Program, "lightColor"), 1.0f, 1.0f, 0.0f);
        for (int i = 0; i <= 2; i++)
        {
            model = glm::translate(glm::mat4(1.0f), pointLightPositions[i]);
            model = glm::scale(model, glm::vec3(0.22f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);
        shaderObj.Use();
        glUniform3f(glGetUniformLocation(shaderObj.Program, "sunLight.direction"), -0.2f, -1.0f, -0.3f);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "sunLight.diffuse"), 0.4f, 0.4f, 0.4f);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "sunLight.specular"), 0.5f, 0.5f, 0.5f);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[0].position"), pointLightPositions[0].x, pointLightPositions[0].y, pointLightPositions[0].z);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[0].diffuse"), 0.8f, 0.8f, 0.8f);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[0].specular"), 1.0f, 1.0f, 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[0].k0"), 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[0].k1"), 0.09);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[0].k2"), 0.032);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[1].position"), pointLightPositions[1].x, pointLightPositions[1].y, pointLightPositions[1].z);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[1].diffuse"), 0.8f, 0.8f, 0.8f);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[1].specular"), 1.0f, 1.0f, 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[1].k0"), 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[1].k1"), 0.09);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[1].k2"), 0.032);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[2].position"), pointLightPositions[2].x, pointLightPositions[2].y, pointLightPositions[2].z);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[2].diffuse"), 0.8f, 0.8f, 0.8f);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[2].specular"), 1.0f, 1.0f, 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[2].k0"), 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[2].k1"), 0.09);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[2].k2"), 0.032);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.position"), camera.Position.x, camera.Position.y, camera.Position.z);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.direction"), camera.Front.x, camera.Front.y, camera.Front.z);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.diffuse"), 1.0f, 1.0f, 1.0f);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.specular"), 1.0f, 1.0f, 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.k0"), 1.0f);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.k1"), 0.09);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.k2"), 0.032);
        glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.cutOff"), glm::cos(glm::radians(12.5f)));
        glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.outCutOff"), glm::cos(glm::radians(16.0f)));
        glUniform3f(glGetUniformLocation(shaderObj.Program, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z);

        modelLoc = glGetUniformLocation(shaderObj.Program, "model");
        for (int i = 0; i <= 7; i++)
        {
            model = glm::translate(glm::mat4(1.0f), positions[i]);
            model = glm::scale(model, glm::vec3(0.01f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
            wood.Draw(shaderObj);
        }
        for (int i = -2; i <= 1; i++)
        {
            for(int j = -2; j <= 1; j++)
            {
                model = glm::translate(glm::mat4(1.0f), glm::vec3(i * 3.52f, -0.05f, j * 3.72f));
                model = glm::scale(model, glm::vec3(0.1f));
                model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
                glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
                ground.Draw(shaderObj);
            }
        }
        shaderMirror.Use();
        model = glm::translate(glm::mat4(1.0f), glm::vec3(-0.51f, -0.3f, -3.71f));
        model = glm::scale(model, glm::vec3(0.0035f));
        model = glm::rotate(model, glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::rotate(model, glm::radians(3.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
        glUniformMatrix4fv(glGetUniformLocation(shaderMirror.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
        mirror.Draw(shaderObj);

        shaderStone.Use();
        glBindVertexArray(stoneVAO);
        glBindTexture(GL_TEXTURE_2D, stone);
        for (GLuint i = 0; i < vegetation.size(); i++)
        {
            model = glm::mat4(1.0f);
            model = glm::translate(model, vegetation[i]);
            model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
            glUniformMatrix4fv(glGetUniformLocation(shaderStone.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
            glDrawArrays(GL_TRIANGLES, 0, 6);
        }
        shaderSkyBox.Use();
        glDepthFunc(GL_LEQUAL);
        view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
        glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
        glBindVertexArray(skyboxVAO);
        glActiveTexture(GL_TEXTURE0);
        glUniform1i(glGetUniformLocation(shaderSkyBox.Program, "skybox"), 0);
        glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glDepthFunc(GL_LESS);


        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        shaderScreen.Use();
        glBindVertexArray(quadVAO);
        glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
        glDrawArrays(GL_TRIANGLES, 0, 6);

        glBindVertexArray(0);
        glfwSwapBuffers(window);
    }

    //------------------------------------------------------解绑---------------------------------------------------------
    glDeleteFramebuffers(1, &FBO);
    glDeleteVertexArrays(1, &lightVAO);
    glDeleteVertexArrays(1, &stoneVAO);
    glDeleteBuffers(1, &stoneVBO);
    glDeleteBuffers(1, &lightVBO);
    glfwTerminate();
    return 0;
}

GLuint loadCubemap(vector<const GLchar*> faces)
{
    GLuint textureID;
    glGenTextures(1, &textureID);

    int width, height;
    unsigned char* image;
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
    for (GLuint i = 0; i < faces.size(); i++)
    {
        image = SOIL_load_image(faces[i], &width, &height, 0, SOIL_LOAD_RGB);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
        SOIL_free_image_data(image);
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    return textureID;
}


GLuint loadTexture(const GLchar* path, GLboolean alpha)
{
    GLuint textureID;
    glGenTextures(1, &textureID);
    int width, height;
    unsigned char* image;
    if (alpha)
        image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGBA);
    else
        image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGB);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, alpha ? GL_RGBA : GL_RGB, width, height, 0, alpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, alpha ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, alpha ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
    SOIL_free_image_data(image);
    return textureID;
}

GLuint getAttachmentTexture()
{
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
    return textureID;
}

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void cameraMove()
{
    GLfloat currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;

    GLfloat cameraSpeed = 1.0f * deltaTime;
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(Camera_Movement(FORWARD), deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(Camera_Movement(BACKWARD), deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(Camera_Movement(LEFT), deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(Camera_Movement(RIGHT), deltaTime);
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (action == GLFW_PRESS)           //如果当前是按下操作
        keys[key] = true;
    else if (action == GLFW_RELEASE)            //松开键盘
        keys[key] = false;
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }
    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;
    lastX = xpos;
    lastY = ypos;
    
    GLfloat sensitivity = 0.05;
    xoffset *= sensitivity;
    yoffset *= sensitivity;
    
    camera.ProcessMouseMovement(xoffset, yoffset);
}

 

四、模型贴图(续)

接《OpenGL基础37:反射与折射》这一章中的模型贴图

前面提到了模型读取不到贴图的原因有三:

  1. 当初制作打包模型时,模型有绑定对应的贴图资源,但是绑定的相对路径有出入
  2. 没有贴图,是因为值单一,因此直接指定了值(这个开始考虑
  3. 当初制作打包模型时,模型并没有绑定贴图资源

其中①③已经解决,现在来解决②

这个值是在哪里指定的呢?又是怎么获取?就拿.obj这个格式来说,一般和.obj配套的还有一个.mtl文件,任意文本编辑器打开它,就可以看到如下的数据(其它格式的3D物体可能无法用普通的文本编辑器查看数据,但也一定能获取到):

红色方框括住的就是材质默认属性,分别为材质颜色(Ka)、漫发射(Kd)和镜面反射(Ks)

想想用过的游戏引擎(还是拿Unity3D举例),当模型没有贴图的时候,显示的是不是下面这样的效果:

这个很经典的灰白模型正是3D物体缺少贴图时的默认显示效果,而它的材质属性就是默认的上面指定的值,部分模型在设计时也干脆直接不给贴图,使用单一值来表示材质属性,又或者需要另外设计贴图,又或者是百搭型,总之这个是一定需要处理的,不然在没有贴图的情况下,你显示的物体将会是全黑!

之前完全没有考虑这一点,修改后的Mesh类和Model类如下,这下就更加完善了:

#ifndef MODEL_H
#define MODEL_H
#include<vector>
#include<string>
#include"Shader.h"
#include"Mesh.h"
#include<opengl/glew.h>
#include<SOIL.h>
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<assimp/Importer.hpp>
#include<assimp/scene.h>
#include<assimp/postprocess.h>
using namespace std;
GLint TextureFromFile(const char* path, string directory, string typeName);

class Model
{
    public:
        Model(const GLchar* path, const GLchar* texPath = "")
        {
            this->loadModel(path, texPath);
        }

        void Draw(Shader shader)
        {
            for (GLuint i = 0; i < this->meshes.size(); i++)
                this->meshes[i].Draw(shader);
        }

    private:
        ifstream myfile;
        vector<Mesh> meshes;
        string directory;
        vector<Texture> textures_loaded;
        void loadModel(string path, string texPath)
        {
            Assimp::Importer importer;
            const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
            if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
            {
                cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
                return;
            }
            this->directory = path.substr(0, path.find_last_of('/'));
            if (texPath != "")
                this->directory = texPath;
            myfile.open(texPath + "/index.txt");
            this->processNode(scene->mRootNode, scene);
            myfile.close();
        }

        //依次处理所有的场景节点
        void processNode(aiNode* node, const aiScene* scene)
        {
            for (GLuint i = 0; i < node->mNumMeshes; i++)
            {
                aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
                this->meshes.push_back(this->processMesh(mesh, scene));
            }
            for (GLuint i = 0; i < node->mNumChildren; i++)
                this->processNode(node->mChildren[i], scene);
        }

        //将所有原始的aimesh对象全部转换成我们自己定义的网格对象
        Mesh processMesh(aiMesh* mesh, const aiScene* scene)
        {
            vector<Vertex> vertices;
            vector<GLuint> indices;
            vector<Texture> textures;
            //处理顶点坐标、法线和纹理坐标
            for (GLuint i = 0; i < mesh->mNumVertices; i++)
            {
                Vertex vertex;
                glm::vec3 vector;
                vector.x = mesh->mVertices[i].x;
                vector.y = mesh->mVertices[i].y;
                vector.z = mesh->mVertices[i].z;
                vertex.Position = vector;
                vector.x = mesh->mNormals[i].x;
                vector.y = mesh->mNormals[i].y;
                vector.z = mesh->mNormals[i].z;
                vertex.Normal = vector;
                if (mesh->mTextureCoords[0])            //不一定有纹理坐标
                {
                    glm::vec2 vec;
                    //暂时只考虑第一组纹理坐标,Assimp允许一个模型的每个顶点有8个不同的纹理坐标,只是可能用不到
                    vec.x = mesh->mTextureCoords[0][i].x;
                    vec.y = mesh->mTextureCoords[0][i].y;
                    vertex.TexCoords = vec;
                }
                else
                    vertex.TexCoords = glm::vec2(0.0f, 0.0f);
                vertices.push_back(vertex);
            }
            //处理顶点索引
            for (GLuint i = 0; i < mesh->mNumFaces; i++)
            {
                aiFace face = mesh->mFaces[i];
                for (GLuint j = 0; j < face.mNumIndices; j++)
                    indices.push_back(face.mIndices[j]);
            }

            //处理材质
            Material mat;
            if (mesh->mMaterialIndex >= 0)
            {
                aiColor3D color;
                aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
                material->Get(AI_MATKEY_COLOR_AMBIENT, color);
                mat.Ka = glm::vec4(color.r, color.g, color.b, 1.0);
                material->Get(AI_MATKEY_COLOR_DIFFUSE, color);
                mat.Kd = glm::vec4(color.r, color.g, color.b, 1.0);
                material->Get(AI_MATKEY_COLOR_SPECULAR, color);
                mat.Ks = glm::vec4(color.r, color.g, color.b, 1.0);

                vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
                textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
                vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
                textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
                vector<Texture> reflectionMaps = this->loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_reflection");
                textures.insert(textures.end(), reflectionMaps.begin(), reflectionMaps.end());
                if (textures.size() == 0 && myfile.is_open())
                {
                    string name, type;
                    myfile >> type;
                    myfile >> name;
                    if (type.length() > 1)
                    {
                        aiString str("Img/" + name);
                        textures.push_back(loadTexturesFromPath(type, str));
                    }
                }
            }

            return Mesh(vertices, indices, textures, mat);
        }

        //遍历所有给定纹理类型的纹理位置,获取纹理的文件位置,然后加载生成纹理
        vector<Texture> loadMaterialTextures(aiMaterial* mat, int type, string typeName)
        {
            vector<Texture> textures;
            for (GLuint i = 0; i < mat->GetTextureCount((aiTextureType)type); i++)
            {
                aiString str;
                mat->GetTexture((aiTextureType)type, i, &str);
                textures.push_back(loadTexturesFromPath(typeName, str));
            }
            return textures;
        }

        Texture loadTexturesFromPath(string typeName, aiString path)
        {
            Texture texture;
            for (GLuint j = 0; j < textures_loaded.size(); j++)
            {
                if (std::strcmp(textures_loaded[j].path.C_Str(), path.C_Str()) == 0)
                    return textures_loaded[j];
            }
            texture.id = TextureFromFile(path.C_Str(), this->directory, typeName);
            texture.type = typeName;
            texture.path = path;
            this->textures_loaded.push_back(texture);
            return texture;
        }
};

GLint TextureFromFile(const char* path, string directory, string typeName)
{
    string filename = string(path);
    filename = directory + '/' + filename;
    cout << typeName << ":" << filename << endl;

    GLuint textureID;
    glGenTextures(1, &textureID);
    int width, height;
    unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
    SOIL_free_image_data(image);
    return textureID;
}
#endif

↑ Model.h  >>>>>>  Mesh.h ↓

#ifndef MESH_H
#define MESH_H
#include<vector>
#include<string>
#include<fstream>
#include<sstream>
#include"Shader.h"
#include<opengl/glew.h>
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<assimp/Importer.hpp>
#include<assimp/scene.h>
#include<assimp/postprocess.h>
using namespace std;
struct Vertex
{
    glm::vec3 Position;         //顶点
    glm::vec3 Normal;           //法线
    glm::vec2 TexCoords;        //贴图
};

struct Material
{
    glm::vec4 Ka;               //材质颜色
    glm::vec4 Kd;               //漫反射
    glm::vec4 Ks;               //镜面反射
};

struct Texture
{
    GLuint id;
    string type;                //贴图类型:漫反射贴图还是镜面贴图(后面还有法线贴图、错位贴图等)
    aiString path;              //贴图路径
};

class Mesh
{
    public:
        vector<Vertex> vertices;
        vector<GLuint> indices;             //索引
        vector<Texture> textures;
        Material mats;
        Mesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures, Material mats)
        {
            this->vertices = vertices;
            this->indices = indices;
            this->textures = textures;
            this->mats = mats;
            this->setupMesh();
        }

        void Draw(Shader shader)
        {
            GLuint diffuseNr = 1;
            GLuint specularNr = 1;
            GLuint reflectionNr = 1;
            for (GLuint i = 0; i < this->textures.size(); i++)
            {
                glActiveTexture(GL_TEXTURE0 + i);
                stringstream ss;
                string name = this->textures[i].type;
                if (name == "texture_diffuse")
                    ss << diffuseNr++;
                else if (name == "texture_specular")
                    ss << specularNr++;
                else if (name == "texture_reflection")
                    ss << reflectionNr++;
                name = name + ss.str();
                glUniform1i(glGetUniformLocation(shader.Program, name.c_str()), i);
                //这样的话,着色器中的纹理名就必须有一个对应的规范,例如“texture_diffuse3”代表第三个漫反射贴图
                //方法不唯一,这是最好理解/最简单的一种规范/写法
                glBindTexture(GL_TEXTURE_2D, this->textures[i].id);
            }

            glBindVertexArray(this->VAO);
            glBindBufferRange(GL_UNIFORM_BUFFER, 1, UBO, 0, sizeof(Material));
            glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);             //EBO绘制
            for (GLuint i = 0; i < this->textures.size(); i++)
            {
                glActiveTexture(GL_TEXTURE0 + i);
                glBindTexture(GL_TEXTURE_2D, 0);
            }
            glBindVertexArray(0);
        }

    private:
        GLuint VAO, VBO, EBO, UBO;
        void setupMesh()
        {
            glGenVertexArrays(1, &this->VAO);
            glGenBuffers(1, &this->VBO);
            glGenBuffers(1, &this->EBO);
            glGenBuffers(1, &this->UBO);

            glBindVertexArray(this->VAO);
            glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
            glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW);
            glBindBuffer(GL_UNIFORM_BUFFER, this->UBO);
            glBufferData(GL_UNIFORM_BUFFER, sizeof(mats), (void*)(&mats), GL_STATIC_DRAW);

            glEnableVertexAttribArray(0);
            //别忘了struct中内存是连续的
            //offsetof():获取结构体属性的偏移量
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
            glEnableVertexAttribArray(1);
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal));
            glEnableVertexAttribArray(2);
            glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords));

            glBindVertexArray(0);
        }
};
#endif

Model类里面添加了默认材质属性的读取,而对于Mesh类,则将默认数据通过UBO传递给着色器,并规定绑定点为1

因为值单一,因此着色器将不在初始化材质纹理为 sampler2D 类型,直接初始化为 vec4,除此之外,正好用上前面讲的UBO,直接将材质颜色(Ka)、漫发射(Kd)和镜面反射(Ks)的值包成一个uniform块,通过顶点着色器传给片段着色器

新物体着色器如下:

#version 420 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texture;
out vec2 texIn;
out vec3 normalIn;
out vec3 fragPosIn;
uniform mat4 model;             //模型矩阵
layout (std140, binding = 0) uniform Matrices
{
    mat4 view;                  //观察矩阵
    mat4 projection;            //投影矩阵
};
layout (std140, binding = 1) uniform Material
{
    vec4 Ambient;
	vec4 Diffuse;
	vec4 Specular;
};
out vec4 Mambient;
out vec4 Mdiffuse;
out vec4 Mspecular;
void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0);
    texIn = texture;
    fragPosIn = vec3(model * vec4(position, 1.0f));
    normalIn = mat3(transpose(inverse(model))) * normal;
    Mambient = Ambient;
    Mdiffuse = Diffuse;
    Mspecular = Specular;
}

///

#version 330 core
struct SunLight             //平行光
{
    vec3 direction;
    vec3 diffuse;
    vec3 specular;
};
struct PointLight           //点光源
{
    vec3 position;
    vec3 diffuse;
    vec3 specular;
    float k0, k1, k2;
};
struct SpotLight            //聚光灯
{
    vec3 position;
    vec3 direction;
    vec3 diffuse;
    vec3 specular;
    float k0, k1, k2;
    float cutOff, outCutOff;
};
in vec4 Mdiffuse;
in vec4 Mspecular;
in vec4 Mambient;
uniform SunLight sunLight;
uniform PointLight pointLights[3];
uniform SpotLight spotLight;
vec3 CalcSunLight(SunLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
out vec4 color;
uniform vec3 viewPos;
in vec2 texIn;
in vec3 fragPosIn;
in vec3 normalIn;
void main()
{
    vec3 result = 0.05f * vec3(Mambient);
    vec3 viewDir = normalize(viewPos - fragPosIn);
    vec3 normal = normalize(normalIn);
    result = result + CalcSunLight(sunLight, normal, viewDir);
    for (int i = 0; i <= 2; i++)
        result = result + CalcPointLight(pointLights[i], normal, fragPosIn, viewDir);
    result = result + CalcSpotLight(spotLight, normal, fragPosIn, viewDir);
    color = vec4(result, 1.0f);
}
 
vec3 CalcSunLight(SunLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    float diff = max(dot(normal, lightDir), 0.0f);
    vec3 diffuse = light.diffuse * (diff * vec3(Mdiffuse));
 
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16);
    vec3 specular = light.specular * (spec * vec3(Mspecular));
 
    return diffuse + specular;
}
 
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    float diff = max(dot(normal, lightDir), 0.0f);
    vec3 diffuse = light.diffuse * (diff * vec3(Mdiffuse));
 
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16);
    vec3 specular = light.specular * (spec * vec3(Mspecular));
 
    float dis = length(light.position - fragPos);
    float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * (dis * dis));
 
    diffuse *= attenuation;
    specular *= attenuation;
    return diffuse + specular;
}
 
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    float theta = dot(lightDir, normalize(-light.direction));
    float lightSoft = clamp((theta - light.outCutOff) / (light.cutOff - light.outCutOff), 0.0f, 1.0f);
 
    float diff = max(dot(normal, lightDir), 0.0f);
    vec3 diffuse = light.diffuse * (diff * vec3(Mdiffuse));
 
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16);
    vec3 specular = light.specular * (spec * vec3(Mspecular));
 
    float dis = length(light.position - fragPos);
    float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * (dis * dis));
 
    diffuse *= attenuation * lightSoft;
    specular *= attenuation * lightSoft;
    return diffuse + specular;
}

好了,这个时候如果你的物体完全没有贴图,显示出来就会是上面的效果,而不再是全黑

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值