参考:

https://learnopengl.com/Advanced-Lighting/HDR
当亮度和颜色存储在帧缓冲中时,它们的默认值被限定在0.0和1.0之间,由于这个原因导致我们总是在这个范围内指定光和颜色的值,试图使它们适合场景,这种方法很好,而且效果还不错,

但是如果我们在一个特别明亮的区域里行走,有多个明亮的光源,而且光源总亮度超过1.0,会发生什么呢?

答案是这些片段中超过1.0的亮度或者颜色值会被约束在1.0,从而导致场景混成一片,难以分辨:


由于大量片段的颜色值都非常接近1.0,在很大一个区域内每一个亮的片段都有相同的白色。这损失了很多的细节,使场景看起来非常假。
解决这个问题的一个方案是减小光源的强度从而保证场景内没有一个片段亮于1.0。
然而这并不是一个好的方案,因为你需要使用不切实际的光照参数。一个更好的方案是让颜色暂时超过1.0,然后将其转换至0.0到1.0的区间内,从而防止损失细节。

显示器被限制为只能显示值为0.0到1.0间的颜色,但是在光照方程中却没有这个限制。通过使片段的颜色超过1.0,我们有了一个更大的颜色范围,这也被称作HDR(High Dynamic Range, 高动态范围)。有了HDR,亮的东西可以变得非常亮,暗的东西可以变得非常暗,而且充满细节。

HDR原本只是被运用在摄影上,摄影师对同一个场景采取不同曝光拍多张照片,捕捉大范围的色彩值。这些图片被合成为HDR图片,从而综合不同的曝光等级使得大范围的细节可见。看下面这个例子,左边这张图片在被光照亮的区域充满细节,但是在黑暗的区域就什么都看不见了;但是右边这张图的高曝光却可以让之前看不出来的黑暗区域显现出来。

这与我们眼睛工作的原理非常相似,也是HDR渲染的基础。当光线很弱的啥时候,人眼会自动调整从而使过暗和过亮的部分变得更清晰,就像人眼有一个能自动根据场景亮度调整的自动曝光滑块。

高动态范围渲染有点像这样。我们允许更大范围的颜色值,以渲染收集大范围的黑暗和明亮的细节的场景,最后将所有HDR值转换成在[0.0, 1.0]范围的LDR

换HDR值到LDR值得过程叫做色调映射

现在现存有很多的色调映射算法,这些算法致力于在转换过程中保留尽可能多的HDR细节。这些色调映射算法经常会包含一个选择性倾向黑暗或者明亮区域的参数。

在实时渲染中,HDR不仅允许我们超过LDR的范围[0.0, 1.0]与保留更多的细节,同时还让我们能够根据光源的真实强度指定它的强度。比如太阳有比闪光灯之类的东西更高的强度,那么我们为什么不这样子设置呢?(比如说设置一个10.0的漫亮度) 这允许我们用更现实的光照参数恰当地配置一个场景的光照,而这在LDR渲染中是不能实现的,因为他们会被上限约束在1.0。

因为显示器只能显示在0.0到1.0范围之内的颜色,我们肯定要做一些转换从而使得当前的HDR颜色值符合显示器的范围。简单地取平均值重新转换这些颜色值并不能很好的解决这个问题,因为明亮的地方会显得更加显著。我们能做的是用一个不同的方程与/或曲线来转换这些HDR值到LDR值,从而给我们对于场景的亮度完全掌控,这就是之前说的色调变换,也是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_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);

默认的帧缓冲默认一个颜色分量只占用8位(bits)。当使用一个使用32位每颜色分量的浮点帧缓冲时(使用GL_RGB32F 或者GL_RGBA32F),我们需要四倍的内存来存储这些颜色。所以除非你需要一个非常高的精确度,32位不是必须的,使用GLRGB16F就足够了。

有了一个带有浮点颜色缓冲的帧缓冲,我们可以放心渲染场景到这个帧缓冲中。在这个教程的例子当中,我们先渲染一个光照的场景到浮点帧缓冲中,之后再在一个铺屏四边形(Screen-filling Quad)上应用这个帧缓冲的颜色缓冲,代码会是这样子:

glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // [...] 渲染(光照的)场景
glBindFramebuffer(GL_FRAMEBUFFER, 0);// 现在使用一个不同的着色器将HDR颜色缓冲渲染至2D铺屏四边形上
hdrShader.Use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, hdrColorBufferTexture);
RenderQuad();

