【OpenGL】hdr_bloom 泛光示例程序

①HDRFBO分别有2个浮点纹理,其中一个纹理捆绑到第一个颜色缓冲区绑定点,第二个纹理绑定到第二个颜色缓冲区绑定点,它们的内容都交给texReplaceProg着色器(basic.vs、tex_replace.fs)渲染,着色器主要内容就是照常渲染场景输出给第一个颜色缓冲区,而第二个颜色缓冲区内容是视野中明亮区域。具体代码如下:

#version 150 
// tex_replace.fs
// outputs 1 color using texture replace
// 

in vec2 vTexCoord; 

uniform sampler2D textureUnit0; 
uniform vec4 vColor;

out vec4 oColor;
out vec4 oBright;

void main(void) 
{
	const float bloomLimit = 1.0;
 
    oColor =  vColor*texture(textureUnit0, vTexCoord);
    oColor.a = 1.0;
    
    vec3 brightColor = max(vColor.rgb - vec3(bloomLimit), vec3(0.0));
    float bright = dot(brightColor, vec3(1.0));
    bright = smoothstep(0.0, 0.5, bright);
    oBright.rgb = mix(vec3(0.0), vColor.rgb, bright).rgb;
    oBright.a = 1.0;
}

其中,bloomLimit是固定值1.0,它是为了将HDR图像的颜色值小于1.0部分全部视为0,而大于1.0部分映射到都会整体-1。接着就是将3个分量相加得到亮度值,亮度值会被映射回[0,1]之间,其中小于0的会变为0,大于0.5的就视为1,接着用这个[0,1]范围的亮度值进行插值(黑色,HDR颜色),即亮度为0时 为黑色,亮度为1时 为HDR颜色,将这个最终得到的颜色输出给第二个颜色缓冲区以备使用。

到此我们得到了HDR原图像纹理对象和明亮区域图像,即代码中的hdrTextures[0]和brightBlurTextures[0]纹理对象存储着。

②brightPassFBO数组 一共有4个用于渲染模糊区域图像的FBO,它们分别都捆绑到了第一个颜色缓冲区绑定点,分别用纹理对象brightBlurTextures1~4来进行捆绑,着色器blurProg(basic.vs、blur.fs)进行渲染,结果是输出给第一个颜色缓冲区的。

#version 150 
// blur.fs
// outputs 1 color using a gaussian blur of the input texture
// 

in vec4 vFragColor; 
in vec2 vTexCoord; 

uniform sampler2D textureUnit0;
uniform vec2 tc_offset[25];
 
out vec4 oColor;

void main(void) 
{ 
	vec4 sample[25];
    for (int i = 0; i < 25; i++)
    {
        sample[i] = texture(textureUnit0, vTexCoord.st + tc_offset[i]);
    }

//   1  4  7  4 1
//   4 16 26 16 4
//   7 26 41 26 7 / 273
//   4 16 26 16 4
//   1  4  7  4 1

    oColor = (
                   (1.0  * (sample[0] + sample[4] + sample[20] + sample[24])) +
                   (4.0  * (sample[1] + sample[3] + sample[5] + sample[9] +
                            sample[15] + sample[19] + sample[21] + sample[23])) +
                   (7.0  * (sample[2] + sample[10] + sample[14] + sample[22])) +
                   (16.0 * (sample[6] + sample[8] + sample[16] + sample[18])) +
                   (26.0 * (sample[7] + sample[11] + sample[13] + sample[17])) +
                   (41.0 * sample[12])
                   ) / 273.0;

}

tc_offset[i]是一个对周围25个片段的偏移量,最终通过它获取周围25个像素的颜色值进行卷积得到oColor即一个2D纹理(全是模糊值)最终经过4次这样的采样,但每一次得到的模糊图像都是在前一个基础上进行的,每一次都会将图像缩小1/3再进行得到模糊图。

③将FBO恢复回默认FBO,将这些结果融合起来,即1张原图图像、1张亮度图、4张模糊图。使用hdrBloomProg着色器(basic.vs、hdr_exposure.fs)进行渲染。

#version 150 
// hdr_exposure.fs
// Scale floating point texture to 0.0 - 1.0 based 
// on the specified exposure
// 

in vec2 vTexCoord;

uniform sampler2D origImage;
uniform sampler2D brightImage;
uniform sampler2D blur1;
uniform sampler2D blur2;
uniform sampler2D blur3;
uniform sampler2D blur4;

