一、泛光(Bloom)

如果一个物体的颜色值接近1.0,怎么确定它是否是光源呢?毕竟一张白纸它的颜色也可以是1.0

在此之前,分辨光源往往都是靠的经验,例如你知道这个物体它是个路灯,又或者说根据周围的光照来判断光的中心等等,其实只要使用的模型,都没什么大的问题,而很多游戏中为了体现出更真实的光源,都会给光源加上泛光,以体现出光源向四周发散的光芒,提升光照的效果:

泛光效果只是一个渲染技巧,本质上就是一个模糊处理,它并不能反映出真实的物理世界,但尽管如此,一个完美的算法依旧可以大幅提升画面的效果,泛光的实现可能相对于之前要复杂一些,但是整体难度并没有太高,大致的流程如下:

  1. 走正常渲染流程,将渲染得到的结果放入颜色缓冲A中,并同时提取亮色(光源)放入颜色缓冲B中
  2. 对于从颜色缓冲B得到的亮色纹理,多次进行模糊处理,最终可以得到一个只有光源且带了泛光效果的纹理C
  3. 混合纹理C和纹理A

很多地方在讲泛光时都会提到HDR,是因为在HDR场景中能够更精准的提取亮色,但是不依赖HDR也可以,例如你可以直接提取所有的放射光贴图

minecraft的光影材质包

二、多渲染目标(MRT、Multiple Render Targets)

之前是将渲染的结果存储在一张纹理中,然后紧接着对着整张纹理进行二次处理,例如HDR和模糊之类的,但要实现泛光就需要渲染两张纹理,一张为正常的渲染结果,另一张为提取的亮色

确实可以渲染两次,但是有个叫做多渲染目标的小技巧,可以直接一次渲染出两个结果:

  1. 为什么一定要筛出亮色:为了后面进行模糊采样时,只对亮色进行采样
  2. 为什么需要特意将亮色处理到一张纹理中:后面在计算模糊时,需要对这张只有亮色的纹理进行多次的着色处理,如果不分开,每次在处理时都需要重新计算一次光照以筛出亮色,不然上一次模糊的结果会混到场景中

和顶点着色器的位置值一样,片段着色器的输出也可以设定位置值:

layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec4 lightColor;
void main()
{//……fragColor = vec4(…);        //渲染到第一张纹理lightColor = vec4(…);       //渲染到第二张纹理
}

对于FBO,只要对应绑定两个颜色缓冲就可以了,当把一个纹理链接到帧缓冲的颜色缓冲上时,可以指定一个颜色附件,之前一直都是GL_COLOR_ATTACHMENT0,这次分别指定两个颜色附件,就要用到GL_COLOR_ATTACHMENT1了

使用多个像素着色器输出的必要条件是:有多个颜色缓冲附加到了当前绑定的帧缓冲对象上,因此别忘了使用glDrawBuffers指定要绘制到的颜色缓冲区的列表

void main()
{//……GLuint FBO, RBO;glGenFramebuffers(1, &FBO);glBindFramebuffer(GL_FRAMEBUFFER, FBO);GLuint texNum = 2;GLuint* textureColorBuffer = getAttachmentTexture(texNum);glGenRenderbuffers(1, &RBO);glBindRenderbuffer(GL_RENDERBUFFER, RBO);glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, WIDTH, HEIGHT);glBindRenderbuffer(GL_RENDERBUFFER, 0);for (GLuint i = 0; i < texNum; i++)glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textureColorBuffer[i], 0);GLuint attachments[5] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4 };glDrawBuffers(texNum, attachments);glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RBO);if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;glBindFramebuffer(GL_FRAMEBUFFER, 0);//……
}GLuint* getAttachmentTexture(GLuint num)
{GLuint *textureID;textureID = new GLuint[num];glGenTextures(num, textureID);for (GLuint i = 0; i < num; i++){glBindTexture(GL_TEXTURE_2D, textureID[i]);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);}glBindTexture(GL_TEXTURE_2D, 0);return textureID;
}

当渲染到这个帧缓冲的时候,只要着色器对布局使用location修饰符,fragment就会写入对应的颜色缓冲

