added blur, bloom, redux algorithms, added post processing pipeline

This commit is contained in:
Anselme 2015-12-28 11:12:53 +01:00
parent 53a37e6738
commit 7825ecd12f
24 changed files with 747 additions and 138 deletions

View File

@ -28,6 +28,8 @@ set(LIB_SRC_LIST
src/shadersource.cpp src/shadersource.cpp
src/light.cpp src/light.cpp
src/posteffectmodule.cpp src/posteffectmodule.cpp
src/textureblur.cpp
src/textureredux.cpp
) )
set(LIBRARY_NAME ${PROJECT_NAME}) set(LIBRARY_NAME ${PROJECT_NAME})

25
shaders/bloom.frag.glsl Normal file
View File

@ -0,0 +1,25 @@
#version 330 core
uniform sampler2DRect colorSampler;
uniform sampler2DRect blurSampler0;
uniform sampler2DRect blurSampler1;
uniform sampler2DRect blurSampler2;
uniform sampler2DRect blurSampler3;
uniform int width;
uniform int height;
layout(location = 0)out vec3 minMaxMean;
layout(location = 1)out vec4 outColor;
void main(void) {
ivec2 ipos = ivec2(gl_FragCoord.xy);
vec3 color = texelFetch(colorSampler, ipos).xyz;
color += texelFetch(blurSampler0, ipos).xyz;
vec2 pos = vec2(gl_FragCoord.x/width, gl_FragCoord.y/height);
color += texture(blurSampler1, pos).xyz;
color += texture(blurSampler2, pos).xyz;
color += texture(blurSampler3, pos).xyz;
outColor = vec4(color, 1.0);
minMaxMean = vec3(0.2126*color.r + 0.7152*color.g + 0.0722*color.b);
}

31
shaders/blur.frag.glsl Normal file
View File

@ -0,0 +1,31 @@
#version 330 core
// linear sampling gaussian blur (kernel size : 9)
// from http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling
uniform sampler2DRect colorSampler;
layout(location = 0)out vec4 outColor;
uniform int width;
uniform int height;
void main(void)
{
//vec3 color = vec3(0);
vec3 color = texelFetch(colorSampler, ivec2(gl_FragCoord.x/width, gl_FragCoord.y/height)).xyz * 0.2270270270;
#ifdef HORIZONTAL_BLUR
float y = gl_FragCoord.y/height;
color += texture(colorSampler, vec2((gl_FragCoord.x + 1.3846153846)/width, y)).xyz * 0.3162162162;
color += texture(colorSampler, vec2((gl_FragCoord.x - 1.3846153846)/width, y)).xyz * 0.3162162162;
color += texture(colorSampler, vec2((gl_FragCoord.x + 3.2307692308)/width, y)).xyz * 0.0702702703;
color += texture(colorSampler, vec2((gl_FragCoord.x - 3.2307692308)/width, y)).xyz * 0.0702702703;
#else
float x = gl_FragCoord.x/width;
color += texture(colorSampler, vec2(x, (gl_FragCoord.y + 1.3846153846)/height)).xyz * 0.3162162162;
color += texture(colorSampler, vec2(x, (gl_FragCoord.y - 1.3846153846)/height)).xyz * 0.3162162162;
color += texture(colorSampler, vec2(x, (gl_FragCoord.y + 3.2307692308)/height)).xyz * 0.0702702703;
color += texture(colorSampler, vec2(x, (gl_FragCoord.y - 3.2307692308)/height)).xyz * 0.0702702703;
#endif
outColor = vec4(color, 1.0);
}

View File

