第一张图是hdr_simple.fs、第二张图是hdr_exposure.fs、第三章图是hdr_adaptive.fs渲染的
示例程序从一个浮点纹理读取并写入到一个帧缓冲区对象,在这个缓冲区对象中一个8位纹理被绑定到了第一个渲染目标。这样就允许从HDR到LDR的变换在逐个像素的基础上进行,减少了当一个纹理单元在明亮部分和阴暗部分之间进行插值时出现的修饰痕迹。一旦LDR图像生成,它就会作为一个纹理直接绘制到屏幕上。
#version 150
// hdr.fs
// Simple texture replace
//
in vec2 vTexCoord;
uniform sampler2D textureUnit0;
out vec4 oColor;
void main(void)
{
// fetch from HDR texture
vec4 hdrTexel = texture(textureUnit0, vTexCoord);
oColor = hdrTexel;
}
#version 150
// hdr_exposure.fs
// Scale floating point texture to 0.0 - 1.0 based
// on the specified exposure
//
in vec2 vTexCoord;
uniform sampler2D textureUnit0;
uniform float exposure;
out vec4 oColor;
void main(void)
{
// fetch from HDR texture
vec4 vColor = texture(textureUnit0, vTexCoord);
// Apply the exposure to this texel
oColor = 1.0 - exp2(-vColor * exposure);
oColor.a = 1.0f;
}
#version 150
// hdr_adaptive.fs
// perform adaptive tone mapping based on relative pixel locations
//
in vec2 vTexCoord;
uniform sampler2D textureUnit0;
uniform sampler1D textureUnit1;
uniform vec2 tc_offset[25];
out vec4 oColor;
void main(void)
{
vec4 hdrSample[25];
for (int i = 0; i < 25; i++)
{ // Perform 25 lookups around the current texel
hdrSample[i] = texture(textureUnit0, vTexCoord.st + tc_offset[i]);
}
// Calculate weighted color of region
vec4 vColor = hdrSample[12];
vec4 kernelcolor = (
(1.0 * (hdrSample[0] + hdrSample[4] + hdrSample[20] + hdrSample[24])) +
(4.0 * (hdrSample[1] + hdrSample[3] + hdrSample[5] + hdrSample[9] +
hdrSample[15] + hdrSample[19] + hdrSample[21] + hdrSample[23])) +
(7.0 * (hdrSample[2] + hdrSample[10] + hdrSample[14] + hdrSample[22])) +
(16.0 * (hdrSample[6] + hdrSample[8] + hdrSample[16] + hdrSample[18])) +
(26.0 * (hdrSample[7] + hdrSample[11] + hdrSample[13] + hdrSample[17])) +
(41.0 * hdrSample[12])
) / 273.0;
// Calculate luminance for the whole filter kernel
float kernelLuminance = dot(kernelcolor.rgb, vec3(0.3, 0.59, 0.11));
// look up the corresponding exposure
float exposure = texture(textureUnit1, kernelLuminance/2.0).r;
exposure = clamp(exposure, 0.02f, 20.0f);
// Apply the exposure to this texel
oColor = 1.0 - exp2(-vColor * exposure);
oColor.a = 1.0f;
}
#include <stdio.h>
#include <iostream>
#include <ImfRgbaFile.h> // OpenEXR headers
#include <ImfArray.h>
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include <GL\glu.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#ifdef _WIN32
#pragma comment (lib, "half.lib")
#pragma comment (lib, "Iex.lib")
#pragma comment (lib, "IlmImf.lib")
#pragma comment (lib, "IlmThread.lib")
#pragma comment (lib, "Imath.lib")
#pragma comment (lib, "zlib.lib")
#endif
#pragma warning (disable : 4305)
GLsizei screenWidth; // Desired window or desktop width
GLsizei screenHeight; // Desired window or desktop height
GLboolean bFullScreen; // Request to run full screen
GLboolean bAnimated; // Request for continual updates
GLMatrixStack modelViewMatrix; // Modelview Matrix
GLMatrixStack projectionMatrix; // Projection Matrix
GLGeometryTransform transformPipeline; // Geometry Transform Pipeline
GLBatch screenQuad;
GLBatch fboQuad;
M3DMatrix44f orthoMatrix;
M3DMatrix44f fboOrthoMatrix;
GLuint hdrTextures[1];
GLuint lutTxtures[1];
GLuint fboTextures[1];
GLuint hdrTexturesWidth[1];
GLuint hdrTexturesHeight[1];
GLuint curHDRTex;
GLuint fboName;
GLuint mapTexProg;
GLuint varExposureProg;
GLuint adaptiveProg;
GLuint curProg;
GLfloat exposure;
void GenerateOrtho2DMat(GLuint windowWidth, GLuint windowHeight, GLuint imageWidth, GLuint imageHeight);
void GenerateFBOOrtho2DMat(GLuint imageWidth, GLuint imageHeight);
void SetupHDRProg();
void SetupStraightTexProg();
bool LoadOpenEXRImage(char *fileName, GLint textureName, GLuint &texWidth, GLuint &texHeight);
// Take a file name/location and load an OpenEXR
// Load the image into the "texture" texture object and pass back the texture sizes
//
bool LoadOpenEXRImage(char *fileName, GLint textureName, GLuint &texWidth, GLuint &texHeight)
{
// The OpenEXR uses exception handling to report errors or failures
// Do all work in a try block to catch any thrown exceptions.
try
{
Imf::Array2D<Imf::Rgba> pixels;
Imf::RgbaInputFile file (fileName);
Imath::Box2i dw = file.dataWindow();
texWidth = dw.max.x - dw.min.x + 1;
texHeight = dw.max.y - dw.min.y + 1;
pixels.resizeErase (texHeight, texWidth);
file.setFrameBuffer (&pixels[0][0] - dw.min.x - dw.min.y * texWidth, 1, texWidth);
file.readPixels (dw.min.y, dw.max.y);
GLfloat* texels = (GLfloat*)malloc(texWidth * texHeight * 3 * sizeof(GLfloat));
GLfloat* pTex = texels;
// Copy OpenEXR into local buffer for loading into a texture
for (unsigned int v = 0; v < texHeight; v++)
{
for (unsigned int u = 0; u < texWidth; u++)
{
Imf::Rgba texel = pixels[texHeight - v - 1][u];
pTex[0] = texel.r;
pTex[1] = texel.g;
pTex[2] = texel.b;
pTex += 3;
}
}
// Bind texture, load image, set tex state
glBindTexture(GL_TEXTURE_2D, textureName);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, texWidth, texHeight, 0, GL_RGB, GL_FLOAT, texels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
free(texels);
}
catch(Iex::BaseExc & e)
{
std::cerr << e.what() << std::endl;
//
// Handle exception.
//
}
return true;
}
///
// OpenGL related startup code is safe to put here. Load textures, etc.
void SetupRC(void)
{
GLfloat texCoordOffsets[4][5*5*2];
GLfloat exposureLUT[20] = { 11.0, 6.0, 3.2, 2.8, 2.2, 1.90, 1.80, 1.80, 1.70, 1.70, 1.60, 1.60, 1.50, 1.50, 1.40, 1.40, 1.30, 1.20, 1.10, 1.00 };
// Make sure OpenGL entry points are set
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
}
// Will not use depth buffer
glDisable(GL_DEPTH_TEST);
curHDRTex = 0;
// Init codel-view and leave it
modelViewMatrix.LoadIdentity();
// Black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Setup LUT texture for use with the adaptive exposure filter
glGenTextures(1, lutTxtures);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_1D, lutTxtures[1]);
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16F, 20, 0, GL_RED, GL_FLOAT, exposureLUT);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Setup HDR texture(s)
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, hdrTextures);
glBindTexture(GL_TEXTURE_2D, hdrTextures[curHDRTex]);
// Load HDR image from EXR file
LoadOpenEXRImage("Tree.exr", hdrTextures[curHDRTex], hdrTexturesWidth[curHDRTex], hdrTexturesHeight[curHDRTex]);
// Create ortho matrix and screen-sized quad matching images aspect ratio
GenerateOrtho2DMat(screenWidth, screenHeight, hdrTexturesWidth[curHDRTex], hdrTexturesHeight[curHDRTex]);
//GenerateFBOOrtho2DMat(hdrTexturesWidth[curHDRTex], hdrTexturesHeight[curHDRTex]);
gltGenerateOrtho2DMat(hdrTexturesWidth[curHDRTex], hdrTexturesHeight[curHDRTex], fboOrthoMatrix, fboQuad);
// Setup tex coords to be used for fetching HDR kernel data
for (int k = 0; k < 4; k++)
{
float xInc = 1.0f / (GLfloat)(hdrTexturesWidth[curHDRTex] >> k);
float yInc = 1.0f / (GLfloat)(hdrTexturesHeight[curHDRTex] >> k);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
texCoordOffsets[k][(((i*5)+j)*2)+0] = (-1.0f * xInc) + ((GLfloat)i * xInc);
texCoordOffsets[k][(((i*5)+j)*2)+1] = (-1.0f * yInc) + ((GLfloat)j * yInc);
}
}
}
// Load shaders
mapTexProg = gltLoadShaderPairWithAttributes("hdr.vs", "hdr_simple.fs", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0");
glBindFragDataLocation(mapTexProg, 0, "oColor");
glLinkProgram(mapTexProg);
varExposureProg = gltLoadShaderPairWithAttributes("hdr.vs", "hdr_exposure.fs", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0");
glBindFragDataLocation(varExposureProg, 0, "oColor");
glLinkProgram(varExposureProg);
glUseProgram(varExposureProg);
glUniform1i(glGetUniformLocation(varExposureProg, "textureUnit0"), 0);
adaptiveProg = gltLoadShaderPairWithAttributes("hdr.vs", "hdr_adaptive.fs", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0");
glBindFragDataLocation(adaptiveProg, 0, "oColor");
glLinkProgram(adaptiveProg);
glUseProgram(adaptiveProg);
glUniform1i(glGetUniformLocation(adaptiveProg, "textureUnit0"), 0);
glUniform1i(glGetUniformLocation(adaptiveProg, "textureUnit1"), 1);
glUniform2fv(glGetUniformLocation(adaptiveProg, "tc_offset"), 25, texCoordOffsets[0]);
glUseProgram(0);
// Create and bind an FBO
glGenFramebuffers(1,&fboName);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboName);
// Create the FBO texture
glGenTextures(1, fboTextures);
glBindTexture(GL_TEXTURE_2D, fboTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, hdrTexturesWidth[curHDRTex], hdrTexturesHeight[curHDRTex], 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);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTextures[0], 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Make sure all went well
gltCheckErrors();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// Set first running mode
curProg = adaptiveProg;
}
///
// Do your cleanup here. Free textures, display lists, buffer objects, etc.
void ShutdownRC(void)
{
// Make sure default FBO is bound
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fboName);
// Cleanup textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_1D, 0);
glDeleteTextures(1, hdrTextures);
glDeleteTextures(1, lutTxtures);
glDeleteTextures(1, fboTextures);
}
///
// Create a matrix that maps geometry to the screen. 1 unit in the x direction equals one pixel
// of width, same with the y direction.
// It also depends on the size of the texture being displayed
void GenerateOrtho2DMat(GLuint windowWidth, GLuint windowHeight, GLuint imageWidth, GLuint imageHeight)
{
float right = (float)windowWidth;
float quadWidth = right;
float left = 0.0f;
float top = (float)windowHeight;
float quadHeight = top;
float bottom = 0.0f;
float screenAspect = (float)windowWidth/windowHeight;
float imageAspect = (float)imageWidth/imageHeight;
if (screenAspect > imageAspect)
quadWidth = windowHeight*imageAspect;
else
quadHeight = windowWidth*imageAspect;
// set ortho matrix
orthoMatrix[0] = (float)(2 / (right - left));
orthoMatrix[1] = 0.0;
orthoMatrix[2] = 0.0;
orthoMatrix[3] = 0.0;
orthoMatrix[4] = 0.0;
orthoMatrix[5] = (float)(2 / (top - bottom));
orthoMatrix[6] = 0.0;
orthoMatrix[7] = 0.0;
orthoMatrix[8] = 0.0;
orthoMatrix[9] = 0.0;
orthoMatrix[10] = (float)(-2 / (1.0 - 0.0));
orthoMatrix[11] = 0.0;
orthoMatrix[12] = -1*(right + left) / (right - left);
orthoMatrix[13] = -1*(top + bottom) / (top - bottom);
orthoMatrix[14] = -1.0f;
orthoMatrix[15] = 1.0;
// set screen quad vertex array
screenQuad.Reset();
screenQuad.Begin(GL_TRIANGLE_STRIP, 4, 1);
screenQuad.Color4f(0.0f, 1.0f, 0.0f, 1.0f);
screenQuad.MultiTexCoord2f(0, 0.0f, 0.0f);
screenQuad.Vertex3f(0.0f, 0.0f, 0.0f);
screenQuad.Color4f(0.0f, 1.0f, 0.0f, 1.0f);
screenQuad.MultiTexCoord2f(0, 1.0f, 0.0f);
screenQuad.Vertex3f(quadWidth, 0.0f, 0.0f);
screenQuad.Color4f(0.0f, 1.0f, 0.0f, 1.0f);
screenQuad.MultiTexCoord2f(0, 0.0f, 1.0f);
screenQuad.Vertex3f(0.0f, quadHeight, 0.0f);
screenQuad.Color4f(0.0f, 1.0f, 0.0f, 1.0f);
screenQuad.MultiTexCoord2f(0, 1.0f, 1.0f);
screenQuad.Vertex3f(quadWidth, quadHeight, 0.0f);
screenQuad.End();
}
///
// This is called at least once and before any rendering occurs. If the screen
// is a resizeable window, then this will also get called whenever the window
// is resized.
void ChangeSize(int nWidth, int nHeight)
{
glViewport(0, 0, nWidth, nHeight);
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
modelViewMatrix.LoadIdentity();
// update screen sizes
screenWidth = nWidth;
screenHeight = nHeight;
GenerateOrtho2DMat(nWidth, nHeight, hdrTexturesWidth[curHDRTex], hdrTexturesHeight[curHDRTex]);
}
///
// Update the camera based on user input, toggle display modes
//
void SpecialKeys(int key, int x, int y)
{
static CStopWatch timer;
float fTime = timer.GetElapsedSeconds();
float linear = fTime / 100;
// Increase the scene exposure
if(key == GLUT_KEY_UP)
{
if((exposure + linear) < 20.0f)
exposure += linear;
}
// Decrease the scene exposure
if(key == GLUT_KEY_DOWN)
{
if((exposure - linear) > 0.01f)
exposure -= linear;
}
}
void ProcessKeys(unsigned char key, int x, int y)
{
if(key == '1')
{
curProg = mapTexProg;
}
if(key == '2')
{
curProg = varExposureProg;
}
if(key == '3')
{
curProg = adaptiveProg;
}
}
void SetupStraightTexProg()
{
// Set the cur prog for tex replace
glUseProgram(mapTexProg);
// Set MVP matrix
glUniformMatrix4fv(glGetUniformLocation(mapTexProg, "mvpMatrix"), 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
}
void SetupHDRProg()
{
// Set the program to the cur
glUseProgram(curProg);
// Set MVP matrix
glUniformMatrix4fv(glGetUniformLocation(curProg, "mvpMatrix"), 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
if (curProg == varExposureProg)
{
// Set the exposure for the scene
glUniform1f(glGetUniformLocation(curProg, "exposure"), exposure);
}
}
///
// Render a frame. The owning framework is responsible for buffer swaps,
// flushes, etc.
void RenderScene(void)
{
static CStopWatch animationTimer;
float yRot = animationTimer.GetElapsedSeconds() * 60.0f;
// first, draw to FBO at full FBO resolution
// Bind FBO with 8b attachment
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboName);
glViewport(0, 0, hdrTexturesWidth[curHDRTex], hdrTexturesHeight[curHDRTex]);
glClear(GL_COLOR_BUFFER_BIT);
// Bind texture with HDR image
glBindTexture(GL_TEXTURE_2D, hdrTextures[curHDRTex]);
// Render pass, downsample to 8b using selected program
projectionMatrix.LoadMatrix(fboOrthoMatrix);
SetupHDRProg();
fboQuad.Draw();
// Then draw the resulting image to the screen, maintain image proportions
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(0, 0, screenWidth, screenHeight);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// Attach 8b texture with HDR image
glBindTexture(GL_TEXTURE_2D, fboTextures[0]);
// draw screen sized, proportional quad with 8b texture
projectionMatrix.LoadMatrix(orthoMatrix);
SetupStraightTexProg();
screenQuad.Draw();
// Do the buffer Swap
glutSwapBuffers();
// Do it again
glutPostRedisplay();
}
int main(int argc, char* argv[])
{
screenWidth = 614;
screenHeight = 655;
mapTexProg = 0;
varExposureProg = 0;
adaptiveProg = 0;
curHDRTex = 0;
fboName = 0;
curProg = 0;
exposure = 0.1f;
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(screenWidth,screenHeight);
glutCreateWindow("HDR Imaging");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
glutKeyboardFunc(ProcessKeys);
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}