提取亮色的逻辑如下,关于需要注意的细节:

  • 在判断一个点亮度是否大于某个值时,最好用对应的灰度值,灰度值并不是简单的RGB三维加在一起除以3,而是对RGB使用了3个不同的比例参数以进行计算,这个特殊值(0.2126, 0.7152, 0.0722)是根据人眼根据光线的感知差得出的
  • 别忘了考虑纯色光源,(0, 0, 1)会是一个纯蓝色的光源,而(0.8, 0.8, 0.8)却有可能只是被照亮的物体表面,当然还有放射光贴图,很多模型上的光源都是用它来展现的
fragColor = vec4(result, 1.0f);
float brightness = dot(fragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
if(brightness > 1.0 || vec3(texture(material.texture_emission1, texCoords)) != vec3(0.0))lightColor = vec4(fragColor.rgb, 1.0);

三、高斯模糊

之前用Kernel矩阵实现过一个非常简单的模糊效果,也就是取图像中所有周围像素的均值,现在可以尝试用一个更好的模糊算法:高斯模糊

高斯模糊的采样曲线如下,片段与当前采样点的距离越小,采样比重就越大,如果考虑精度,整体采样的范围大概是 32 x 32,也就是每一个片段的最终颜色,都是从周围 32 x 32 范围内的纹理中采样决定

这和定义一个 32 x 32 的Kernel矩阵一样,但是这一步计算复杂度貌似太高了,因为每个纹理点都需要采样 1024 次,整体复杂度是  ,其中p为分辨率、r为高斯模糊采样半径,因此需要找一个优化的方法:

考虑到高斯模糊最是一种图像滤波器,它是用正态分布计算模糊模板,并使用该模板与原图像做卷积运算达到模糊图像的目的的,而其中的正态分布正是高斯函数,这也正对应了上面那张“钟形函数”的图片,对应的函数公式为:

其中,σ 是正态分布的标准差,σ 值越大,图像越模糊,x 和 y 决定了模糊半径,这个二维函数有个很好的性质:它可以转换为两个一维函数的乘积:

这就意味着,高斯模糊可以水平垂直交替分开处理,具体来说:我们准备两个帧缓冲C和D专门来做这个算法,先在C中进行垂直采样,处理完毕放入D缓冲,紧接着在D中进行横向采样

关于具体的算法:

因为 σ 可以给定,所以可以直接确定每个采样比例的常数值,着色器代码如下:

#version 330 core
out vec4 color;
in vec2 texIn;
uniform sampler2D gaussTexture;
uniform bool horizontal;
uniform float weight[5] = float[](0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
void main()
{             vec2 tex_offset = 1.0 / textureSize(gaussTexture, 0);vec3 result = texture(gaussTexture, texIn).rgb * weight[0]; if(horizontal){for(int i = 1; i < 5; ++i){result += texture(gaussTexture, texIn + vec2(tex_offset.x * i, 0.0)).rgb * weight[i];result += texture(gaussTexture, texIn - vec2(tex_offset.x * i, 0.0)).rgb * weight[i];}}else{for(int i = 1; i < 5; ++i){result += texture(gaussTexture, texIn + vec2(0.0, tex_offset.y * i)).rgb * weight[i];result += texture(gaussTexture, texIn - vec2(0.0, tex_offset.y * i)).rgb * weight[i];}}color = vec4(result, 1.0);
}

然后就是主代码,想要进行多次高斯模糊,就要在两个帧缓冲之间来回倒腾,这种感觉有点像某些需要两个相同容器的算法一样,一个容器中计算的结果放入另一个容器,然后另一个容器继续相同的算法,结果再放入第一个容器……

例子中,先是用提取出来的亮区纹理所在的帧缓冲作为第一个帧缓冲,后面每次循环再根据打算渲染的是水平还是垂直来绑定两个缓冲其中之一,并将另一个绑定为纹理进行模糊,代码中总共模糊的次数是5次(=垂直水平各5次)

在使用相同的高斯权重的情况下,进行高斯模糊的次数越多,模糊的强度就越大

GLuint gaussFBO[2];
GLuint* gaussColorbuffers = getAttachmentTexture(2);
glGenFramebuffers(2, gaussFBO);
for (GLuint i = 0; i < 2; i++)
{glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[i]);glBindTexture(GL_TEXTURE_2D, gaussColorbuffers[i]);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gaussColorbuffers[i], 0);if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);//……
while (!glfwWindowShouldClose(window))
{//……GLuint amount = 10;bool horizontal = true;bool first_iteration = true;shaderBlur.Use();for (GLuint i = 0; i < amount; i++){glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[horizontal]);glUniform1i(glGetUniformLocation(shaderBlur.Program, "horizontal"), horizontal);glActiveTexture(GL_TEXTURE1);glUniform1i(glGetUniformLocation(shaderBlur.Program, "gaussTexture"), 1);glBindTexture(GL_TEXTURE_2D, first_iteration ? textureColorBuffer[1] : gaussColorbuffers[!horizontal]);renderQuad();horizontal = !horizontal;if (first_iteration)first_iteration = false;}glBindFramebuffer(GL_FRAMEBUFFER, 0);//……
}