uniform float exposure;
uniform float bloomLevel;

out vec4 oColor;
out vec4 oBright;

void main(void) 
{ 
	// fetch from HDR & blur textures
	vec4 vBaseImage  = texture2D(origImage, vTexCoord); 
	vec4 vBrightPass = texture2D(brightImage, vTexCoord); 
	vec4 vBlurColor1 = texture2D(blur1, vTexCoord); 
	vec4 vBlurColor2 = texture2D(blur2, vTexCoord); 
	vec4 vBlurColor3 = texture2D(blur3, vTexCoord); 
	vec4 vBlurColor4 = texture2D(blur4, vTexCoord); 
	
	vec4 vBloom = vBrightPass + 
				  vBlurColor1 + 
				  vBlurColor2 + 
				  vBlurColor3 + 
				  vBlurColor4;
	
	vec4 vColor = vBaseImage + bloomLevel * vBloom;
	
	// Apply the exposure to this texel
	vColor = 1.0 - exp2(-vColor * exposure);
	oColor = vColor;
    oColor.a = 1.0f;
    
}

它将亮度图采样出的亮度值 和 4张模糊图采样出的4个模糊值进行相加得到 曝光值,与一个bloomLevel统一值(曝光等级)相乘得到最终曝光颜色,将采样出的原色和曝光颜色叠加得到vColor接着进行一个常规曝光处理(完全看不懂)得到最终的经过曝光处理的颜色输出。

上面的basic.vs代码就是简单的转换了顶点坐标和计算了漫反射颜色(好像也没用到)

#version 150 
// hdr_bloom.vs
// outputs MVP transformed position 
// passes texture coordinates through
// color is * by normal and passed on.

in vec3 vVertex;
in vec3 vNormal;
in vec2 vTexCoord0;

uniform mat4 mvMatrix;
uniform mat4 pMatrix;
uniform vec3 vLightPos;
uniform vec4 vColor;

out vec4 vFragColor;
out vec2 vTexCoord;

void main(void) 
{ 
	mat3 mNormalMatrix;
	mNormalMatrix[0] = normalize(mvMatrix[0].xyz);
	mNormalMatrix[1] = normalize(mvMatrix[1].xyz);
	mNormalMatrix[2] = normalize(mvMatrix[2].xyz);
	
	vec3 vNorm = normalize(mNormalMatrix * vNormal);
	
	vec4 ecPosition;
	vec3 ecPosition3;
	ecPosition = mvMatrix * vec4(vVertex, 1.0);
	ecPosition3 = ecPosition.xyz /ecPosition.w;
	vec3 vLightDir = normalize(vLightPos - ecPosition3);
	float fDot = max(0.0, dot(vNorm, vLightDir)); 
	vFragColor.rgb = vColor.rgb * fDot;
	vFragColor.a = vColor.a;
	vTexCoord = vTexCoord0;
	mat4 mvpMatrix;
	mvpMatrix = pMatrix * mvMatrix;
	gl_Position = mvpMatrix * vec4(vVertex, 1.0); 
}
#include <stdio.h>
#include <iostream>

#include <ImfRgbaFile.h>            // OpenEXR headers
#include <ImfArray.h>

#include <GLTools.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 : 4244)

static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static GLfloat vWhiteX2[] = { 2.0f, 2.0f, 2.0f, 2.0f };
static GLfloat vGrey[] =  { 0.5f, 0.5f, 0.5f, 1.0f };
static GLfloat vLightPos[] = { -2.0f, 3.0f, -2.0f, 1.0f };
static GLfloat vSkyBlue[] = { 0.160f, 0.376f, 0.925f, 1.0f};

static const GLenum windowBuff[] = { GL_BACK_LEFT };
static const GLenum fboBuffs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };

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
GLFrustum			viewFrustum;			// View Frustum
GLGeometryTransform	transformPipeline;		// Geometry Transform Pipeline
GLFrame				cameraFrame;			// Camera frame
GLBatch             screenQuad;
M3DMatrix44f        orthoMatrix;  

GLBatch				floorBatch;
GLBatch				windowBatch;
GLBatch				windowBorderBatch;
GLBatch				windowGridBatch;

GLuint				flatColorProg;
GLuint				texReplaceProg;
GLuint				hdrBloomProg;
GLuint				blurProg;

