一、HDR与LDR

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

如果光源足够的多,越或者某个光源亮度非常的高,那么对当下的环境,就有可能遇到这样的情况:

图中的纹理已经变得难以分辨了,丢失了特别多的细节:在计算片段亮度的过程中,这些片段中超过1.0的亮度或者颜色值都会被约束在1.0,这样一大段的区域都会是单纯的白色

一个很容易想到的解决方法是:调整光照强度,但是这个太过于被动,并且一旦使用了不切实际的光照参数,当光源的数量发生变化时就可能需要再次进行调整,所以这个方法就不要再考虑了

正确的方法是:在计算光照时使用更高的颜色范围HDR(High Dynamic Range),也就是高动态范围,需要显示时再将所有HDR值转换成在[0.0, 1.0]范围内的LDR值。这种转换HDR值到LDR值得过程也叫做色调映射(Tone Mapping)

HDR原本只是被运用在摄影上,摄影师对同一个场景采取不同曝光拍多张照片,捕捉大范围的色彩值。这些图片被合成为HDR图片,从而综合不同的曝光等级使得大范围的细节可见

一个非常完美的HDR&映射算法可以使得亮的东西非常亮,暗的东西非常暗,并且充满细节

二、浮点帧缓冲

当帧缓冲使用了一个标准化的定点格式(例如之前的GL_RGB)为其颜色缓冲的内部格式时,OpenGL就会在将这些值存入帧缓冲前自动将其约束到0.0到1.0之间,而当一个帧缓冲的颜色缓冲的内部格式被设定成了 GL_RGB16F、GL_RGBA16F、GL_RGB32F 或者 GL_RGBA32F 时,这些帧缓冲就被叫做浮点帧缓冲(Floating Point Framebuffer),浮点帧缓冲可以存储超过0.0到1.0范围的浮点值,所以非常适合HDR渲染

想要创建一个浮点帧缓冲,只需要改变颜色缓冲的内部格式参数就可以了

glBindTexture(GL_TEXTURE_2D, colorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);

到此第一步就搞定了,可以放心渲染场景到这个帧缓冲中

如果对帧缓冲不了解,可以回去参考《OpenGL基础34:帧缓冲(中)之附件》和 《OpenGL基础33:帧缓冲(上)之离屏渲染》,除了上面的颜色缓冲内部格式参数要改下外,写法没有任何变化

完整的主代码:

#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 getMultiSampleTexture(GLuint samples);           //MSAA几重采样
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", "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");//------------------------------------------------------数据定义---------------------------------------------------------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 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};//------------------------------------------------------VAO & VBO---------------------------------------------------------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);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 textureColorBuffer = getAttachmentTexture();glGenRenderbuffers(1, &RBO);glBindRenderbuffer(GL_RENDERBUFFER, RBO);glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, WIDTH, HEIGHT);glBindRenderbuffer(GL_RENDERBUFFER, 0);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorBuffer, 0);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);//------------------------------------------------------设置状态、加载模型和天空盒---------------------------------------------------------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);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);glActiveTexture(GL_TEXTURE1);glUniform1i(glGetUniformLocation(shaderScreen.Program, "screenTexture"), 1);glBindTexture(GL_TEXTURE_2D, textureColorBuffer);glDrawArrays(GL_TRIANGLES, 0, 6);glBindVertexArray(0);glfwSwapBuffers(window);}//------------------------------------------------------解绑---------------------------------------------------------glDeleteFramebuffers(1, &FBO);glfwTerminate();return 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 textureID;glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_2D, textureID);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);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基础45:光照矫正(下)之Gamma矫正》:

在进行Gamma矫正的时候,或许可能遇到过这样的渲染错误:

可以看出天空盒的纹理出现了“断层”:出现这种情况的原因是在定点格式的帧缓冲中进行Gamma重校时,颜色值不但会被限制在[0, 1]之间,还会出现精度丢失,从而在默认帧缓冲中再次进行Gamma矫正时,整张纹理就变得“粗糙”了,对于颜色比较深的物体或者纹理复杂的物体可能还不怎么能看出来,但是对于天空盒这种单色调的就非常明显

因此,最好是在浮点帧缓冲中进行Gamma重校,或者能有方法保证精度(例如在同一帧缓冲中直接完成重校和校正)

三、色调映射

接下来就是色调映射(Tone Mapping)了,它能将HDR颜色值转入[0, 1]范围内以正确显示在屏幕上,色调映射的算法很多,最终效果的风格也不同,但通常都会伴有特定的风格的色平衡(Stylistic Color Balance),也就是同时校正图像色偏、过饱和或饱和度不足的情况

和《OpenGL基础35:帧缓冲(下)之简单图像处理》一样,也可以将色调映射理解为一种特殊的图像处理方法

Reinhard色调映射:

最简单的映射算法,平均地将所有亮度值分散到LDR上:

曝光(Exposure)度色调映射算法:

HDR图片包含在不同曝光等级的细节,如果有一个场景要展现日夜交替,往往会在白天使用低曝光,在夜间使用高曝光,就像人眼调节方式一样,随着曝光度越来越低,高光部分的细节越来越清晰,当然,低光部分的细节就越来越模糊了

对应的着色器代码如下(带上了Gamma矫正):

