延迟渲染总结
1 准备阶段
1. G-buffer创建
(1) 创建G-buffer帧缓冲
创建了G-buffer,因为接下来的操作要在该帧缓冲上进行,第三行我们在这里绑定到G-buffer帧缓冲上;
unsigned int gBuffer;
glGenFramebuffers(1, &gBuffer);//Gbuffer创建
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);//绑定该缓冲
如果没有第三行绑定到gbuffer缓冲上的话,会出现什么问题呢?
没有第三行的绑定的话后面的三个纹理附件就会绑定在默认的帧缓冲上,即前置缓冲器中,也就是屏幕显示帧缓冲上;这样的话就会把第一次没有光照的场景渲染到G-buffer中,而后面的三个纹理附件就没有任何数据,后面的光照计算也就没有数据了,会导致黑屏;
(2) 为G-buffer创建三个纹理附件(颜色附件)
- gPosition顶点位置信息;
- gNormal法线信息;
- gAlbedoSpec颜色信息和镜面强度值;
//position-位置信息作为一个例子
//1.纹理创建以及绑定
glGenTextures(1, &gPosition);
glBindTexture(GL_TEXTURE_2D, gPosition);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//2.将相应的纹理绑定到帧缓冲的对应缓冲区
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0);
(3) 用多渲染目标(MRT)方法将对应数据分配到各个颜色附件
多渲染目标技术:将每个像素的数据保存到帧缓冲不同的缓冲区中,使得这些缓冲区的数据由此可以进行后续的计算;
//MRT-Multiple Render Targets 多渲染目标技术
unsigned int attachments[3] =
{ GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1,GL_COLOR_ATTACHMENT2 };
//MRT代码的实现:表示缓冲区的个数和缓冲区的名称
glDrawBuffers(3, attachments);
运行片段着色器以后就可以将数据分别分配到各个颜色附件
2. 着色器,模型和光源设置
(1)着色器设置
几何着色器:存储所需的数据
光照着色器:进行光照计算
//载入着色器
Shader shaderGeometryPass("res/shader/g_buffer.vs","res/shader/g_buffer.fs");
Shader shaderLightingPass("res/shader/deferred_shading.vs","res/shader/deferred_shading.fs");
(2)模型设置
Model BB8("res/models/BB8 New/bb8.obj");
std::vector<glm::vec3> objectPositions;
objectPositions.push_back(glm::vec3(-1.0, -3.0, 0.0));
objectPositions.push_back(glm::vec3(2.0, -3.0, 0.0));
(3)光源设置
//多光源
std::vector<glm::vec3> lightPositions;
std::vector<glm::vec3> lightColors;
srand(13);
for (unsigned int i = 0; i < NR_LIGHTS; i++)
{
float xPos = ((rand() % 100) / 100.0) * 6.0 - 3.0;
float yPos = ((rand() % 100) / 100.0) * 6.0 - 4.0;
float zPos = ((rand() % 100) / 100.0) * 6.0 - 3.0;
lightPositions.push_back(glm::vec3(xPos, yPos, zPos));
float rColor = ((rand() % 100) / 200.0f) + 0.5;
float gColor = ((rand() % 100) / 200.0f) + 0.5;
float bColor = ((rand() % 100) / 200.0f) + 0.5;
lightColors.push_back(glm::vec3(rColor, gColor, bColor));
}
2 几何阶段
注意:在进行这个过程中先要绑定G-buffer;因为我们要把数据传给帧缓冲,从渲染的过程中并没有看到数据传到G-buffer中,shader装进去,G-buffer数据的传输在GPU中进行;
//1.激活着色器
shaderGeometryPass.use();
//2.设置PVM矩阵
shaderGeometryPass.setMat4("projection", projection);
shaderGeometryPass.setMat4("view", view);
for (unsigned int i = 0; i < objectPositions.size(); i++)
{
model = glm::mat4(1);
model = glm::translate(model, objectPositions[i]);
model = glm::scale(model, glm::vec3(0.02f));
shaderGeometryPass.setMat4("model", model);
BB8.Draw(shaderGeometryPass);
}
1 顶点着色器
注意:和一般的顶点着色器不一样的地方是要获取顶点在世界坐标系下的坐标,传给片元着色器,目的是为了将数据分配到不同的纹理附件;
纹理坐标和法线坐标和平常一样传给片元着色器中;
void main()
{
//--------------------------------------------------------------
//为了将数据分配到不同的纹理附件,我们需要计算顶点在世界坐标系下的坐标
vec4 worldPos = model * vec4(aPos, 1.0);
FragPos = worldPos.xyz;
//--------------------------------------------------------------
TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
Normal = normalMatrix * aNormal;
gl_Position = projection * view * worldPos;
}
2 片元着色器
- 顶点着色器的数据传到片元着色器中,通过下面的代码
in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;
- 从模型的纹理提取颜色和镜面高光值
///-----------------------=====================
//输出到3个颜色缓冲区
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
//=====================================
void main()
{
gPosition = FragPos;
gNormal = normalize(Normal);
//===========================
//从模型的贴图中提取相应的数据
gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb;
gAlbedoSpec.a = texture(texture_specular1, TexCoords).r;
//==================================
}
- 通过layout关键词将颜色,法线以及镜面高光值传到前面创建的G-buffer相应的颜色缓冲区内,这样就可以在光照阶段进行使用了;
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
3 光照处理阶段
1 cpp程序
///======================================
///1.将帧缓冲绑定到默认缓冲即屏幕显示的帧缓冲上
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//============================================
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//2.激活纹理通道,绑定纹理附件
shaderLightingPass.use() );//激活shaderLightingPass
glActiveTexture(GL_TEXTURE0);//激活纹理通道
glBindTexture(GL_TEXTURE0, gPosition);//绑定纹理附件
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, gNormal);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
///3.设置好光源的位置和颜色信息,用renderQuad()函数进行绘制
//num参数用于削弱最后光照结果,因为光源数量较多时叠加后亮度过高
shaderLightingPass.setFloat("num", NR_LIGHTS / (NR_LIGHTS / 3));
for (unsigned int i = 0; i < lightPositions.size(); i++)
{
shaderLightingPass.setVec3("lights[" + std::to_string(i) + "].Position", lightPositions[i]);
shaderLightingPass.setVec3("lights[" + std::to_string(i) + "].Color", lightColors[i]);;
}
shaderLightingPass.setVec3("viewPos", camera.Position);
renderQuad();
renderQuad()函数的功能:绘制铺满整个屏幕的四边形,通过shaderlightingpass着色器直接从三个纹理附件提取每个像素的数据进行绘制
2 shaderlightingpass着色器
(1)顶点着色器
直接传入四边形的顶点坐标和纹理坐标就可以了;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
(2) 片元着色器
从纹理附件中提取数据,然后进行phone的光照模型进行光照计算,从而得到最终的结果;
vec3 FragPos = texture(gPosition, TexCoords).rgb;
vec3 Normal = texture(gNormal, TexCoords).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb;
float Specular = texture(gAlbedoSpec, TexCoords).a
4 问题
1 延时是啥呢,和正向渲染有啥区别呢
延时其实指的是在渲染场景之后,再来进行光照计算,也就是延时光照计算在后期进行,
正向渲染的着色器采取物体+光照同时进行计算,也就是在渲染物体的时候,直接利用物体的物质,法线和颜色进行光照计算;
而延时渲染,是先渲染场景,在G-buffer中存储光照计算所需要的数据
2 延时渲染的缺点
- 不可以用混合技术,因此透明对象无法渲染
- 没法实现基于硬件实现的多重采样抗锯齿功能;因为渲染过程发生在上面的第二步,第二步只有一个样本,但是基于硬件实现的多重采样抗锯齿需要多个样本
- 需要高带宽的显卡
- 显卡不支持mrt技术,还可以实现延时渲染吗?
可以做,可以通过建立三个帧缓冲,分别存储位置,法线和延时信息,渲染三次场景提取信息并存储在三个附件中,但是不建议,这样显卡的负担会很大;
5 代码
/*
延迟渲染主要步骤:
1.几何处理阶段;
2.光照处理阶段;
*/
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <stb_image.h>
#include "Camera.h"//相机类
#include "Shader.h"//着色器类
#include "Model.h"//模型类,载入模型
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
void renderQuad();
void renderCube();
//光源数量
unsigned int NR_LIGHTS = 50;
const unsigned int SCR_WIDTH = 1280;
const unsigned int SCR_HEIGHT = 720;
Camera camera(glm::vec3(0.0f, -2.0f, 5.0f));
float lastX = (float)SCR_WIDTH / 2.0;
float lastY = (float)SCR_HEIGHT / 2.0;
bool firstMouse = true;
bool LightBox = false;
float deltaTime = 0.0f;
float lastFrame = 0.0f;
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Deferred Rendering", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glEnable(GL_DEPTH_TEST);
// G-buffer的创建
unsigned int gBuffer;
glGenFramebuffers(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
unsigned int gPosition, gNormal, gAlbedoSpec;
//position-位置信息
glGenTextures(1, &gPosition);
glBindTexture(GL_TEXTURE_2D, gPosition);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0);
//normal-法线信息
glGenTextures(1, &gNormal);
glBindTexture(GL_TEXTURE_2D, gNormal);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0);
//albedospec-颜色信息
glGenTextures(1, &gAlbedoSpec);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedoSpec, 0);
//深度附件
unsigned int rboDepth;
glGenRenderbuffers(1, &rboDepth);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);
//MRT-Multiple Render Targets 多渲染目标技术
unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1,GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments);
//判断帧缓冲是否创建成功
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "Framebuffer not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//载入着色器和模型
Shader shaderGeometryPass("res/shader/g_buffer.vs", "res/shader/g_buffer.fs");
Shader shaderLightingPass("res/shader/deferred_shading.vs", "res/shader/deferred_shading.fs");
Shader shaderLightBox("res/shader/deferred_light_box.vs", "res/shader/deferred_light_box.fs");
Model ourModel("res/models/character.obj");
std::vector<glm::vec3> objectPositions;
objectPositions.push_back(glm::vec3(-1.0, -3.0, 0.0));
objectPositions.push_back(glm::vec3(2.0, -3.0, 0.0));
shaderLightingPass.Use();
shaderLightingPass.SetInt("gPosition", 0);
shaderLightingPass.SetInt("gNormal", 1);
shaderLightingPass.SetInt("gAlbedoSpec", 2);
//多光源
std::vector<glm::vec3> lightPositions;
std::vector<glm::vec3> lightColors;
srand(13);
for (unsigned int i = 0; i < NR_LIGHTS; i++)
{
float xPos = ((rand() % 100) / 100.0) * 6.0 - 3.0;
float yPos = ((rand() % 100) / 100.0) * 6.0 - 4.0;
float zPos = ((rand() % 100) / 100.0) * 6.0 - 3.0;
lightPositions.push_back(glm::vec3(xPos, yPos, zPos));
float rColor = ((rand() % 100) / 200.0f) + 0.5;
float gColor = ((rand() % 100) / 200.0f) + 0.5;
float bColor = ((rand() % 100) / 200.0f) + 0.5;
lightColors.push_back(glm::vec3(rColor, gColor, bColor));
}
//渲染步骤
while (!glfwWindowShouldClose(window))
{
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
processInput(window);
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 model;
//几何处理阶段
shaderGeometryPass.Use();
shaderGeometryPass.SetMat4("projection", projection);
shaderGeometryPass.SetMat4("view", view);
for (unsigned int i = 0; i < objectPositions.size(); i++)
{
model = glm::mat4(1);
model = glm::translate(model, objectPositions[i]);
model = glm::scale(model, glm::vec3(0.4f));
shaderGeometryPass.SetMat4("model", model);
ourModel.Draw(shaderGeometryPass);
}
//光照处理阶段
glBindFramebuffer(GL_FRAMEBUFFER, 0);
shaderLightingPass.Use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gPosition);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, gNormal);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
shaderLightingPass.SetFloat("num", NR_LIGHTS/3);
for (unsigned int i = 0; i < lightPositions.size(); i++)
{
shaderLightingPass.SetVec3("lights[" + std::to_string(i) + "].Position", lightPositions[i]);
shaderLightingPass.SetVec3("lights[" + std::to_string(i) + "].Color", lightColors[i]);;
}
shaderLightingPass.SetVec3("viewPos", camera.Position);
renderQuad();
if (LightBox)
{
//绘制光源
glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
shaderLightBox.Use();
shaderLightBox.SetMat4("projection", projection);
shaderLightBox.SetMat4("view", view);
for (unsigned int i = 0; i < lightPositions.size(); i++)
{
model = glm::mat4(1);
model = glm::translate(model, lightPositions[i]);
model = glm::scale(model, glm::vec3(0.125f));
shaderLightBox.SetMat4("model", model);
shaderLightBox.SetVec3("lightColor", lightColors[i]);
renderCube();
}
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
unsigned int cubeVAO = 0;
unsigned int cubeVBO = 0;
//绘制立方体
void renderCube()
{
if (cubeVAO == 0)
{
float vertices[] = {
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f
};
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(cubeVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
}
//绘制铺满整个屏幕的矩形
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad()
{
if (quadVAO == 0)
{
float quadVertices[] = {
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
//是否显示光源 "Z"-显示 "X"-关闭显示
if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS)
LightBox = true;
if (glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS)
LightBox = false;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
g-buffer.fs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec3 FragPos;
out vec2 TexCoords;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
vec4 worldPos = model * vec4(aPos, 1.0);
FragPos = worldPos.xyz;
TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
Normal = normalMatrix * aNormal;
gl_Position = projection * view * worldPos;
}
g-buffer.vs
#version 330 core
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;
uniform sampler2D texture_diffuse1;
void main()
{
gPosition = FragPos;
gNormal = normalize(Normal);
gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb;
}
deferred_shading.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
deferred_shading.fs
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
uniform float num;
struct Light {
vec3 Position;
vec3 Color;
};
const int NR_LIGHTS = 50;
uniform Light lights[NR_LIGHTS];
uniform vec3 viewPos;
void main()
{
// 从G-buffer中提取数据
vec3 FragPos = texture(gPosition, TexCoords).rgb;
vec3 Normal = texture(gNormal, TexCoords).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb;
float Specular = 1.0f;
// 光照计算
vec3 lighting = Diffuse * 0.9;
vec3 viewDir = normalize(viewPos - FragPos);
for(int i = 0; i < NR_LIGHTS; ++i)
{
//环境光
vec3 ambient = Diffuse * 0.01;
// 漫反射
vec3 lightDir = normalize(lights[i].Position - FragPos);
vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * lights[i].Color;
// 镜面高光
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0);
vec3 specular = lights[i].Color * spec * Specular;
lighting += ambient + diffuse + specular;
}
FragColor = vec4(lighting/num, 1.0);
}