GLuint              hdrFBO[1];
GLuint              brightPassFBO[4];
GLuint				textures[1];
GLuint				hdrTextures[1];
GLuint				brightBlurTextures[5];
GLuint				windowTexture;
GLfloat				exposure;
GLfloat				bloomLevel;
GLfloat				texCoordOffsets[5*5*2];
void UpdateMode(void);
void GenerateOrtho2DMat(GLuint imageWidth, GLuint imageHeight);
bool LoadBMPTexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode);
bool LoadOpenEXRImage(char *fileName, GLint textureName, GLuint &texWidth, GLuint &texHeight);
void GenTexCoordOffsets(GLuint width, GLuint height);
void SetupTexReplaceProg(GLfloat *vLightPos, GLfloat *vColor);
void SetupFlatColorProg(GLfloat *vLightPos, GLfloat *vColor);
void SetupHDRProg();
void SetupBlurProg();


///
// Load in a BMP file as a texture. Allows specification of the filters and the wrap mode
bool LoadBMPTexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)	
{
	GLbyte *pBits;
	GLint iWidth, iHeight;

	pBits = gltReadBMPBits(szFileName, &iWidth, &iHeight);
	if(pBits == NULL)
		return false;

	// Set Wrap modes
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, iWidth, iHeight, 0, GL_BGR, GL_UNSIGNED_BYTE, pBits);

    // Do I need to generate mipmaps?
	if(minFilter == GL_LINEAR_MIPMAP_LINEAR || minFilter == GL_LINEAR_MIPMAP_NEAREST || minFilter == GL_NEAREST_MIPMAP_LINEAR || minFilter == GL_NEAREST_MIPMAP_NEAREST)
		glGenerateMipmap(GL_TEXTURE_2D);    

	return true;
}



void GenTexCoordOffsets(GLuint width, GLuint height)
{
	float xInc = 1.0f / (GLfloat)(width);
	float yInc = 1.0f / (GLfloat)(height);

	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			texCoordOffsets[(((i*5)+j)*2)+0] = (-2.0f * xInc) + ((GLfloat)i * xInc);
			texCoordOffsets[(((i*5)+j)*2)+1] = (-2.0f * yInc) + ((GLfloat)j * yInc);
		}
	}
}