@ -48,14 +48,14 @@ in vec3 halfVecInView;
layout(location = 0)out vec4 outColor; layout(location = 0)out vec4 outColor;
vec3 phongLighting(in vec3 kd, in vec3 ks, in float ns, in vec3 color, in vec3 normal, in vec3 lightDir, in vec3 halfVec){ vec3 phongLighting(in vec3 kd, in vec3 ks, in float ns, in vec3 color, in vec3 normal, in vec3 lightDir, in vec3 halfVec){
float diffuseComponent = max(dot(normal, lightDir), 0); float diffuseComponent = max(dot(normal, lightDir), 0);
float specularComponent = max(dot(halfVec, normal), 0); float specularComponent = max(dot(halfVec, normal), 0);
return color*diffuseComponent*(kd+ks*pow(specularComponent, ns)); return color*diffuseComponent*(kd+ks*pow(specularComponent, ns));
} }
float computeShadow(sampler2D shadowmap, vec3 shadow){ float computeShadow(sampler2D shadowmap, vec3 shadow){
float lightFragDepth = texture(shadowmap, shadow.xy).r; float lightFragDepth = texture(shadowmap, shadow.xy).r;
return lightFragDepth < shadow.z ? 0 : 1; return lightFragDepth < shadow.z ? 0 : 1;
} }
void main(void) { void main(void) {
@ -65,8 +65,8 @@ void main(void) {
#endif #endif
#ifdef NORMAL_MAP #ifdef NORMAL_MAP
vec3 normal = normalize(texture(normalMap, varTexCoord).xyz * tangentSpace); vec3 normal = normalize(texture(normalMap, varTexCoord).xyz * tangentSpace);
normal = normalize(vec3(0, 0, 1) * tangentSpace); normal = normalize(vec3(0, 0, 1) * tangentSpace);
#else #else
vec3 normal = normalize(varNormal); vec3 normal = normalize(varNormal);
#endif #endif
@ -78,27 +78,27 @@ void main(void) {
#endif #endif
#ifdef DIFFUSE_TEXTURE #ifdef DIFFUSE_TEXTURE
vec3 diffuse = texture(diffuseTexture, varTexCoord).rgb; vec3 diffuse = texture(diffuseTexture, varTexCoord).rgb;
#else #else
vec3 diffuse = materialKd; vec3 diffuse = materialKd;
#endif #endif
#ifdef SPECULAR_TEXTURE #ifdef SPECULAR_TEXTURE
vec3 specular = vec3(texture(specularTexture, varTexCoord).r); vec3 specular = vec3(texture(specularTexture, varTexCoord).r);
#else #else
vec3 specular = materialKs; vec3 specular = materialKs;
#endif #endif
#ifdef SHADOWMAP #ifdef SHADOWMAP
float shadow = computeShadow(shadowMap, posInLightSpace.xyz/posInLightSpace.w); float shadow = computeShadow(shadowMap, posInLightSpace.xyz/posInLightSpace.w);
#else #else
float shadow = 1; float shadow = 1;
#endif #endif
#ifdef AMBIENT_LIGHT #ifdef AMBIENT_LIGHT
outColor = vec4(0, 0, 0, 1); outColor = vec4(0, 0, 0, 1);
#else #else
vec3 light = phongLighting(diffuse, specular, materialNs, lightColor, normal, lightDirInView, halfVecInView); vec3 light = phongLighting(diffuse, specular, materialNs, lightColor, normal, lightDirInView, halfVecInView);
outColor = vec4(shadow*light, 1); outColor = vec4(shadow*light, 1);
#endif #endif
} }

View File

@ -44,28 +44,28 @@ void main(void) {
#ifdef DIRECTIONNAL_LIGHT #ifdef DIRECTIONNAL_LIGHT
lightDirInView = normalize(mat3(viewMatrix)*dirLight); lightDirInView = normalize(mat3(viewMatrix)*dirLight);
halfVecInView = normalize(lightDirInView - normalize(posInView)); halfVecInView = normalize(lightDirInView - normalize(posInView));
#endif #endif
#ifdef SHADOWMAP #ifdef SHADOWMAP
posInLightSpace = lightMVP * vec4(inPosition, 1.0); posInLightSpace = lightMVP * vec4(inPosition, 1.0);
#endif #endif
#ifdef POINT_LIGHT #ifdef POINT_LIGHT
vec4 lightPosInView = viewMatrix*vec4(pointLight, 1); vec4 lightPosInView = viewMatrix*vec4(pointLight, 1);
lightDirInView = normalize(lightPosInView.xyz - posInView); lightDirInView = normalize(lightPosInView.xyz - posInView);
halfVecInView = normalize(lightDirInView - normalize(posInView)); halfVecInView = normalize(lightDirInView - normalize(posInView));
#endif #endif
#ifdef NORMAL_MAP #ifdef NORMAL_MAP
tangentSpace = transpose(mat3(normalize(normalMatrix*inTangent), tangentSpace = transpose(mat3(normalize(normalMatrix*inTangent),
normalize(normalMatrix*inBinormal), normalize(normalMatrix*inBinormal),
normalize(normalMatrix*inNormal))); normalize(normalMatrix*inNormal)));
#else #else
varNormal = normalize(normalMatrix*inNormal); varNormal = normalize(normalMatrix*inNormal);
#endif #endif
varTexCoord = inTexCoord.xy; varTexCoord = inTexCoord.xy;
gl_Position = MVP * vec4(inPosition, 1.0); gl_Position = MVP * vec4(inPosition, 1.0);
} }

12
shaders/hdr.frag.glsl Normal file
View File

@ -0,0 +1,12 @@
#version 330 core
uniform sampler2DRect colorSampler;
uniform vec3 minMaxMean;
layout(location = 0)out vec4 outColor;
void main(void) {
ivec2 pos = ivec2(gl_FragCoord.xy);
outColor = vec4(texelFetch(colorSampler, pos).xyz, 1.0);
}

View File

@ -0,0 +1,12 @@
#version 330 core
uniform sampler2DRect colorSampler;
uniform float threshold;
layout(location = 0)out vec3 filter;
void main(void) {
vec3 color = texelFetch(colorSampler, ivec2(gl_FragCoord.xy)).xyz;
float luminance = 0.2126*color.r + 0.7152*color.g + 0.0722*color.b;
filter = luminance > threshold ? color : vec3(0);
}

View File

@ -1,12 +0,0 @@
#version 330 core
uniform sampler2DRect colorSampler;
uniform sampler2DRect depthSampler;
in ivec2 varTexCoord;
layout(location = 0)out vec4 outColor;
void main(void) {
outColor = texelFetch(colorSampler, varTexCoord);
}

View File

@ -1,12 +0,0 @@
#version 330 core
uniform mat4 MVP;
out vec2 varTexCoord;
layout(location = 0)in vec3 inPosition;
void main(void) {
varTexCoord = inPosition.xy;
gl_Position = MVP * vec4(inPosition, 1.0);
}

View File

@ -0,0 +1,27 @@
#version 330 core
uniform sampler2DRect minMaxMeanSampler;
uniform int width;
uniform int height;
layout(location = 0)out vec3 outValues;
void main(void) {
vec3 val[3];
ivec2 pos = ivec2(gl_FragCoord.xy)*2;
// fetching 4 texels
outValues = texelFetch(minMaxMeanSampler, pos).xyz;
val[0] = texelFetch(minMaxMeanSampler, pos + ivec2(1, 0)).xyz;
val[1] = texelFetch(minMaxMeanSampler, pos + ivec2(0, 1)).xyz;
val[2] = texelFetch(minMaxMeanSampler, pos + ivec2(1, 1)).xyz;
// computing min, max, and mean for these 4 texels
for(int i=0; i<3; ++i)
{
outValues.x = min(outValues.x, val[i].x);
outValues.y = max(outValues.y, val[i].y);
outValues.z += val[i].z;
}
outValues.z /= 4;
}

View File

@ -208,7 +208,7 @@ void ForwardModule::compileShaders(Scene* scene)
} }
} }
void ForwardModule::setRenderTarget(FrameBuffer* target) void ForwardModule::setRenderTarget(const FrameBuffer* target)
{ {
if(target != NULL) if(target != NULL)
renderTarget = target; renderTarget = target;

View File

@ -29,7 +29,7 @@ public:
void setShaderSource(ShaderSource* source); void setShaderSource(ShaderSource* source);
void compileShaders(Scene* scene); void compileShaders(Scene* scene);
void setRenderTarget(FrameBuffer* target); void setRenderTarget(const FrameBuffer* target);
private: private:
static const char* const flagStr[NB_FLAGS]; static const char* const flagStr[NB_FLAGS];

View File

@ -23,6 +23,7 @@ void FrameBuffer::addTexture(Texture* tex, GLenum attachment)
bindFBO(); bindFBO();
if(tex->isCubeMap()) if(tex->isCubeMap())
{ {
// TODO
// http://cg.siomax.ru/index.php/computer-graphics/10-one-pass-rendering-to-cube-map // http://cg.siomax.ru/index.php/computer-graphics/10-one-pass-rendering-to-cube-map
} }
else else
@ -42,11 +43,50 @@ void FrameBuffer::initColorAttachments()
bindFBO(); bindFBO();
glAssert(glDrawBuffers(attachments.size(), attachments.data())); glAssert(glDrawBuffers(attachments.size(), attachments.data()));
} }
else
glAssert(glDrawBuffer(GL_NONE));
check();
} }
void FrameBuffer::bindFBO() const void FrameBuffer::deleteTextures()
{ {
glAssert(glBindFramebuffer(GL_FRAMEBUFFER, fbo)); for(Texture* t : textures)
delete(t);
}
void FrameBuffer::bindFBO(GLenum target) const
{
glAssert(glBindFramebuffer(target, fbo));
}
bool FrameBuffer::check()
{
GLenum err;
glAssert(err = glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (err != GL_FRAMEBUFFER_COMPLETE) {
std::cerr << "FBO not complete (error = " << err << ") : ";
switch (err) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
std::cerr << "GL_FRAMEBUFFER_UNSUPPORTED";
break;
default:
std::cerr << "Unknown ERROR";
}
std::cerr << std::endl;
}
return err == GL_FRAMEBUFFER_COMPLETE;
} }
Texture* FrameBuffer::getTexture(int texId) Texture* FrameBuffer::getTexture(int texId)