这里场景的颜色值存在一个可以包含任意颜色值的浮点颜色缓冲中,值可能是超过1.0的。这个简单的演示中,场景被创建为一个被拉伸的立方体通道和四个点光源,其中一个非常亮的在隧道的尽头:

std::vector<glm::vec3> lightColors;
lightColors.push_back(glm::vec3(200.0f, 200.0f, 200.0f));
lightColors.push_back(glm::vec3(0.1f, 0.0f, 0.0f));
lightColors.push_back(glm::vec3(0.0f, 0.0f, 0.2f));
lightColors.push_back(glm::vec3(0.0f, 0.1f, 0.0f));

渲染至浮点帧缓冲和渲染至一个普通的帧缓冲是一样的。新的东西就是这个的hdrShader的片段着色器,用来渲染最终拥有浮点颜色缓冲纹理的2D四边形。我们来定义一个简单的直通片段着色器(Pass-through Fragment Shader):

#version 330 core
out vec4 color;
in vec2 TexCoords;uniform sampler2D hdrBuffer;void main()
{             vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;color = vec4(hdrColor, 1.0);
}

这里我们直接采样了浮点颜色缓冲并将其作为片段着色器的输出。然而,这个2D四边形的输出是被直接渲染到默认的帧缓冲中,导致所有片段着色器的输出值被约束在0.0到1.0间,尽管我们已经有了一些存在浮点颜色纹理的值超过了1.0。

很明显,在隧道尽头的强光的值被约束在1.0,因为一大块区域都是白色的,过程中超过1.0的地方损失了所有细节。因为我们直接转换HDR值到LDR值,这就像我们根本就没有应用HDR一样。为了修复这个问题我们需要做的是无损转化所有浮点颜色值回0.0-1.0范围中。我们需要应用到色调映射。

色调映射

色调映射(Tone Mapping)是一个损失很小的转换浮点颜色值至我们所需的LDR[0.0, 1.0]范围内的过程,通常会伴有特定的风格的色平衡(Stylistic Color Balance)。

最简单的色调映射算法是Reinhard色调映射,它涉及到分散整个HDR颜色值到LDR颜色值上,所有的值都有对应。Reinhard色调映射算法平均得将所有亮度值分散到LDR上。我们将Reinhard色调映射应用到之前的片段着色器上,并且为了更好的测量加上一个Gamma校正过滤(包括SRGB纹理的使用):

void main()
{             const float gamma = 2.2;vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;// Reinhard色调映射vec3 mapped = hdrColor / (hdrColor + vec3(1.0));// Gamma校正mapped = pow(mapped, vec3(1.0 / gamma));color = vec4(mapped, 1.0);
}

有了Reinhard色调映射的应用,我们不再会在场景明亮的地方损失细节。当然,这个算法是倾向明亮的区域的,暗的区域会不那么精细也不那么有区分度。

现在你可以看到在隧道的尽头木头纹理变得可见了。用了这个非常简单地色调映射算法,我们可以合适的看到存在浮点帧缓冲中整个范围的HDR值,给我们对于无损场景光照精确的控制。

另一个有趣的色调映射应用是曝光(Exposure)参数的使用。你可能还记得之前我们在介绍里讲到的,HDR图片包含在不同曝光等级的细节。如果我们有一个场景要展现日夜交替,我们当然会在白天使用低曝光,在夜间使用高曝光,就像人眼调节方式一样。有了这个曝光参数,我们可以去设置可以同时在白天和夜晚不同光照条件工作的光照参数,我们只需要调整曝光参数就行了。

uniform float exposure;void main()
{             const float gamma = 2.2;vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;// 曝光色调映射vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure);// Gamma校正 mapped = pow(mapped, vec3(1.0 / gamma));color = vec4(mapped, 1.0);
}

在这里我们将exposure定义为默认为1.0的uniform,从而允许我们更加精确设定我们是要注重黑暗还是明亮的区域的HDR颜色值。举例来说,高曝光值会使隧道的黑暗部分显示更多的细节,然而低曝光值会显著减少黑暗区域的细节,但允许我们看到更多明亮区域的细节。下面这组图片展示了在不同曝光值下的通道:

通过改变曝光等级,我们可以看见场景的很多细节,而这些细节可能在LDR渲染中都被丢失了。比如说隧道尽头,在正常曝光下木头结构隐约可见,但用低曝光木头的花纹就可以清晰看见了

HDR渲染的一些优点:在明亮和黑暗区域无细节损失,因为它们可以由色调映射重新获取多个光照的叠加不会导致亮度被约束;光照可以被设定为他们原来的亮度而不是被LDR值限定。而且,HDR渲染也使一些有趣的效果更加可行和真实。

代码实现:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "shader.h"
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Camera.h"
#include "Model.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <random>
void CaldeltaTime();
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);
int main();
void processInput(GLFWwindow *window);
unsigned int loadTexture(const char *path, bool gammaCorrection);
unsigned int loadCubemap(vector<std::string> faces);const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
GLfloat lerp(GLfloat a, GLfloat b, GLfloat f)
{return a + f * (b - a);
}Camera camera(glm::vec3(0.0f, 0.0f, 5.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;float deltaTime = 0.0f;
float lastFrame = 0.0f;bool blinn = false;
bool blinnKeyPressed = false;GLuint woodTexture;
GLuint planeVAO;
GLboolean shadows = true;void renderQuad();
void renderCube();
bool hdr = true;
bool hdrKeyPressed = false;
float exposure = 1.0f;int main()
{// glfw: initialize and configure// ------------------------------glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif// glfw window creation// --------------------GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", 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);// tell GLFW to capture our mouseglfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);// glad: load all OpenGL function pointers// ---------------------------------------if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// configure global opengl state// -----------------------------glEnable(GL_DEPTH_TEST);// build and compile shaders// -------------------------Shader shader("D:\\OpenGL\\LearnOpenGL-master\\src\\5.advanced_lighting\\6.hdr\\6.lighting.vs", "D:\\OpenGL\\LearnOpenGL-master\\src\\5.advanced_lighting\\6.hdr\\6.lighting.fs");Shader hdrShader("D:\\OpenGL\\LearnOpenGL-master\\src\\5.advanced_lighting\\6.hdr\\6.hdr.vs", "D:\\OpenGL\\LearnOpenGL-master\\src\\5.advanced_lighting\\6.hdr\\6.hdr.fs");// load textures// -------------unsigned int woodTexture = loadTexture("D:\\OpenGL\\LearnOpenGL-master\\resources\\textures\\wood.png", true); // 注意,我们将纹理加载为SRGB纹理//创建缓冲unsigned int hdrFBO;glGenFramebuffers(1, &hdrFBO);// 创建浮点颜色缓冲unsigned int colorBuffer;glGenTextures(1, &colorBuffer);glBindTexture(GL_TEXTURE_2D, colorBuffer);//默认的帧缓冲默认一个颜色分量只占用8位(bits)。//当使用一个使用32位每颜色分量的浮点帧缓冲时(使用GL_RGB32F 或者GL_RGBA32F),我们需要四倍的内存来存储这些颜色。//所以除非你需要一个非常高的精确度,32位不是必须的,使用GLRGB16F就足够了。glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_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);// 创建深度缓冲unsigned int rboDepth;glGenRenderbuffers(1, &rboDepth);glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT);// 附加缓冲glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBuffer, 0);glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)std::cout << "Framebuffer not complete!" << std::endl;glBindFramebuffer(GL_FRAMEBUFFER, 0);// 光照信息// -------------// 位置std::vector<glm::vec3> lightPositions;lightPositions.push_back(glm::vec3(0.0f, 0.0f, 49.5f)); // back lightlightPositions.push_back(glm::vec3(-1.4f, -1.9f, 9.0f));lightPositions.push_back(glm::vec3(0.0f, -1.8f, 4.0f));lightPositions.push_back(glm::vec3(0.8f, -1.7f, 6.0f));// 颜色std::vector<glm::vec3> lightColors;lightColors.push_back(glm::vec3(200.0f, 200.0f, 200.0f));lightColors.push_back(glm::vec3(0.1f, 0.0f, 0.0f));lightColors.push_back(glm::vec3(0.0f, 0.0f, 0.2f));lightColors.push_back(glm::vec3(0.0f, 0.1f, 0.0f));// 着色器配置// --------------------shader.use();shader.setInt("diffuseTexture", 0);hdrShader.use();hdrShader.setInt("hdrBuffer", 0);// 循环渲染// -----------while (!glfwWindowShouldClose(window)){CaldeltaTime();// 输入// -----processInput(window);// 渲染// ------glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 1. 渲染场景到浮点帧缓冲// -----------------------------------------------glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (GLfloat)SCR_WIDTH / (GLfloat)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();shader.use();shader.setMat4("projection", projection);shader.setMat4("view", view);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, woodTexture);// 设置光源uniformsfor (unsigned int i = 0; i < lightPositions.size(); i++){shader.setVec3("lights[" + std::to_string(i) + "].Position", lightPositions[i]);shader.setVec3("lights[" + std::to_string(i) + "].Color", lightColors[i]);}shader.setVec3("viewPos", camera.Position);// 渲染隧道glm::mat4 model = glm::mat4(1.0f);model = glm::translate(model, glm::vec3(0.0f, 0.0f, 25.0));model = glm::scale(model, glm::vec3(2.5f, 2.5f, 27.5f));shader.setMat4("model", model);shader.setInt("inverse_normals", true);renderCube();glBindFramebuffer(GL_FRAMEBUFFER, 0);// 2. 渲染浮点颜色缓冲区到2D四边形,色调映射HDR颜色到默认帧缓冲区的颜色范围(0-1)// --------------------------------------------------------------------------------------------------------------------------glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);hdrShader.use();glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, colorBuffer);hdrShader.setInt("hdr", hdr);hdrShader.setFloat("exposure", exposure);renderQuad();glfwSwapBuffers(window);glfwPollEvents();}glfwTerminate();return 0;
}// renderCube() renders a 1x1 3D cube in NDC.
// -------------------------------------------------
unsigned int cubeVAO = 0;
unsigned int cubeVBO = 0;
void renderCube()
{// initialize (if necessary)if (cubeVAO == 0){float vertices[] = {// back face-1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f, // bottom-left1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f, // top-right1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 0.0f, // bottom-right         1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f, // top-right-1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f, // bottom-left-1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 1.0f, // top-left// front face-1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f, // bottom-left1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 0.0f, // bottom-right1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f, // top-right1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f, // top-right-1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 1.0f, // top-left-1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f, // bottom-left// left face-1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right-1.0f,  1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-left-1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-left-1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-left-1.0f, -1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 0.0f, // bottom-right-1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right// right face1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-left1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-right1.0f,  1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-right         1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-right1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-left1.0f, -1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 0.0f, // bottom-left     // bottom face-1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 1.0f, // top-left1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f, // bottom-left1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f, // bottom-left-1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 0.0f, // bottom-right-1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right// top face-1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f, // top-left1.0f,  1.0f , 1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 1.0f, // top-right     1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right-1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f, // top-left-1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 0.0f  // bottom-left        };glGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &cubeVBO);// fill bufferglBindBuffer(GL_ARRAY_BUFFER, cubeVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// link vertex attributesglBindVertexArray(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);}// render CubeglBindVertexArray(cubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);
}// renderQuad() renders a 1x1 XY quad in NDC
// -----------------------------------------
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad()
{if (quadVAO == 0){float quadVertices[] = {// positions        // texture Coords-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,};// setup plane VAOglGenVertexArrays(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 CaldeltaTime()
{float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;
}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);if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)camera.ProcessKeyboard(UP, deltaTime);if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && !hdrKeyPressed){hdr = !hdr;hdrKeyPressed = true;}if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_RELEASE){hdrKeyPressed = false;}if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)camera.ProcessKeyboard(DOWN, deltaTime);if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS){if (exposure > 0.0f)exposure -= 0.001f;elseexposure = 0.0f;}else if (glfwGetKey(window, GLFW_KEY_C) == GLFW_PRESS){exposure += 0.001f;}}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; // reversed since y-coordinates go from bottom to toplastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}
unsigned int loadTexture(char const * path, bool gammaCorrection)
{unsigned int textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);if (data){GLenum internalFormat;GLenum dataFormat;if (nrComponents == 1){internalFormat = dataFormat = GL_RED;}else if (nrComponents == 3){internalFormat = gammaCorrection ? GL_SRGB : GL_RGB;dataFormat = GL_RGB;}else if (nrComponents == 4){internalFormat = gammaCorrection ? GL_SRGB_ALPHA : GL_RGBA;dataFormat = GL_RGBA;}glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, data);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);stbi_image_free(data);}else{std::cout << "Texture failed to load at path: " << path << std::endl;stbi_image_free(data);}return textureID;
}
unsigned int loadCubemap(vector<std::string> faces)
{unsigned int textureID;glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);int width, height, nrChannels;for (unsigned int i = 0; i < faces.size(); i++){unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);if (data){glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);stbi_image_free(data);}else{std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;stbi_image_free(data);}}glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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);return textureID;
}