///
// OpenGL related startup code is safe to put here. Load textures, etc.
void SetupRC(void)
{
	GLenum err = glewInit();
	if (GLEW_OK != err)
	{
		/* Problem: glewInit failed, something is seriously wrong. */
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
	}

    glEnable(GL_DEPTH_TEST);

	exposure = 1.0f;
	bloomLevel = 0.5;

	// Light Blue
	glClearColor(vSkyBlue[0], vSkyBlue[1], vSkyBlue[2], vSkyBlue[3]);

	// Load geometry
	GLfloat alpha = 0.25f;
	floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
		floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
		floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
		floorBatch.Normal3f(0.0, 1.0f, 0.0f);
		floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);

		floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
		floorBatch.MultiTexCoord2f(0, 10.0f, 0.0f);
		floorBatch.Normal3f(0.0, 1.0f, 0.0f);
		floorBatch.Vertex3f(20.0f, -0.41f, 20.0f);

		floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
		floorBatch.MultiTexCoord2f(0, 10.0f, 10.0f);
		floorBatch.Normal3f(0.0, 1.0f, 0.0f);
		floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);

		floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
		floorBatch.MultiTexCoord2f(0, 0.0f, 10.0f);
		floorBatch.Normal3f(0.0, 1.0f, 0.0f);
		floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
	floorBatch.End();

	windowBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
		windowBatch.Color4f(1.0f, 0.0f, 0.0f, 1.0f);
		windowBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
		windowBatch.Normal3f( 0.0f, 1.0f, 0.0f);
		windowBatch.Vertex3f(-1.0f, 0.0f, 0.0f);

		windowBatch.Color4f(1.0f, 0.0f, 0.0f, 1.0f);
		windowBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
		windowBatch.Normal3f(0.0f, 1.0f, 0.0f);
		windowBatch.Vertex3f(1.0f, 0.0f, 0.0f);

		windowBatch.Color4f(1.0f, 0.0f, 0.0f, 1.0f);
		windowBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
		windowBatch.Normal3f(0.0f, 1.0f, 0.0f);
		windowBatch.Vertex3f(1.0f, 2.0f, 0.0f);

		windowBatch.Color4f(1.0f, 0.0f, 0.0f, 1.0f);
		windowBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
		windowBatch.Normal3f( 0.0f, 1.0f, 0.0f);
		windowBatch.Vertex3f(-1.0f, 2.0f, 0.0f);
	windowBatch.End();

	const float width = 0.2f;
	const float gridWidth = (float)0.05;
	windowBorderBatch.Begin(GL_TRIANGLE_STRIP, 13);
		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(-1.01f, width, 0.01f);

		windowBorderBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(-1.01f, 0.0f, 0.01f);

		windowBorderBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(1.01f, width, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(1.01f, 0.0f, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(1.01-width, 0.0f, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(1.01f, 2.0f, 0.01f);
			
		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(1.01-width, 2.0f, 0.01f);
			
		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(1.01f, 2.0-width, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(-1.01f, 2.f, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(-1.01f, 2.0-width, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(-1.01+width, 2.f, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(-1.01f, 0.0f, 0.01f);

		windowBorderBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowBorderBatch.Vertex3f(-1.01+width, 0.0f, 0.01f);
	windowBorderBatch.End();

	windowGridBatch.Begin(GL_TRIANGLES, 24);
		// bottom horizontal
		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-1.0f, 0.7+gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-1.0f, 0.7-gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(1.0f, 0.7-gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(1.0f, 0.7-gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(1.0f, 0.7+gridWidth, 0.01f);

		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-1.0f, 0.7+gridWidth, 0.01f);
		
		// Top horizontal
		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-1.0f, 1.3+gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-1.0f, 1.3-gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(1.0f, 1.3-gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(1.0f, 1.3-gridWidth, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(1.0f, 1.3+gridWidth, 0.01f);

		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-1.0f, 1.3+gridWidth, 0.01f);
		
		// Left Vertical
		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-0.3+gridWidth, 0.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-0.3-gridWidth, 0.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-0.3-gridWidth, 2.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-0.3-gridWidth, 2.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-0.3+gridWidth, 2.0, 0.01f);

		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(-0.3+gridWidth, 0.0f, 0.01f);

		// Right Vertical
		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(0.3+gridWidth, 0.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(0.3-gridWidth, 0.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(0.3-gridWidth, 2.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(0.3-gridWidth, 2.0f, 0.01f);

		windowGridBatch.Normal3f(0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(0.3+gridWidth, 2.0, 0.01f);

		windowGridBatch.Normal3f( 0.0f, 0.0f, 1.0f);
		windowGridBatch.Vertex3f(0.3+gridWidth, 0.0f, 0.01f);
	windowGridBatch.End();

	glGenTextures(1, textures);
	glBindTexture(GL_TEXTURE_2D, textures[0]);
	LoadBMPTexture("marble.bmp", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);

	// Setup HDR render texture
	glGenTextures(1, hdrTextures);
	glBindTexture(GL_TEXTURE_2D, hdrTextures[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, screenWidth, screenHeight, 0, GL_RGBA, GL_FLOAT, NULL);
	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_MIPMAP_LINEAR);
		
	// create textures for bloom effect
	glGenTextures(5, brightBlurTextures);
	int i = 0;
	for (i=0; i<5; i++)
	{
		glBindTexture(GL_TEXTURE_2D, brightBlurTextures[i]);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, screenWidth, screenHeight, 0, GL_RGBA, GL_FLOAT, NULL);
		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_MIPMAP_LINEAR);
	}

	// Attach HDR texture to fbo
	// Create and bind an FBO
	glGenFramebuffers(1,hdrFBO);
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hdrFBO[0]);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hdrTextures[0], 0);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, brightBlurTextures[0], 0);

	// Create FBOs for bloom effect
	glGenFramebuffers(4,brightPassFBO);
	for (i=0; i<4; i++)
	{
		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, brightPassFBO[i]);
		glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brightBlurTextures[i+1], 0);
	}

	// Create window texture
	glGenTextures(1, &windowTexture);
	glBindTexture(GL_TEXTURE_2D, windowTexture);
	GLuint texWidth = 0;
	GLuint texHeight = 0;
	// Load HDR image from EXR file
    LoadOpenEXRImage("window.exr", windowTexture, texWidth, texHeight);

	// Load flat color shader
	flatColorProg =  gltLoadShaderPairWithAttributes("basic.vs", "color.fs", 3, 
							GLT_ATTRIBUTE_VERTEX, "vVertex", 
							GLT_ATTRIBUTE_NORMAL, "vNormal", 
							GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0");
	glBindFragDataLocation(flatColorProg, 0, "oColor");
	glBindFragDataLocation(flatColorProg, 1, "oBright");
	glLinkProgram(flatColorProg);

	// Load texture replace shader
	texReplaceProg =  gltLoadShaderPairWithAttributes("basic.vs", "tex_replace.fs", 3, 
							GLT_ATTRIBUTE_VERTEX, "vVertex", 
							GLT_ATTRIBUTE_NORMAL, "vNormal", 
							GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0");
	glBindFragDataLocation(texReplaceProg, 0, "oColor");
	glBindFragDataLocation(texReplaceProg, 1, "oBright");
	glLinkProgram(texReplaceProg);

	// Load bloom shader
	hdrBloomProg =  gltLoadShaderPairWithAttributes("basic.vs", "hdr_exposure.fs", 3, 
							GLT_ATTRIBUTE_VERTEX, "vVertex", 
							GLT_ATTRIBUTE_NORMAL, "vNormal", 
							GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0");
	glBindFragDataLocation(hdrBloomProg, 0, "oColor");
	glLinkProgram(hdrBloomProg);

	// Load blur shader
	blurProg =  gltLoadShaderPairWithAttributes("basic.vs", "blur.fs", 2,
							GLT_ATTRIBUTE_VERTEX, "vVertex", 
							GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0");
	glBindFragDataLocation(blurProg, 0, "oColor");
	glLinkProgram(blurProg);
	glUseProgram(blurProg);
	
	// Setup tex coords to be used for fetching HDR kernel data
	GenTexCoordOffsets(screenWidth, screenHeight);
	glUniform2fv(glGetUniformLocation(blurProg, "tc_offset"), 25, &texCoordOffsets[0]);

	// Make sure all went well
	gltCheckErrors(flatColorProg);
	gltCheckErrors(texReplaceProg);
	gltCheckErrors(hdrBloomProg);
	gltCheckErrors(blurProg);
	
	// Reset framebuffer binding
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}


// 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;
}




///
// 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);

	// Cleanup textures
	for (int i=0; i<5;i++)
	{
		glActiveTexture(GL_TEXTURE0+i);
		glBindTexture(GL_TEXTURE_2D, 0);
	}

	glDeleteTextures(1, textures);
	glDeleteTextures(1, hdrTextures);
	glDeleteTextures(5, brightBlurTextures);
	glDeleteTextures(1, &windowTexture);

	// Cleanup FBOs
	glDeleteFramebuffers(1, hdrFBO);
	glDeleteFramebuffers(1, brightPassFBO);

}
void SetupFlatColorProg(GLfloat *vLightPos, GLfloat *vColor)
{
	glUseProgram(flatColorProg);

	// Set projection matrix
	glUniformMatrix4fv(glGetUniformLocation(flatColorProg, "pMatrix"), 
		1, GL_FALSE, transformPipeline.GetProjectionMatrix());

	// Set MVP matrix
	glUniformMatrix4fv(glGetUniformLocation(flatColorProg, "mvMatrix"), 
		1, GL_FALSE, transformPipeline.GetModelViewMatrix());

	// Set Light Pos
	glUniform3fv(glGetUniformLocation(flatColorProg, "vLightPos"), 1, vLightPos);

	// Set Color
	glUniform4fv(glGetUniformLocation(flatColorProg, "vColor"), 1, vColor);

	gltCheckErrors(flatColorProg);
}

void SetupTexReplaceProg(GLfloat *vLightPos, GLfloat *vColor)
{
	glUseProgram(texReplaceProg);

	// Set projection matrix
	glUniformMatrix4fv(glGetUniformLocation(texReplaceProg, "pMatrix"), 
		1, GL_FALSE, transformPipeline.GetProjectionMatrix());

	// Set MVP matrix
	glUniformMatrix4fv(glGetUniformLocation(texReplaceProg, "mvMatrix"), 
		1, GL_FALSE, transformPipeline.GetModelViewMatrix());

	// Set Light Pos
	glUniform3fv(glGetUniformLocation(texReplaceProg, "vLightPos"), 1, vLightPos);
	
	// Set Color
	glUniform4fv(glGetUniformLocation(texReplaceProg, "vColor"), 1, vColor);

	// Set Tex Unit
	glUniform1i(glGetUniformLocation(texReplaceProg, "textureUnit0"), 0);

	gltCheckErrors(texReplaceProg);
}

void SetupHDRProg()
{
	glUseProgram(hdrBloomProg);

	// Set projection matrix
	glUniformMatrix4fv(glGetUniformLocation(hdrBloomProg, "pMatrix"), 
		1, GL_FALSE, transformPipeline.GetProjectionMatrix());

	// Set MVP matrix
	glUniformMatrix4fv(glGetUniformLocation(hdrBloomProg, "mvMatrix"), 
		1, GL_FALSE, transformPipeline.GetModelViewMatrix());

	// Set user controled uniforms
	glUniform1fv(glGetUniformLocation(hdrBloomProg, "exposure"), 1, &exposure);
	glUniform1fv(glGetUniformLocation(hdrBloomProg, "bloomLevel"), 1, &bloomLevel);

	// Set texture uniforms
	glUniform1i(glGetUniformLocation(hdrBloomProg, "origImage"), 0);
	glUniform1i(glGetUniformLocation(hdrBloomProg, "brightImage"), 1);
	glUniform1i(glGetUniformLocation(hdrBloomProg, "blur1"), 2);
	glUniform1i(glGetUniformLocation(hdrBloomProg, "blur2"), 3);
	glUniform1i(glGetUniformLocation(hdrBloomProg, "blur3"), 4);
	glUniform1i(glGetUniformLocation(hdrBloomProg, "blur4"), 5);

	// Now setup the right textures
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, hdrTextures[0]);
	for (int i=0; i<5; i++)
	{
		glActiveTexture(GL_TEXTURE1+i);
		glBindTexture(GL_TEXTURE_2D, brightBlurTextures[i]);
	}
	gltCheckErrors(hdrBloomProg);
} 

void SetupBlurProg()
{
    // Set the program to the cur
	glUseProgram(blurProg);

	// Set projection matrix
	glUniformMatrix4fv(glGetUniformLocation(blurProg, "pMatrix"), 
		1, GL_FALSE, transformPipeline.GetProjectionMatrix());

	// Set MVP matrix
	glUniformMatrix4fv(glGetUniformLocation(blurProg, "mvMatrix"), 
		1, GL_FALSE, transformPipeline.GetModelViewMatrix());

	glUniform1i(glGetUniformLocation(blurProg, "textureUnit0"), 0);
 
	gltCheckErrors(blurProg);
}
///
// 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);
 
	viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
	projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
	modelViewMatrix.LoadIdentity();

	// update screen sizes
	screenWidth = nWidth;
	screenHeight = nHeight;

	glBindTexture(GL_TEXTURE_2D, hdrTextures[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, screenWidth, screenHeight, 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, brightBlurTextures[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, screenWidth, screenHeight, 0, GL_RGBA, GL_FLOAT, NULL);

	for(int i=1; i<5; i++)
		{		
		glBindTexture(GL_TEXTURE_2D, brightBlurTextures[i]);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, screenWidth/(i*3.0), screenHeight/(i*3.0), 0, GL_RGBA, GL_FLOAT, NULL);
		}

	// Setup tex coords to be used for fetching HDR kernel data
	glUseProgram(blurProg);
	GenTexCoordOffsets(screenWidth, screenHeight);
	glUniform2fv(glGetUniformLocation(blurProg, "tc_offset"), 25, texCoordOffsets);

	GenerateOrtho2DMat(nWidth, nHeight);
	glUseProgram(0);
}


///
// Create a matrix that maps geometry to the screen. 1 unit in the x directionequals one pixel 
// of width, same with the y direction.
//
void GenerateOrtho2DMat(GLuint imageWidth, GLuint imageHeight)
{
    float right = (float)imageWidth;
	float quadWidth = right;
	float left  = 0.0f;
	float top = (float)imageHeight;
	float quadHeight = top;
	float bottom = 0.0f;

    // set ortho matrix
	orthoMatrix[0] = (float)(2 / (right));
	orthoMatrix[1] = 0.0;
	orthoMatrix[2] = 0.0;
	orthoMatrix[3] = 0.0;

	orthoMatrix[4] = 0.0;
	orthoMatrix[5] = (float)(2 / (top));
	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.0f;
	orthoMatrix[13] = -1.0f;
	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(right, 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, top, 0.0f);

		screenQuad.Color4f(0.0f, 1.0f, 0.0f, 1.0f);
		screenQuad.MultiTexCoord2f(0, 1.0f, 1.0f);
		screenQuad.Vertex3f(right, top, 0.0f);
	screenQuad.End();
}

///
// 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;
	float smallLinear = fTime / 1000;

	// 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;
	}
	
	// Decrease amount of bloom effect
	if(key == GLUT_KEY_LEFT)
	{
		if((bloomLevel - smallLinear) > 0.00f)
			bloomLevel -= smallLinear;
		
	}
	// Increase amount of bloom effect
	if(key == GLUT_KEY_RIGHT)
	{
		if((bloomLevel + smallLinear) < 1.5f)
			bloomLevel += smallLinear;
	}
}


///
// Render a frame. The owning framework is responsible for buffer swaps,
// flushes, etc.
void RenderScene(void)
{
	int i =0;

	// first render the scene in HDR to fbo
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hdrFBO[0]);
	glDrawBuffers(1, &fboBuffs[0]);
	glClearColor(vSkyBlue[0], vSkyBlue[1], vSkyBlue[2], vSkyBlue[3]);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glDrawBuffers(1, &fboBuffs[1]);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Draw to two textures, the first contains scene data
	// the second contains only the bright areas
	glDrawBuffers(2, fboBuffs);
	modelViewMatrix.PushMatrix();	
		M3DMatrix44f mCamera;
		cameraFrame.GetCameraMatrix(mCamera);
		modelViewMatrix.MultMatrix(mCamera);
		glBindTexture(GL_TEXTURE_2D, textures[0]); // Marble

		// Draw the floor
		SetupTexReplaceProg(vLightPos, vWhite);
		floorBatch.Draw();

		// Draw the window
		modelViewMatrix.PushMatrix();
			modelViewMatrix.Translate(0.0f, -0.4f, -4.0f);
			modelViewMatrix.Rotate(10.0, 0.0, 1.0, 0.0);
			glBindTexture(GL_TEXTURE_2D, windowTexture); // Window Tex
			
			// First draw the window contents from texture
			SetupTexReplaceProg(vLightPos, vWhiteX2);
			windowBatch.Draw();

			// Now draw the border and the grid
			SetupFlatColorProg(vLightPos, vGrey);
			windowGridBatch.Draw();
			windowBorderBatch.Draw();
		modelViewMatrix.PopMatrix();
	modelViewMatrix.PopMatrix();

	projectionMatrix.PushMatrix();
		projectionMatrix.LoadMatrix(orthoMatrix);
		modelViewMatrix.PushMatrix();
			modelViewMatrix.LoadIdentity();
			
			// Take the data from the brightness texture and 
			// blur it in 4 consecutive passes to textures of 
			// decreasing size
			SetupBlurProg();
			for (i =0; i<4; i++)
			{
				glBindFramebuffer(GL_DRAW_FRAMEBUFFER, brightPassFBO[i]); // draws into brightBlurTextures[i+1]
				glDrawBuffers(1, &fboBuffs[0]);
				glViewport(0, 0, screenWidth/((i+1)*3.0), screenHeight/((i+1)*3.0));
				glBindTexture(GL_TEXTURE_2D, brightBlurTextures[i]);
				screenQuad.Draw();
			}

			// Combine original scene with blurred bright textures 
			// to create the bloom effect
			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
			glDrawBuffers(1,windowBuff);
			glViewport(0, 0, screenWidth, screenHeight);
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			SetupHDRProg();
			screenQuad.Draw();
		modelViewMatrix.PopMatrix();
	projectionMatrix.PopMatrix();
	
	// Put the texture units back the way they were
	for (i=5; i>-1; i--)
	{
		glActiveTexture(GL_TEXTURE0+i);
		glBindTexture(GL_TEXTURE_2D, 0);
	}
    
    // Do the buffer Swap
    glutSwapBuffers();
        
    // Do it again
    glutPostRedisplay();
}


int main(int argc, char* argv[])
{
    screenWidth = 800;
    screenHeight = 600;
    bFullScreen = false; 
    bAnimated = true;
    bloomLevel = 0.0;

    cameraFrame.MoveUp(0.50);

	gltSetWorkingDirectory(argv[0]);
		
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(screenWidth,screenHeight);
  
    glutCreateWindow("HDR Bloom");
 
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);

    SetupRC();
    glutMainLoop();    
    ShutdownRC();
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值