View File

@ -10,6 +10,7 @@ class FrameBuffer
{ {
private: private:
FrameBuffer(int id) : fbo(id) {} FrameBuffer(int id) : fbo(id) {}
bool check();
protected: protected:
GLuint fbo; GLuint fbo;
@ -21,8 +22,8 @@ public:
~FrameBuffer(); ~FrameBuffer();
void addTexture(Texture* tex, GLenum attachment); void addTexture(Texture* tex, GLenum attachment);
void initColorAttachments(); void initColorAttachments();
void deleteTextures();
void bindFBO() const; void bindFBO(GLenum target = GL_FRAMEBUFFER) const;
Texture* getTexture(int texId); Texture* getTexture(int texId);
static const FrameBuffer* screen; static const FrameBuffer* screen;
}; };

View File

@ -9,6 +9,7 @@ class Module
public: public:
virtual void renderGL(Camera* myCamera, Scene* scene) = 0; virtual void renderGL(Camera* myCamera, Scene* scene) = 0;
virtual bool requiresModernOpenGL() {return true;} virtual bool requiresModernOpenGL() {return true;}
virtual void resize(int width, int height) {}
}; };
#endif // MODULE #endif // MODULE

View File

@ -3,75 +3,220 @@
#include "glassert.h" #include "glassert.h"
#include "texture.h" #include "texture.h"
#include "shader.h" #include "shader.h"
#include "textureblur.h"
#include "textureredux.h"
#include "shadersource.h"
#include <glm/ext.hpp> #include <glm/ext.hpp>
const GLfloat PostEffectModule::vertices[] = { const GLfloat PostEffectModule::vertices[] = {
0.0f, 1.0f, 0.0f, -1.0f, -1.0f,
1.0f, 1.0f, 0.0f, 3.0f, -1.0f,
1.0f, 0.0f, 0.0f, -1.0f, 3.0f
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f
}; };
PostEffectModule::PostEffectModule(int width, int height) PostEffectModule::PostEffectModule(int width, int height) :
outputFBO(FrameBuffer::screen),
frameBuffers(NULL),
blur(NULL),
redux(NULL),
blurSource(NULL)
{ {
inputFBO = new FrameBuffer(); for(int i=0; i<NB_SHADERS; ++i)
Texture* tex; shaders[i] = NULL;
// Color buffer
tex = new Texture(GL_RGBA, GL_RGBA, width, height);
tex->setFiltering(GL_NEAREST);
inputFBO->addTexture(tex, GL_COLOR_ATTACHMENT1);
// Depth buffer
tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, width, height, GL_FLOAT);
tex->setFiltering(GL_NEAREST);
inputFBO->addTexture(tex, GL_DEPTH_ATTACHMENT);
inputFBO->initColorAttachments();
outputFBO = FrameBuffer::screen;
// set up vao // set up vao
glAssert(glGenVertexArrays(1, &vao)); glAssert(glGenVertexArrays(1, &vao));
glAssert(glBindVertexArray(vao)); glAssert(glBindVertexArray(vao));
glAssert(glGenBuffers(1, &vbo)); glAssert(glGenBuffers(1, &vbo));
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo)); glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo));
glAssert(glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GLfloat), vertices, GL_STATIC_DRAW)); glAssert(glBufferData(GL_ARRAY_BUFFER, 3 * 2 * sizeof(GLfloat), vertices, GL_STATIC_DRAW));
glAssert(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, NULL)); glAssert(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, NULL));
glAssert(glEnableVertexAttribArray(0)); glAssert(glEnableVertexAttribArray(0));
mvp = glm::ortho(0, width, 0, height); resize(width, height);
} }
PostEffectModule::~PostEffectModule() PostEffectModule::~PostEffectModule()
{ {
glAssert(glDeleteVertexArrays(1, &vao)); glAssert(glDeleteVertexArrays(1, &vao));
glAssert(glDeleteBuffers(1, &vbo)); glAssert(glDeleteBuffers(1, &vbo));
delete(inputFBO); for(int i=0; i<NB_FBO; ++i)
frameBuffers[i].deleteTextures();
delete[](frameBuffers);
}
void PostEffectModule::resize(int w, int h)
{
width = w;
height = h;
Texture* tex;
if(frameBuffers != NULL)
{
for(int i=0; i<NB_FBO; ++i)
frameBuffers[i].deleteTextures();
delete[](frameBuffers);
}
frameBuffers = new FrameBuffer[NB_FBO];
// creating input FBO
// Color buffer
tex = new Texture(GL_RGBA, GL_RGBA16F, width, height, GL_HALF_FLOAT, GL_TEXTURE_RECTANGLE);
frameBuffers[INPUT_FBO].addTexture(tex, GL_COLOR_ATTACHMENT0);
// Depth buffer
tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, width, height, GL_FLOAT, GL_TEXTURE_RECTANGLE);
frameBuffers[INPUT_FBO].addTexture(tex, GL_DEPTH_ATTACHMENT);
frameBuffers[INPUT_FBO].initColorAttachments();
// creating luminance FBO
// luminance filter
tex = new Texture(GL_RGB, GL_RGB16F, width, height, GL_HALF_FLOAT, GL_TEXTURE_RECTANGLE);
frameBuffers[LUMINANCE_FBO].addTexture(tex, GL_COLOR_ATTACHMENT0);
frameBuffers[LUMINANCE_FBO].initColorAttachments();
// creating bloom FBO
// min/max/mean buffers
tex = new Texture(GL_RGB, GL_RGB32F, width, height, GL_FLOAT, GL_TEXTURE_RECTANGLE);
frameBuffers[BLOOM_FBO].addTexture(tex, GL_COLOR_ATTACHMENT0);
// color buffer
tex = new Texture(frameBuffers[INPUT_FBO].getTexture(0));
frameBuffers[BLOOM_FBO].addTexture(tex, GL_COLOR_ATTACHMENT1);
frameBuffers[BLOOM_FBO].initColorAttachments();
// blur and redux tools
if(blur != NULL)
delete(blur);
blur = new TextureBlur(frameBuffers + LUMINANCE_FBO);
if(blurSource != NULL)
blur->setSource(blurSource);
if(redux != NULL)
delete(redux);
redux = new TextureRedux(frameBuffers + BLOOM_FBO);
if(shaders[REDUX_SHADER] != NULL)
redux->setShader(shaders[REDUX_SHADER]);
} }
void PostEffectModule::renderGL(Camera* myCamera, Scene* scene) void PostEffectModule::renderGL(Camera* myCamera, Scene* scene)
{ {
if(shader != NULL) if(shaders[0] != NULL)
{ {
shader->bind(); glAssert(glDisable(GL_BLEND));
shader->bindInteger(colorLocation, COLOR_BUFFER); glAssert(glDisable(GL_DEPTH_TEST));
shader->bindInteger(depthLocation, DEPTH_BUFFER);
outputFBO->bindFBO();
inputFBO->getTexture(COLOR_BUFFER)->bind(COLOR_BUFFER);
inputFBO->getTexture(DEPTH_BUFFER)->bind(DEPTH_BUFFER);
glAssert(glBindVertexArray(vao)); glAssert(glBindVertexArray(vao));
glAssert(glDrawArrays(GL_TRIANGLES, 0, 36));
luminanceStep();
bloomStep();
hdrStep();
glAssert(glEnable(GL_DEPTH_TEST));
} }
frameBuffers[INPUT_FBO].bindFBO();
glAssert(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
} }
void PostEffectModule::setShader(Shader* myShader) void PostEffectModule::luminanceStep()
{ {
shader = myShader; glAssert(glViewport(0, 0, width, height));
colorLocation = shader->getLocation("colorSampler");
depthLocation = shader->getLocation("depthSampler"); frameBuffers[LUMINANCE_FBO].bindFBO();
glAssert(glClear(GL_COLOR_BUFFER_BIT));
shaders[LUMINANCE_SHADER]->bind();
shaders[LUMINANCE_SHADER]->bindInteger(shaders[LUMINANCE_SHADER]->getLocation("colorSampler"), 0);
frameBuffers[INPUT_FBO].getTexture(0)->bind(0);
shaders[LUMINANCE_SHADER]->bindFloat(shaders[LUMINANCE_SHADER]->getLocation("threshold"), BLOOM_LUMINANCE_THRESHOLD);
glAssert(glDrawArrays(GL_TRIANGLES, 0, 3));
} }
void PostEffectModule::bloomStep()
{
blur->blur();
glAssert(glViewport(0, 0, width, height));
frameBuffers[BLOOM_FBO].bindFBO();
glAssert(glClear(GL_COLOR_BUFFER_BIT));
shaders[BLOOM_SHADER]->bind();
shaders[BLOOM_SHADER]->bindInteger(shaders[BLOOM_SHADER]->getLocation("colorSampler"), 0);
frameBuffers[INPUT_FBO].getTexture(0)->bind(0);
shaders[BLOOM_SHADER]->bindInteger(shaders[BLOOM_SHADER]->getLocation("blurSampler0"), 1);
blur->getTexture(0)->bind(1);
shaders[BLOOM_SHADER]->bindInteger(shaders[BLOOM_SHADER]->getLocation("blurSampler1"), 2);
blur->getTexture(1)->bind(2);
shaders[BLOOM_SHADER]->bindInteger(shaders[BLOOM_SHADER]->getLocation("blurSampler2"), 3);
blur->getTexture(2)->bind(3);
shaders[BLOOM_SHADER]->bindInteger(shaders[BLOOM_SHADER]->getLocation("blurSampler3"), 4);
blur->getTexture(3)->bind(4);
shaders[BLOOM_SHADER]->bindInteger(shaders[BLOOM_SHADER]->getLocation("width"), width);
shaders[BLOOM_SHADER]->bindInteger(shaders[BLOOM_SHADER]->getLocation("height"), height);
glAssert(glDrawArrays(GL_TRIANGLES, 0, 3));
}
void PostEffectModule::hdrStep()
{
glm::vec3 minMaxMean = redux->redux();
glAssert(glViewport(0, 0, width, height));
outputFBO->bindFBO();
glAssert(glClear(GL_COLOR_BUFFER_BIT));
shaders[HDR_SHADER]->bind();
shaders[HDR_SHADER]->bindInteger(shaders[HDR_SHADER]->getLocation("colorSampler"), 0);
frameBuffers[BLOOM_FBO].getTexture(1)->bind(0);
shaders[HDR_SHADER]->bindVec3(shaders[HDR_SHADER]->getLocation("minMaxMean"), minMaxMean);
glAssert(glDrawArrays(GL_TRIANGLES, 0, 3));
}
void PostEffectModule::setShaders(const std::string &luminanceFragSource,
const std::string &bloomFragSource,
const std::string &hdrFragSource,
const std::string &blurFragSource,
const std::string &reduxFragSource)
{
shaders[LUMINANCE_SHADER] = new Shader(vertSource, luminanceFragSource);
shaders[BLOOM_SHADER] = new Shader(vertSource, bloomFragSource);
shaders[HDR_SHADER] = new Shader(vertSource, hdrFragSource);
shaders[REDUX_SHADER] = new Shader(vertSource, reduxFragSource);
redux->setShader(shaders[REDUX_SHADER]);
if(blurSource != NULL)
delete(blurSource);
blurSource = new ShaderSource();
blurSource->setSource(vertSource.c_str(), ShaderSource::VERTEX);
blurSource->setSource(blurFragSource.c_str(), ShaderSource::FRAGMENT);
blur->setSource(blurSource);
}
const std::string PostEffectModule::vertSource =
"#version 330 core\n\
layout(location = 0)in vec2 inPosition;\n\
void main(void) {\n\
gl_Position = vec4(inPosition, 0.0, 1.0);\n\
}\n";

View File

@ -3,35 +3,81 @@
#include <glew/glew.h> #include <glew/glew.h>
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include "module.h" #include "module.h"
#include <string>
#define BLOOM_LUMINANCE_THRESHOLD 0.5f
class Shader; class Shader;
class FrameBuffer; class FrameBuffer;
class Texture;
class TextureBlur;
class ShaderSource;
class TextureRedux;
class PostEffectModule : public Module class PostEffectModule : public Module
{ {
// framebuffers
enum
{
INPUT_FBO,
LUMINANCE_FBO,
BLOOM_FBO,
NB_FBO
};
FrameBuffer* frameBuffers;
const FrameBuffer* outputFBO;
// shaders
enum
{
LUMINANCE_SHADER,
BLOOM_SHADER,
HDR_SHADER,
REDUX_SHADER,
NB_SHADERS
};
static const std::string vertSource;
Shader* shaders[NB_SHADERS];
// dimensions
int width;
int height;
// geometry (one triangle)
GLuint vao; GLuint vao;
GLuint vbo; GLuint vbo;
FrameBuffer* inputFBO;
const FrameBuffer* outputFBO;
Shader* shader;
GLuint colorLocation;
GLuint depthLocation;
glm::mat4 mvp;
static const GLfloat vertices[]; static const GLfloat vertices[];
enum Buffers {COLOR_BUFFER, DEPTH_BUFFER}; // post processing pipeline
void luminanceStep();
void bloomStep();
void hdrStep();
TextureBlur* blur;
ShaderSource* blurSource;
TextureRedux* redux;
public: public:
PostEffectModule(int width, int height); PostEffectModule(int width, int height);
~PostEffectModule(); ~PostEffectModule();
virtual void renderGL(Camera* myCamera, Scene* scene); virtual void renderGL(Camera* myCamera, Scene* scene);
virtual void resize(int w, int h);
/**
* @brief setShaders must be called to set the 5 required fragment shaders
*/
void setShaders(const std::string &luminanceSource,
const std::string &bloomSource,
const std::string &hdrSource,
const std::string &blurSource,
const std::string &reduxSource);
FrameBuffer* getInputFBO() {return frameBuffers;}
FrameBuffer* getInputFBO() {return inputFBO;}
void setRenderTarget(FrameBuffer* renderTarget) {outputFBO = renderTarget;} void setRenderTarget(FrameBuffer* renderTarget) {outputFBO = renderTarget;}
void setShader(Shader* myShader);
}; };
#endif // POSTEFFECTMODULE_H #endif // POSTEFFECTMODULE_H

View File

@ -61,12 +61,14 @@ void SparrowRenderer::resizeGL(int w, int h)
height = h; height = h;
glAssert(glViewport(0, 0, width, height)); glAssert(glViewport(0, 0, width, height));
camera->resize(width, height); camera->resize(width, height);
for(ModuleNode &mn : modules)
mn.module->resize(w, h);
} }
void SparrowRenderer::renderGL() void SparrowRenderer::renderGL()
{ {
glAssert(glViewport(0, 0, width, height));
FrameBuffer::screen->bindFBO(); FrameBuffer::screen->bindFBO();
glAssert(glViewport(0, 0, width, height));
glAssert(glClearColor(clearColor.r, clearColor.g, clearColor.b, 1)); glAssert(glClearColor(clearColor.r, clearColor.g, clearColor.b, 1));
glAssert(glClearDepth(1.0)); glAssert(glClearDepth(1.0));
glAssert(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); glAssert(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));

View File

@ -8,46 +8,77 @@ Texture::Texture(GLenum format,
int height, int height,
GLenum dataType, GLenum dataType,
GLenum texTarget) : GLenum texTarget) :
target(texTarget) m_target(texTarget),
m_format(format),
m_internal_format(internal_format),
m_width(width),
m_height(height),
m_dataType(dataType)
{ {
glAssert(glGenTextures(1, &texId)); glAssert(glGenTextures(1, &texId));
glAssert(glBindTexture(target, texId)); glAssert(glBindTexture(m_target, texId));
if(target == GL_TEXTURE_2D) switch(m_target)
{ {
glAssert(glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, dataType, NULL)); case GL_TEXTURE_2D :
glAssert(glTexImage2D(m_target, 0, internal_format, width, height, 0, format, dataType, NULL));
setWrap(GL_REPEAT); setWrap(GL_REPEAT);
setFiltering(GL_LINEAR); setFiltering(GL_LINEAR);
} break;
else if(target == GL_TEXTURE_CUBE_MAP) case GL_TEXTURE_RECTANGLE :
{ glAssert(glTexImage2D(m_target, 0, internal_format, width, height, 0, format, dataType, NULL));
setWrap(GL_CLAMP_TO_EDGE);
setFiltering(GL_LINEAR);
break;
case GL_TEXTURE_CUBE_MAP :
for(int i=0; i<6; ++i) for(int i=0; i<6; ++i)
glAssert(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, width, height, 0, format, dataType, NULL)); glAssert(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, width, height, 0, format, dataType, NULL));
setWrap(GL_CLAMP_TO_EDGE); setWrap(GL_CLAMP_TO_EDGE);
setFiltering(GL_LINEAR); setFiltering(GL_LINEAR);
break;
} }
} }
Texture::Texture(Image* myImage) : target(GL_TEXTURE_2D) Texture::Texture(Image* myImage) :
m_target(GL_TEXTURE_2D),
m_width(myImage->width),
m_height(myImage->height),
m_dataType(GL_UNSIGNED_BYTE)
{ {
glAssert(glGenTextures(1, &texId)); glAssert(glGenTextures(1, &texId));
glAssert(glBindTexture(target, texId)); glAssert(glBindTexture(m_target, texId));
initPixels(myImage, GL_TEXTURE_2D); initPixels(myImage, GL_TEXTURE_2D);
setWrap(GL_REPEAT); setWrap(GL_REPEAT);
setFiltering(GL_LINEAR); setFiltering(GL_LINEAR);
} }
Texture::Texture(Image* myCubemapImages[6]) : target(GL_TEXTURE_CUBE_MAP) Texture::Texture(Image* myCubemapImages[6]) :
m_target(GL_TEXTURE_CUBE_MAP),
m_dataType(GL_UNSIGNED_BYTE)
{ {
glAssert(glActiveTexture(GL_TEXTURE0));
glAssert(glGenTextures(1, &texId)); glAssert(glGenTextures(1, &texId));
glAssert(glBindTexture(target, texId)); glAssert(glBindTexture(m_target, texId));
for(int i=0; i<6; ++i) for(int i=0; i<6; ++i)
{
initPixels(myCubemapImages[i], GL_TEXTURE_CUBE_MAP_POSITIVE_X + i); initPixels(myCubemapImages[i], GL_TEXTURE_CUBE_MAP_POSITIVE_X + i);
setWrap(GL_CLAMP_TO_EDGE); setWrap(GL_CLAMP_TO_EDGE);
setFiltering(GL_LINEAR); setFiltering(GL_LINEAR);
}
Texture::Texture(Texture* tex, bool halfDim) :
m_target(tex->m_target),
m_format(tex->m_format),
m_internal_format(tex->m_internal_format),
m_width(tex->m_width),
m_height(tex->m_height),
m_dataType(tex->m_dataType)
{
glAssert(glGenTextures(1, &texId));
glAssert(glBindTexture(m_target, texId));
if(halfDim)
{
m_width /= 2;
m_height /= 2;
} }
glAssert(glTexImage2D(m_target, 0, m_internal_format, m_width, m_height, 0, m_format, m_dataType, NULL));
} }
Texture::~Texture() Texture::~Texture()
@ -60,34 +91,37 @@ void Texture::initPixels(Image* myImage, GLenum target)
switch(myImage->depth) switch(myImage->depth)
{ {
case 32: case 32:
glAssert(glTexImage2D(target, 0, GL_RGBA, myImage->width, myImage->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, myImage->pixels)); m_format = GL_RGBA;
m_internal_format = GL_RGBA;
break; break;
case 24: case 24:
glAssert(glTexImage2D(target, 0, GL_RGB, myImage->width, myImage->height, 0, GL_RGB, GL_UNSIGNED_BYTE, myImage->pixels)); m_format = GL_RGB;
m_internal_format = GL_RGB;
break; break;
case 8: case 8:
glAssert(glTexImage2D(target, 0, GL_R8, myImage->width, myImage->height, 0, GL_RED, GL_UNSIGNED_BYTE, myImage->pixels)); m_format = GL_RED;
m_internal_format = GL_R8;
break; break;
} }
glAssert(glTexImage2D(target, 0, m_internal_format, m_width, m_height, 0, m_format, m_dataType, myImage->pixels));
} }
void Texture::setWrap(GLint wrap) void Texture::setWrap(GLint wrap)
{ {
glAssert(glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap)); glAssert(glTexParameteri(m_target, GL_TEXTURE_WRAP_S, wrap));
glAssert(glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap)); glAssert(glTexParameteri(m_target, GL_TEXTURE_WRAP_T, wrap));
glAssert(glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap)); glAssert(glTexParameteri(m_target, GL_TEXTURE_WRAP_R, wrap));
} }
void Texture::setFiltering(GLint filter) void Texture::setFiltering(GLint filter)
{ {
glAssert(glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter)); glAssert(glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, filter));
glAssert(glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter)); glAssert(glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, filter));
} }
void Texture::bind(int slot) void Texture::bind(int slot)
{ {
GLenum texSlot = GL_TEXTURE0+slot; glAssert(glActiveTexture(GL_TEXTURE0+slot));
glAssert(glActiveTexture(texSlot)); glAssert(glBindTexture(m_target, texId));
glAssert(glBindTexture(target, texId));
} }