OpenGL之HDR相关推荐

  1. 【opengl】HDR与Bloomw(未)

    HDR与Bloom HDR 浮点帧缓冲 创建帧缓冲 创建纹理 创建深度缓冲 检查 色调映射(tone mapping) Reinhard色调映射 有曝光参数(Exposure)的色调映射 自动曝光调整 ...

  2. [转]HDR渲染器的实现(基于OpenGL)

    http://dev.gameres.com/Program/Visual/3D/HDRTutorial/HDRTutorial.htm 作者:何咏(欢迎和大家交流,我的QQ:35574585,Ema ...

  3. OpenGL HDR色调映射的实例

    OpenGL HDR色调映射 先上图,再解答. 正常显示 按下M键 完整主要的源代码 源代码剖析 先上图,再解答. 正常显示 按下M键 完整主要的源代码 #

  4. OpenGL HDR曝光的实例

    OpenGL HDR曝光 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <string> #include <vmath.h& ...

  5. OpenGL HDR渲染

    OpenGL HDR渲染 HDR渲染简介 浮点帧缓冲 色调映射 HDR渲染简介 一般来说,当存储在帧缓冲(Framebuffer)中时,亮度和颜色的值是默认被限制在0.0到1.0之间的.这个看起来无辜 ...

  6. OpenGL基础50:HDR

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

  7. OpenGL(十八)Gamma校正 色域 与 HDR

    通常来说,在不同设备上看到的颜色是不同的.其中最常提及的概念是高动态光照渲染(High-Dynamic Range,简称 HDR ).它可以使图像在亮度的表现上更丰富.这篇文章讨论设备颜色和校正的相关 ...

  8. HDR in OpenGL

    HDR Framebuffer中的亮度和颜色值的范围被限制在0.0和1.0之间.我们在场景中设置光和颜色的时,也只能这个范围取值.这么做大部分情况下是OK的,结果也还可以,但是当场景中有一块多光源,亮 ...

  9. OpenGL核心技术之HDR

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...

