前置: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也有一定的内存布局规则,不同的布局规则如下:

布局控制修饰符
shared Uniform 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                          // 0vec3 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                          // 144int 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);elseimage = 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;}elsevertex.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;
}

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

OpenGL基础40:Uniform缓冲相关推荐

  1. opengl之高级GLSL(2)-接口块,Uniform缓冲对象,Uniform块布局等

    接口块 当程序变得更大时,你希望发送的可能就不只是几个变量了,它还可能包括数组和结构体. 为了帮助我们管理这些变量,GLSL为我们提供了一个叫做接口块(Interface Block)的东西,来方便我 ...

  2. OpenGL基础35:帧缓冲(下)之简单图像处理

    在之前的章节,所有的物体都是中规中矩的显示的,只考虑了光照对物体的影响,那假设想要显示特殊的效果该怎么操作呢?例如马赛克风.将所有的物体都显示为黑白色,就像上世纪80年代的灰白电视一样,又或者说将整个 ...

  3. OpenGL基础34:帧缓冲(中)之附件

    在之前的章节,所有的物体都是中规中矩的显示的,只考虑了光照对物体的影响,那假设想要显示特殊的效果该怎么操作呢?例如马赛克风.将所有的物体都显示为黑白色,就像上世纪80年代的灰白电视一样,又或者说将整个 ...

  4. OpenGL基础33:帧缓冲(上)之离屏渲染

    在之前的章节,所有的物体都是中规中矩的显示的,只考虑了光照对物体的影响,那假设想要显示特殊的效果该怎么操作呢?例如马赛克风.将所有的物体都显示为黑白色,就像上世纪80年代的灰白电视一样,又或者说将整个 ...

  5. OpenGL基础31:混合

    在很多游戏场景中,地面往往都不是完全干净和平坦的,如果是草坪,那么肯定会有一些长得比较高的杂草,而对于沙地,往往总会有一些奇形怪状的石头等,一般来讲确实可以用模型,但是贴图也是一个不错的选择 一.Al ...

  6. OpenGL基础54:点光源阴影

    前置: OpenGL基础53:阴影映射(下) 一.万象阴影贴图 之前成功实现了平行光阴影,生成阴影贴图时使用的矩阵是正交矩阵,若是想要实现点光源的阴影效果,那么理论上只需要修改投影矩阵为透视矩阵就好了 ...

  7. OpenGL基础50:HDR

    一.HDR与LDR 由于显示器只能显示值为0.0到1.0间的颜色,因此当数据存储在帧缓冲(Framebuffer)中时,亮度和颜色的值也是默认被限制在0.0到1.0之间的,这个颜色范围即是LDR(Lo ...

  8. OpenGL基础44:光照矫正(上)

    对于openGL的API,倒是没有必要花太多时间,重点应该还是在着色器上 一.采样器.glActiveTexture和glBindTexture 在之前测试简单光照时可能出现的两个问题,尽管它们可能不 ...

  9. OpenGL基础43:抗锯齿

    一.走样与反走样 走样(Aliasing)就是锯齿化,反走样(Anti-aliasing)就是抗锯齿 只要玩过游戏,那么都应该对抗锯齿不陌生,不少游戏也都有关于抗锯齿的设置 如上图,放大的部分能很明显 ...

最新文章

  1. oracle管理 题库,Oracle数据库管理与开发习题集
  2. Siverlight5新功能/改进总结
  3. ExecuteScalar
  4. 迁移学习训练集准确率一直上不去_可以提高你的图像识别模型准确率的7个技巧...
  5. Android连续点击多次事件的实现
  6. 多重环境下web.config配置管理解决方案
  7. flexgdsgen
  8. 数据集永久下架,微软不是第一个,MIT 也不是最后一个
  9. swift - 添加定时器
  10. 人工智能的未来是强化学习_多主体强化学习与AI的未来
  11. 软件生成问候图片_设计师注意啦!欧美风双色图片一键生成的软件来啦!
  12. TC27x启动过程(2)-TC277
  13. Largest prime factor
  14. 互联网电影院5G让3D体验更流畅
  15. 关于使用硬盘对拷机后两硬盘UUID一样无法挂载问题
  16. 构建一个透明的activity
  17. Office2022个人家庭版
  18. Linux的Scp命令简单描述
  19. PCIe转28串口8串口CH384设计注意事项
  20. 步步高電子詞典10/24

热门文章

  1. python基础知识-Python基础知识
  2. python有哪些用途-python的主要用途是什么
  3. python编程入门 pdf-PYTHON游戏编程入门 PDF 下载
  4. python装饰器详解-python装饰器使用实例详解
  5. unicode编码java_JAVA转化Unicode编码
  6. php将abc转换成整形是什么意思,php强制转换类型的方法
  7. echarts 环形图鼠标悬停
  8. 设置ListCtrl列表控件其中某一行的字体和背景颜色
  9. A5如何备份oracle数据库,oracle的数据库的导入导出
  10. mysql索引动态维护_MySQL之——索引