View File

@ -10,7 +10,12 @@ class Texture
{ {
private: private:
GLuint texId; GLuint texId;
GLenum target; GLenum m_target;
GLenum m_format;
GLenum m_internal_format;
int m_width;
int m_height;
GLenum m_dataType;
void initPixels(Image* myImage, GLenum textureSlot); void initPixels(Image* myImage, GLenum textureSlot);
public: public:
@ -24,17 +29,22 @@ public:
GLenum dataType = GL_UNSIGNED_BYTE, GLenum dataType = GL_UNSIGNED_BYTE,
GLenum texTarget = GL_TEXTURE_2D); GLenum texTarget = GL_TEXTURE_2D);
// creates a standard texture from an image // creates a standard texture from an image
Texture(Image* myImage); Texture(Image* myImage);
// creates a cubeMap from 6 images // creates a cubeMap from 6 images
Texture(Image* myCubemapImages[6]); Texture(Image* myCubemapImages[6]);
// creates a texture from another
Texture(Texture* tex, bool halfDim = false);
~Texture(); ~Texture();
void bind(int slot); void bind(int slot);
GLuint getId() {return texId;} GLuint getId() {return texId;}
GLenum getTarget() {return target;} GLenum getTarget() {return m_target;}
void setWrap(GLint wrap); void setWrap(GLint wrap);
void setFiltering(GLint filter); void setFiltering(GLint filter);
bool isCubeMap() {return target == GL_TEXTURE_CUBE_MAP;} bool isCubeMap() {return m_target == GL_TEXTURE_CUBE_MAP;}
int getWidth() {return m_width;}
int getHeight() {return m_height;}
}; };
#endif // TEXTURE_H #endif // TEXTURE_H