最新文章

  1. linux基于此语言的密码,一次有趣的Linux下.Net Core与C语言的合作开发体验:生成Linux标准的用户密码串...
  2. gd动态曲线 php_php中用GD绘制折线图
  3. EasyUI 在aspx页面显示高度不正常解决办法
  4. JAVA入门级教学之(方法递归)
  5. 王思聪又双被限制消费了!
  6. JavaScript实现类与继承
  7. 2008年全国计算机等级考试须知及参考资料
  8. 小云(云层-陈霁)的发展史
  9. Java学到什么程度可以面试工作?
  10. matlab对语音信号预加重处理,语音信号的预加重处理和加窗处理
  11. 政务区块链平台设计思路
  12. linux下划线后面加变量名,Shell中下划线_与变量的关系
  13. 【matlab小笔记】
  14. 歌手详情页:下拉方大歌手图片
  15. cv2.imread()和cv2.cvtColor() 的使用
  16. 粤嵌GEC-6818
  17. VB编程:IF语句嵌套实例猜数小游戏-9
  18. XR21V141x usb转串口芯片驱动添加
  19. PHP支付宝APP支付接口
  20. DirectIO(O_DIRECT) 详解

热门文章

  1. aix系统oracle客户端配置文件,oracle客户端的配置
  2. VMware Workstation虚拟机装GHOST Win7
  3. idea 不能识别java_java – 为什么IntelliJ Idea不能识别我的Spek测试?
  4. 自从掌握了软件开发的 5 条核心原则,我每天工作时至少可以多摸鱼 4 个小时
  5. 智慧的数据中心运维风险管理
  6. Java文件操作源码大全
  7. 范凯谈Ruby on Rails项目实践
  8. Jsoup获取网页源代码不一致的问题
  9. Java之下载word文档
  10. 过分依赖计算机英语作文,2017年英语四级作文预测:过分依赖电脑