好了,这下复杂度就由 32 * 32 降到了 32 + 32,也就是 ,考虑到傅里叶变换的过程,如果采样半径足够大,还可以FFT优化,复杂度再降到 ,不过这就不属于这篇文章的范畴了

四、纹理混合

搞定了高斯模糊后,最后就可以得到两个最终需要的纹理:标准渲染结果,以及进行高斯模糊处理过的亮色纹理

混合就可以了,这是最后一步显示的着色器:

#version 330 core
in vec2 texIn;
out vec4 color;
uniform sampler2D screenTexture;
uniform sampler2D bloomTexture;
const float offset = 1.0 / 500;
void main()
{vec3 bloomColor = texture(bloomTexture, texIn).rgb;vec3 result = col + bloomColor;result = vec3(1.0) - exp(-result);result = pow(result, vec3(1.0 / 2.2));color = vec4(result, 1.0);
}

放下完整的主代码和效果吧:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include"Camera.h"
#include"Light.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;
bool openSpotLight = 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(GLuint num);
GLuint getMultiSampleTexture(GLuint samples);           //MSAA几重采样
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void cameraMove();
void renderQuad();
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", "ObjGShader.geom");Shader shaderObjToDebug("ObjNormalVShader.vert", "ObjNormalFShader.frag", "ObjNormalGShader.geom");Shader shaderObjWithoutTex("ObjVShaderWithoutTex.vert", "ObjFShaderWithoutTex.frag");Shader shaderLight("LightVShader.vert", "LightFShader.frag");Shader shaderSkyBox("SkyboxVShader.vert", "SkyboxFShader.frag");Shader shaderScreen("ScreenVShader.vert", "ScreenFShader.frag");Shader shaderBlur("GaussVShader.vert", "GaussFShader.frag");//------------------------------------------------------数据定义---------------------------------------------------------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 wallPositions[] ={glm::vec3(-1, 0.3, -0.802),glm::vec3(-5.07, 0.3, -0.8),glm::vec3(3.07, 0.3, -0.8),};glm::vec3 pointLightPositions[] = {glm::vec3(-4.0f, 2.6f, -0.25f),glm::vec3(1000000.0f, 0.8f, 2.0f),glm::vec3(1000000.0f, 0.8f, 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};//------------------------------------------------------VAO & VBO---------------------------------------------------------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);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);//------------------------------------------------------UBO---------------------------------------------------------GLuint UBO;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 texNum = 2;GLuint* textureColorBuffer = getAttachmentTexture(texNum);glGenRenderbuffers(1, &RBO);glBindRenderbuffer(GL_RENDERBUFFER, RBO);glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, WIDTH, HEIGHT);glBindRenderbuffer(GL_RENDERBUFFER, 0);for (GLuint i = 0; i < texNum; i++)glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textureColorBuffer[i], 0);GLuint attachments[5] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4 };glDrawBuffers(texNum, attachments);glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RBO);if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;glBindFramebuffer(GL_FRAMEBUFFER, 0);GLuint gaussFBO[2];GLuint* gaussColorbuffers = getAttachmentTexture(2);glGenFramebuffers(2, gaussFBO);for (GLuint i = 0; i < 2; i++){glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[i]);glBindTexture(GL_TEXTURE_2D, gaussColorbuffers[i]);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gaussColorbuffers[i], 0);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_MULTISAMPLE);glEnable(GL_STENCIL_TEST);glEnable(GL_DEPTH_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 wall("Object/wall/stonewall.fbx", "Object/wall");Model lightObj("Object/light/file.fbx", "Object/light");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);//------------------------------------------------------实例化---------------------------------------------------------glm::mat4* modelMatrices;modelMatrices = new glm::mat4[1000];glm::mat4 model = glm::mat4(1.0f);for (GLuint i = 0; i <= 7; i++){model = glm::translate(glm::mat4(1.0f), positions[i]);model = glm::scale(model, glm::vec3(0.01f));modelMatrices[i] = model;}wood.UpdateModelMatrix(modelMatrices, 8);GLint groundIndex = 0;for (int i = -1; i <= 2; i++){for (int j = 0; j <= 2; 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));modelMatrices[groundIndex++] = model;}}ground.UpdateModelMatrix(modelMatrices, groundIndex);for (GLuint i = 0; i <= 2; i++){model = glm::translate(glm::mat4(1.0f), wallPositions[i]);model = glm::scale(model, glm::vec3(0.15, 0.3, 0.15));model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));modelMatrices[i] = model;}wall.UpdateModelMatrix(modelMatrices, 3);model = glm::translate(glm::mat4(1.0f), glm::vec3(-4.0f, 2.3f, -0.25f));model = glm::scale(model, glm::vec3(0.015, 0.015, 0.015));model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));modelMatrices[0] = model;lightObj.UpdateModelMatrix(modelMatrices, 1);delete[] modelMatrices;Light light;light.AddSunLight(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3(0.03f, 0.03f, 0.03f), glm::vec3(0.036f, 0.036f, 0.036f));for (int i = 0; i <= 2; i++)light.AddPointLight(glm::vec3(pointLightPositions[i].x, pointLightPositions[i].y, pointLightPositions[i].z), glm::vec3(0.6, 0.6, 0.6), glm::vec3(0.45, 0.45, 0.45));light.AddSpotLight(glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z), glm::vec3(camera.Front.x, camera.Front.y, camera.Front.z), glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f));//------------------------------------------------------渲染ing---------------------------------------------------------while (!glfwWindowShouldClose(window)){glfwPollEvents();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();glm::mat4 view = camera.GetViewMatrix();glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);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);glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);shaderObj.Use();light.spotLight.clear();if (openSpotLight){light.AddSpotLight(glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z), glm::vec3(camera.Front.x, camera.Front.y, camera.Front.z),glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f));}light.AppAllLightToShader(shaderObj.Program);glUniform3f(glGetUniformLocation(shaderObj.Program, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z);wall.Draw(shaderObj, 3);wood.Draw(shaderObj, 8);ground.Draw(shaderObj, groundIndex + 1);lightObj.Draw(shaderObj, 1);//--------------------------------------------------天空盒-----------------------------------------------------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_TEXTURE1);glUniform1i(glGetUniformLocation(shaderSkyBox.Program, "skybox"), 1);glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);glDepthFunc(GL_LESS);//--------------------------------------------------高斯模糊-----------------------------------------------------GLuint amount = 10;bool horizontal = true;bool first_iteration = true;shaderBlur.Use();for (GLuint i = 0; i < amount; i++){glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[horizontal]);glUniform1i(glGetUniformLocation(shaderBlur.Program, "horizontal"), horizontal);glActiveTexture(GL_TEXTURE1);glUniform1i(glGetUniformLocation(shaderBlur.Program, "gaussTexture"), 1);glBindTexture(GL_TEXTURE_2D, first_iteration ? textureColorBuffer[1] : gaussColorbuffers[!horizontal]);renderQuad();horizontal = !horizontal;if (first_iteration)first_iteration = false;}glBindFramebuffer(GL_FRAMEBUFFER, 0);//--------------------------------------------------默认帧缓冲--------------------------------------------------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();glActiveTexture(GL_TEXTURE1);glUniform1i(glGetUniformLocation(shaderScreen.Program, "screenTexture"), 1);glBindTexture(GL_TEXTURE_2D, textureColorBuffer[0]);glActiveTexture(GL_TEXTURE2);glUniform1i(glGetUniformLocation(shaderScreen.Program, "bloomTexture"), 2);glBindTexture(GL_TEXTURE_2D, gaussColorbuffers[!horizontal]);renderQuad();glfwSwapBuffers(window);}//------------------------------------------------------解绑---------------------------------------------------------glDeleteFramebuffers(1, &FBO);glfwTerminate();return 0;
}GLuint quadVAO = 0;
GLuint quadVBO;
void renderQuad()
{if (quadVAO == 0){GLfloat 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);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);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);}glBindVertexArray(quadVAO);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);glBindVertexArray(0);
}GLuint getMultiSampleTexture(GLuint samples)
{GLuint texture;glGenTextures(1, &texture);glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, WIDTH, HEIGHT, GL_TRUE);glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);return texture;
}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_SRGB, 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* getAttachmentTexture(GLuint num)
{GLuint *textureID;textureID = new GLuint[num];glGenTextures(num, textureID);for (GLuint i = 0; i < num; i++){glBindTexture(GL_TEXTURE_2D, textureID[i]);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);}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 (key == GLFW_KEY_TAB && action == GLFW_PRESS)openSpotLight = !openSpotLight;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基础51:泛光相关推荐

  1. [转]OpenGL基础技术讲座--发展历史

    OpenGL基础技术讲座--发展历史 第一讲 OpenGL 的发展历史 人们对三维图形技术的研究已经经历了一个很长的历程,而且涌现了许多三维图形开发工具,其中SGI公司推出的GL(Graphics L ...

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

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

  3. OpenGL基础50:HDR

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

  4. OpenGL基础图形编程

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  5. OpenGL基础知识介绍和简单使用

    OpenGL基础知识介绍 OpenGL简介 OpenGL 专业词解析 1.OpenGL上下文[context] 2.渲染 3.顶点数组和顶点缓冲区 4.着色器程序Shader 5.顶点着色器(Vert ...

  6. OpenGL基础编程

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  7. 【转】OpenGL基础图形编程(一)

    原文:http://blog.chinaunix.net/uid-20638550-id-1909183.html  分类: 一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 ...

  8. OpenGL基础图形编程(转)

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  9. OpenGL基础图形编程(一)

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

最新文章

  1. 有序数组二分查找java_详解Java数据结构和算法(有序数组和二分查找)
  2. Amh/Nginx更改网站根目录
  3. 从零开始学 Web 之 BOM(四)client系列
  4. java css是什么_Java 之 CSS
  5. Linux 中挖矿病毒处理过程
  6. DFS BFS 总结
  7. Linux 命令之 iwlist 命令-从无线网卡获取更详细的无线信息
  8. VR--VIVE-SteamVR API
  9. java 夏令时区_如何获得另一个时区的夏令时状态
  10. Linux常用指令---快捷键
  11. 我在30岁之前做了三件骄傲的事
  12. GPS定位可以使用物联卡吗?信号稳定吗?
  13. java Array入门
  14. 遗传算法 python 简书_基本遗传算法介绍
  15. matlab中syms空间问题
  16. Win7-VirsualBox下学习Ubuntu--Ubuntu和Win7共享文件夹
  17. 应用程序图标变成白色
  18. 分形造型的常用模型,C构造简单的IFS图形
  19. 计算机维修实操,wt初级计算机维修工操作题(二).doc
  20. python 如果文件夹不存在则新建

热门文章

  1. python软件下载3版本-Python3.9下载
  2. python工资这么高为什么不学-为什么学Python的人越来越多?
  3. python和c 的区别-对比平台--C和Python之间的区别
  4. 只有10万元预算买畅销SUV自动车型您选谁?
  5. python递归函数入门教程_Python递归函数
  6. python输入年月日输出年月日_Python网站浪漫表白神器那些鲜为人知的技术
  7. 使用 CSS 模拟鼠标点击交互
  8. Flash Media Live Encoder 使用帮助
  9. 2020PHP版本,phpmaker2020
  10. 2021江西高考成绩查询方式6,2021年江西高考成绩6月23日公布 多种查分方式