112
src/textureblur.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "textureblur.h"
#include "framebuffer.h"
#include "shader.h"
#include "shadersource.h"
#include "texture.h"
#include "glassert.h"
TextureBlur::TextureBlur(FrameBuffer* input, int downsampling, int textureId) :
m_input(input),
m_downsampling(downsampling),
horizontal(NULL),
vertical(NULL)
{
Texture* srcTexture = input->getTexture(textureId);
int nb_fbos = (downsampling+1)*2;
fbos = new FrameBuffer[nb_fbos];
Texture* tex = srcTexture;
for(int i=0; i<nb_fbos/2; ++i)
{
// ping FBOs
fbos[i].addTexture(tex, GL_COLOR_ATTACHMENT0);
fbos[i].initColorAttachments();
tex = new Texture(tex, false);
tex->setWrap(GL_CLAMP_TO_EDGE);
tex->setFiltering(GL_LINEAR);
// pong FBOs
fbos[i+nb_fbos/2].addTexture(tex, GL_COLOR_ATTACHMENT0);
fbos[i+nb_fbos/2].initColorAttachments();
if(i < nb_fbos-1)
{
tex = new Texture(tex, true);
tex->setWrap(GL_CLAMP_TO_EDGE);
tex->setFiltering(GL_LINEAR);
}
}
}
TextureBlur::~TextureBlur()
{
for(int i=1; i<(m_downsampling+1)*2; ++i)
delete(fbos[i].getTexture(0));
delete[](fbos);
}
void TextureBlur::setSource(ShaderSource* source)
{
const char* horizontalDefine = "HORIZONTAL_BLUR";
horizontal = source->compile(1, &horizontalDefine);
const char* verticalDefine = "VERTICAL_BLUR";
vertical = source->compile(1, &verticalDefine);
uniformLocations[H_SAMPLER] = horizontal->getLocation("colorSampler");
uniformLocations[H_WIDTH] = horizontal->getLocation("width");
uniformLocations[H_HEIGHT] = horizontal->getLocation("height");
uniformLocations[V_SAMPLER] = vertical->getLocation("colorSampler");
uniformLocations[V_WIDTH] = vertical->getLocation("width");
uniformLocations[V_HEIGHT] = vertical->getLocation("height");
}
void TextureBlur::blur()
{
// downsampling
for(int i=0; i<m_downsampling; ++i)
{
FrameBuffer* src = fbos + i;
FrameBuffer* dst = fbos + (i + 1);
src->bindFBO(GL_READ_FRAMEBUFFER);
dst->bindFBO(GL_DRAW_FRAMEBUFFER);
glAssert(glBlitFramebuffer(0, 0,
src->getTexture(0)->getWidth(),
src->getTexture(0)->getHeight(),
0, 0,
dst->getTexture(0)->getWidth(),
dst->getTexture(0)->getHeight(),
GL_COLOR_BUFFER_BIT, GL_LINEAR));
}
// blurring
for(int i=0; i<m_downsampling+1; ++i)
{
FrameBuffer* ping = fbos + i;
FrameBuffer* pong = ping + (m_downsampling + 1);
Texture* tex = ping->getTexture(0);
glAssert(glViewport(0, 0, tex->getWidth(), tex->getHeight()));
// ping (horizontal blur)
horizontal->bind();
pong->bindFBO();
tex->bind(0);
horizontal->bindInteger(uniformLocations[H_SAMPLER], 0);
horizontal->bindInteger(uniformLocations[H_WIDTH], tex->getWidth());
horizontal->bindInteger(uniformLocations[H_HEIGHT], tex->getHeight());
glAssert(glDrawArrays(GL_TRIANGLES, 0, 3));
// pong (vertical blur)
vertical->bind();
ping->bindFBO();
tex = pong->getTexture(0);
tex->bind(0);
vertical->bindInteger(uniformLocations[V_SAMPLER], 0);
vertical->bindInteger(uniformLocations[V_WIDTH], tex->getWidth());
vertical->bindInteger(uniformLocations[V_HEIGHT], tex->getHeight());
glAssert(glDrawArrays(GL_TRIANGLES, 0, 3));
}
}
Texture* TextureBlur::getTexture(int downsamplingLevel)
{
return fbos[downsamplingLevel].getTexture(0);
}