#version 330 core
in vec2 texIn;
out vec4 color;
uniform sampler2D screenTexture;
const float offset = 1.0 / 500;
void main()
{//可以在这里计算Kernel矩阵vec3 result = vec3(1.0) - exp(-col);result = pow(result, vec3(1.0 / 2.2));color = vec4(result, 1.0);
}

四、扩展

更好/更多的色调映射算法:

这些算法各有长短,一些色调映射算法倾向于特定的某种颜色/强度,也有一些算法同时显示低高曝光颜色从而能够显示更加多彩和精细的图像,也有一些技巧被称作自动曝光调整(Automatic Exposure Adjustment)或是人眼适应(Eye Adaptation)技术,它能够检测前一帧场景的亮度并且缓慢调整曝光参数以模仿人眼,使得场景在黑暗区域逐渐变亮、在明亮区域逐渐变暗

当然上面提到的最简单的两个色调映射算法,已经可以起到一个还算不错的效果了

泛光:

可以利用高斯模糊使得光源带有泛光的效果以更加的真实,当然了:在筛出所有的光源前可以进行色调映射以保证颜色值为1的点一定是光源

HDR和抗锯齿的冲突:

可能会有比较少的一部分游戏玩家会遇到这样的问题:有些游戏/电脑无法同时应用HDR和抗锯齿

NV采用了OpenEXR做为HDR运算的缓存格式,GeForce 6/7系列显卡都提供了对OpenEXR的16位浮点(FP16)贴图、过滤、混合、存储支持(即HDR),然而在DX9C模式下运行FP16时,会占用到原本属于MSAA(多重采样抗锯齿)的缓冲区域,使得在开启HDR效果后无法进行AA处理。这也就是为什么几乎所有的FP16游戏无法同时支持FSAA和HDR的原因,如果强行打开FP16 HDR+AA则会出现贴图破碎混乱的问题

这个只需要了解以下就好,不必要深究

OpenGL基础50:HDR相关推荐

  1. OpenGL基础23:平行光与点光源

    前面几章主要是针对物体,现在开始针对光源! 一.平行光 在 OpenGL基础18:光照基础 这一章里面讲了几种常见光源,先看平行光吧 一个很好的例子就是太阳光,因为离我们的距离过远,所以太阳光的特点就 ...

  2. OpenGL基础16:视角

    一.欧拉角 三种欧拉角: 俯仰角(Pitch):沿x轴旋转的角,从上往下看的角 偏航角(Yaw):沿y轴旋转的角,从左往右看的角 滚转角(Roll):沿z轴旋转的角(对于摄像机而言,一般不关心这个) ...

  3. OpenGL基础知识(四)

    今天将继续修改OpenGL基础知识(三)中的例子,并进行了一些简单的动画绘制.在编写代码之前先介绍一下函数glutTimerFunc(unsigned int msecs,void (*func) ( ...

  4. 【OpenGL】计算机图形学实验一:OpenGL基础实验(实验环境的熟悉、简单图形的绘制和输出)

    实验一:OpenGL基础实验 (实验环境的熟悉.简单图形的绘制和输出) 1.实验目的和要求 学习基本的OpenGL图形绘制和输出函数,掌握使用基于C++  OpenGL开发图形程序的流程. 2.实验设 ...

  5. OpenGL基础图形编程

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

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

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

  7. OpenGL基础编程

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

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

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

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

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

最新文章

  1. OKR管理和绩效考核有什么不一样呢?
  2. android 常用小功能(第二版)
  3. 基于脑电和特征加权阶段训练的驾驶员疲劳状态估计
  4. MySQL-索引优化篇(3)_利用索引优化锁
  5. eclipse工作空间在哪里配置?
  6. 14行代码满分:1037 在霍格沃茨找零钱 (20分)
  7. 缺氧游戏计算机,缺氧PC最低什么配置一览 你觉得高吗
  8. JavaScript原生对象属性和方法详解——Array对象
  9. C#中另类自定义公式计算 字符串转换为计算公式,并得出计算结果【转载】
  10. 计算机维修的税收编码,维修服务在税收分类编码是什么?
  11. 设计三极管放大电路有哪些技巧?尤其是假设
  12. C++11新特性内存模型总结详解--一篇秒懂
  13. 怎样使用计算机网络,手机使用电脑网络怎么操作 手机使用电脑网络操作方法...
  14. SHAP 可视化解释机器学习模型简介
  15. Java学习—画图程序项目(2)
  16. 为什么说买腾讯云服务器通过代理商购买更划算
  17. TFTP协议解析及C/C++代码实现
  18. 【统计学习方法】第7章 支持向量机
  19. 方向标 | c++ | 动态规划
  20. 蓝桥杯c语言试题寒假作业,寒假作业--蓝桥杯

热门文章

  1. python从入门到精通百度云资源-Python从入门到精通(资源汇总)
  2. python零基础能学吗-0基础该不该学习Python?适合学习吗?
  3. 语音识别哪家强?百度 、苹果、科大讯飞都有制胜法宝
  4. 连续数字及数字串识别技术
  5. IBM分布式深度学习技术将语音识别训练时间从一周缩短到11小时
  6. 栈中对象定位的方式(句柄池,直接引用)
  7. 最简单的基于FFmpeg的移动端例子:Android 视频转码器
  8. FFmpeg源代码简单分析:libavdevice的gdigrab
  9. android读取mysql数据库文件_Android开发系列(十七):读取assets目录下的数据库文件...
  10. 序列划分c语言,一篇“get”C语言八大排序算法