想想之前代码,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)开始处的字节偏移量,变量对齐的字节偏移一定等于它的基线对齐的倍数
类型 | 布局规范 |
像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:反射与折射》这一章中的模型贴图
前面提到了模型读取不到贴图的原因有三:
- 当初制作打包模型时,模型有绑定对应的贴图资源,但是绑定的相对路径有出入
- 没有贴图,是因为值单一,因此直接指定了值(这个开始考虑)
- 当初制作打包模型时,模型并没有绑定贴图资源
其中①③已经解决,现在来解决②
这个值是在哪里指定的呢?又是怎么获取?就拿.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;
}
好了,这个时候如果你的物体完全没有贴图,显示出来就会是上面的效果,而不再是全黑