36
src/textureblur.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef TEXTUREBLUR_H
#define TEXTUREBLUR_H
#include <glew/glew.h>
class FrameBuffer;
class Shader;
class ShaderSource;
class Texture;
class TextureBlur
{
FrameBuffer* fbos;
FrameBuffer* m_input;
int m_downsampling;
Shader* horizontal;
Shader* vertical;
enum UniformLocation {
H_SAMPLER,
H_WIDTH,
H_HEIGHT,
V_SAMPLER,
V_WIDTH,
V_HEIGHT
};
GLuint uniformLocations[6];
public:
TextureBlur(FrameBuffer* input, int downsampling = 3, int textureId = 0);
~TextureBlur();
void setSource(ShaderSource* source);
void blur();
Texture* getTexture(int downsamplingLevel);
};
#endif // TEXTUREBLUR_H

61
src/textureredux.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "textureredux.h"
#include "texture.h"
#include "glassert.h"
#include "shader.h"
#include <glm/ext.hpp>
TextureRedux::TextureRedux(FrameBuffer* input, int textureId) :
m_shader(NULL)
{
Texture* inputTexture = input->getTexture(textureId);
Texture* tex = new Texture(inputTexture, true);
ping.addTexture(tex, GL_COLOR_ATTACHMENT0);
pong.addTexture(inputTexture, GL_COLOR_ATTACHMENT0);
ping.initColorAttachments();
pong.initColorAttachments();
}
TextureRedux::~TextureRedux()
{
delete(ping.getTexture(0));
}
void TextureRedux::setShader(Shader* shader)
{
m_shader = shader;
uniformLocations[SAMPLER] = shader->getLocation("colorSampler");
uniformLocations[WIDTH] = shader->getLocation("width");
uniformLocations[HEIGTH] = shader->getLocation("height");
}
glm::vec3 TextureRedux::redux()
{
bool inverted = false;
int tempWidth = pong.getTexture(0)->getWidth();
int tempHeight = pong.getTexture(0)->getHeight();
while(tempWidth > 1 && tempHeight > 1)
{
m_shader->bind();
m_shader->bindInteger(uniformLocations[SAMPLER], 0);
m_shader->bindInteger(uniformLocations[WIDTH], tempWidth);
m_shader->bindInteger(uniformLocations[HEIGTH], tempHeight);
if(inverted)
{
ping.getTexture(0)->bind(0);
pong.bindFBO();
}
else
{
ping.bindFBO();
pong.getTexture(0)->bind(0);
}
tempWidth /= 2;
tempHeight /= 2;
glViewport(0, 0, tempWidth, tempHeight);
glAssert(glDrawArrays(GL_TRIANGLES, 0, 3));
}
glm::vec3 minMaxMean;
glAssert(glReadPixels(0, 0, 0, 0, GL_RGB, GL_FLOAT, glm::value_ptr(minMaxMean)));
return minMaxMean;
}

36
src/textureredux.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef TEXTUREREDUX_H
#define TEXTUREREDUX_H
#include "framebuffer.h"
#include <glm/vec3.hpp>
class FrameBuffer;
class Shader;
class TextureRedux
{
FrameBuffer ping;
FrameBuffer pong;
FrameBuffer* m_input;
Shader* m_shader;
enum UniformLocation {
SAMPLER,
WIDTH,
HEIGTH,
};
GLuint uniformLocations[3];
public:
/**
* the input texture must be of type GL_FLOAT and format GL_RGB
*/
TextureRedux(FrameBuffer* input, int textureId = 0);
~TextureRedux();
void setShader(Shader* shader);
glm::vec3 redux();
};
#endif // TEXTUREREDUX_H