From 7825ecd12fd8257ccbb4bfce4c95e2b288ee62e0 Mon Sep 17 00:00:00 2001 From: Anselme Date: Mon, 28 Dec 2015 11:12:53 +0100 Subject: [PATCH] added blur, bloom, redux algorithms, added post processing pipeline --- CMakeLists.txt | 2 + shaders/bloom.frag.glsl | 25 ++++ shaders/blur.frag.glsl | 31 +++++ shaders/forward.frag.glsl | 30 ++--- shaders/forward.vert.glsl | 20 ++-- shaders/hdr.frag.glsl | 12 ++ shaders/luminance.frag.glsl | 12 ++ shaders/posteffect.frag.glsl | 12 -- shaders/posteffect.vert.glsl | 12 -- shaders/reduction.frag.glsl | 27 +++++ src/forwardmodule.cpp | 2 +- src/forwardmodule.h | 2 +- src/framebuffer.cpp | 44 ++++++- src/framebuffer.h | 5 +- src/module.h | 1 + src/posteffectmodule.cpp | 225 ++++++++++++++++++++++++++++------- src/posteffectmodule.h | 68 +++++++++-- src/sparrowrenderer.cpp | 4 +- src/texture.cpp | 88 +++++++++----- src/texture.h | 18 ++- src/textureblur.cpp | 112 +++++++++++++++++ src/textureblur.h | 36 ++++++ src/textureredux.cpp | 61 ++++++++++ src/textureredux.h | 36 ++++++ 24 files changed, 747 insertions(+), 138 deletions(-) create mode 100644 shaders/bloom.frag.glsl create mode 100644 shaders/blur.frag.glsl create mode 100644 shaders/hdr.frag.glsl create mode 100644 shaders/luminance.frag.glsl delete mode 100644 shaders/posteffect.frag.glsl delete mode 100644 shaders/posteffect.vert.glsl create mode 100644 shaders/reduction.frag.glsl create mode 100644 src/textureblur.cpp create mode 100644 src/textureblur.h create mode 100644 src/textureredux.cpp create mode 100644 src/textureredux.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bba575..889b1f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,8 @@ set(LIB_SRC_LIST src/shadersource.cpp src/light.cpp src/posteffectmodule.cpp + src/textureblur.cpp + src/textureredux.cpp ) set(LIBRARY_NAME ${PROJECT_NAME}) diff --git a/shaders/bloom.frag.glsl b/shaders/bloom.frag.glsl new file mode 100644 index 0000000..505b0bd --- /dev/null +++ b/shaders/bloom.frag.glsl @@ -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); +} diff --git a/shaders/blur.frag.glsl b/shaders/blur.frag.glsl new file mode 100644 index 0000000..2b042bc --- /dev/null +++ b/shaders/blur.frag.glsl @@ -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); +} diff --git a/shaders/forward.frag.glsl b/shaders/forward.frag.glsl index 33c95a0..af91c6c 100644 --- a/shaders/forward.frag.glsl +++ b/shaders/forward.frag.glsl @@ -48,14 +48,14 @@ in vec3 halfVecInView; 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){ - float diffuseComponent = max(dot(normal, lightDir), 0); - float specularComponent = max(dot(halfVec, normal), 0); - return color*diffuseComponent*(kd+ks*pow(specularComponent, ns)); + float diffuseComponent = max(dot(normal, lightDir), 0); + float specularComponent = max(dot(halfVec, normal), 0); + return color*diffuseComponent*(kd+ks*pow(specularComponent, ns)); } float computeShadow(sampler2D shadowmap, vec3 shadow){ - float lightFragDepth = texture(shadowmap, shadow.xy).r; - return lightFragDepth < shadow.z ? 0 : 1; + float lightFragDepth = texture(shadowmap, shadow.xy).r; + return lightFragDepth < shadow.z ? 0 : 1; } void main(void) { @@ -65,8 +65,8 @@ void main(void) { #endif #ifdef NORMAL_MAP - vec3 normal = normalize(texture(normalMap, varTexCoord).xyz * tangentSpace); - normal = normalize(vec3(0, 0, 1) * tangentSpace); + vec3 normal = normalize(texture(normalMap, varTexCoord).xyz * tangentSpace); + normal = normalize(vec3(0, 0, 1) * tangentSpace); #else vec3 normal = normalize(varNormal); #endif @@ -78,27 +78,27 @@ void main(void) { #endif #ifdef DIFFUSE_TEXTURE - vec3 diffuse = texture(diffuseTexture, varTexCoord).rgb; + vec3 diffuse = texture(diffuseTexture, varTexCoord).rgb; #else - vec3 diffuse = materialKd; + vec3 diffuse = materialKd; #endif #ifdef SPECULAR_TEXTURE - vec3 specular = vec3(texture(specularTexture, varTexCoord).r); + vec3 specular = vec3(texture(specularTexture, varTexCoord).r); #else vec3 specular = materialKs; #endif #ifdef SHADOWMAP - float shadow = computeShadow(shadowMap, posInLightSpace.xyz/posInLightSpace.w); + float shadow = computeShadow(shadowMap, posInLightSpace.xyz/posInLightSpace.w); #else - float shadow = 1; + float shadow = 1; #endif #ifdef AMBIENT_LIGHT - outColor = vec4(0, 0, 0, 1); + outColor = vec4(0, 0, 0, 1); #else - vec3 light = phongLighting(diffuse, specular, materialNs, lightColor, normal, lightDirInView, halfVecInView); - outColor = vec4(shadow*light, 1); + vec3 light = phongLighting(diffuse, specular, materialNs, lightColor, normal, lightDirInView, halfVecInView); + outColor = vec4(shadow*light, 1); #endif } diff --git a/shaders/forward.vert.glsl b/shaders/forward.vert.glsl index d5a2bbf..e4459ef 100644 --- a/shaders/forward.vert.glsl +++ b/shaders/forward.vert.glsl @@ -44,28 +44,28 @@ void main(void) { #ifdef DIRECTIONNAL_LIGHT lightDirInView = normalize(mat3(viewMatrix)*dirLight); - halfVecInView = normalize(lightDirInView - normalize(posInView)); + halfVecInView = normalize(lightDirInView - normalize(posInView)); #endif #ifdef SHADOWMAP - posInLightSpace = lightMVP * vec4(inPosition, 1.0); + posInLightSpace = lightMVP * vec4(inPosition, 1.0); #endif #ifdef POINT_LIGHT - vec4 lightPosInView = viewMatrix*vec4(pointLight, 1); - lightDirInView = normalize(lightPosInView.xyz - posInView); - halfVecInView = normalize(lightDirInView - normalize(posInView)); + vec4 lightPosInView = viewMatrix*vec4(pointLight, 1); + lightDirInView = normalize(lightPosInView.xyz - posInView); + halfVecInView = normalize(lightDirInView - normalize(posInView)); #endif #ifdef NORMAL_MAP - tangentSpace = transpose(mat3(normalize(normalMatrix*inTangent), - normalize(normalMatrix*inBinormal), - normalize(normalMatrix*inNormal))); + tangentSpace = transpose(mat3(normalize(normalMatrix*inTangent), + normalize(normalMatrix*inBinormal), + normalize(normalMatrix*inNormal))); #else - varNormal = normalize(normalMatrix*inNormal); + varNormal = normalize(normalMatrix*inNormal); #endif varTexCoord = inTexCoord.xy; - gl_Position = MVP * vec4(inPosition, 1.0); + gl_Position = MVP * vec4(inPosition, 1.0); } diff --git a/shaders/hdr.frag.glsl b/shaders/hdr.frag.glsl new file mode 100644 index 0000000..0c51b0d --- /dev/null +++ b/shaders/hdr.frag.glsl @@ -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); +} diff --git a/shaders/luminance.frag.glsl b/shaders/luminance.frag.glsl new file mode 100644 index 0000000..afcece6 --- /dev/null +++ b/shaders/luminance.frag.glsl @@ -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); +} diff --git a/shaders/posteffect.frag.glsl b/shaders/posteffect.frag.glsl deleted file mode 100644 index b06c528..0000000 --- a/shaders/posteffect.frag.glsl +++ /dev/null @@ -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); -} diff --git a/shaders/posteffect.vert.glsl b/shaders/posteffect.vert.glsl deleted file mode 100644 index ab728da..0000000 --- a/shaders/posteffect.vert.glsl +++ /dev/null @@ -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); -} diff --git a/shaders/reduction.frag.glsl b/shaders/reduction.frag.glsl new file mode 100644 index 0000000..0852506 --- /dev/null +++ b/shaders/reduction.frag.glsl @@ -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; +} diff --git a/src/forwardmodule.cpp b/src/forwardmodule.cpp index 1a735e4..6621a51 100644 --- a/src/forwardmodule.cpp +++ b/src/forwardmodule.cpp @@ -208,7 +208,7 @@ void ForwardModule::compileShaders(Scene* scene) } } -void ForwardModule::setRenderTarget(FrameBuffer* target) +void ForwardModule::setRenderTarget(const FrameBuffer* target) { if(target != NULL) renderTarget = target; diff --git a/src/forwardmodule.h b/src/forwardmodule.h index 027fc4b..65a28f5 100644 --- a/src/forwardmodule.h +++ b/src/forwardmodule.h @@ -29,7 +29,7 @@ public: void setShaderSource(ShaderSource* source); void compileShaders(Scene* scene); - void setRenderTarget(FrameBuffer* target); + void setRenderTarget(const FrameBuffer* target); private: static const char* const flagStr[NB_FLAGS]; diff --git a/src/framebuffer.cpp b/src/framebuffer.cpp index c1de13d..1b2ae4a 100644 --- a/src/framebuffer.cpp +++ b/src/framebuffer.cpp @@ -23,6 +23,7 @@ void FrameBuffer::addTexture(Texture* tex, GLenum attachment) bindFBO(); if(tex->isCubeMap()) { + // TODO // http://cg.siomax.ru/index.php/computer-graphics/10-one-pass-rendering-to-cube-map } else @@ -42,11 +43,50 @@ void FrameBuffer::initColorAttachments() bindFBO(); 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) diff --git a/src/framebuffer.h b/src/framebuffer.h index 962ec4e..cd9e164 100644 --- a/src/framebuffer.h +++ b/src/framebuffer.h @@ -10,6 +10,7 @@ class FrameBuffer { private: FrameBuffer(int id) : fbo(id) {} + bool check(); protected: GLuint fbo; @@ -21,8 +22,8 @@ public: ~FrameBuffer(); void addTexture(Texture* tex, GLenum attachment); void initColorAttachments(); - - void bindFBO() const; + void deleteTextures(); + void bindFBO(GLenum target = GL_FRAMEBUFFER) const; Texture* getTexture(int texId); static const FrameBuffer* screen; }; diff --git a/src/module.h b/src/module.h index 00ed72a..d17e00c 100644 --- a/src/module.h +++ b/src/module.h @@ -9,6 +9,7 @@ class Module public: virtual void renderGL(Camera* myCamera, Scene* scene) = 0; virtual bool requiresModernOpenGL() {return true;} + virtual void resize(int width, int height) {} }; #endif // MODULE diff --git a/src/posteffectmodule.cpp b/src/posteffectmodule.cpp index cdf2b4d..2d952cf 100644 --- a/src/posteffectmodule.cpp +++ b/src/posteffectmodule.cpp @@ -3,75 +3,220 @@ #include "glassert.h" #include "texture.h" #include "shader.h" +#include "textureblur.h" +#include "textureredux.h" +#include "shadersource.h" #include const GLfloat PostEffectModule::vertices[] = { - 0.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f + -1.0f, -1.0f, + 3.0f, -1.0f, + -1.0f, 3.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(); - Texture* tex; - // 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; + for(int i=0; isetSource(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) { - if(shader != NULL) + if(shaders[0] != NULL) { - shader->bind(); - shader->bindInteger(colorLocation, COLOR_BUFFER); - shader->bindInteger(depthLocation, DEPTH_BUFFER); - - outputFBO->bindFBO(); - - inputFBO->getTexture(COLOR_BUFFER)->bind(COLOR_BUFFER); - inputFBO->getTexture(DEPTH_BUFFER)->bind(DEPTH_BUFFER); + glAssert(glDisable(GL_BLEND)); + glAssert(glDisable(GL_DEPTH_TEST)); 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; - colorLocation = shader->getLocation("colorSampler"); - depthLocation = shader->getLocation("depthSampler"); + glAssert(glViewport(0, 0, width, height)); + + 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"; + + diff --git a/src/posteffectmodule.h b/src/posteffectmodule.h index 490df0f..ddc65de 100644 --- a/src/posteffectmodule.h +++ b/src/posteffectmodule.h @@ -3,35 +3,81 @@ #include #include +#include #include "module.h" +#include + +#define BLOOM_LUMINANCE_THRESHOLD 0.5f class Shader; class FrameBuffer; +class Texture; +class TextureBlur; +class ShaderSource; +class TextureRedux; 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 vbo; - FrameBuffer* inputFBO; - const FrameBuffer* outputFBO; - Shader* shader; - GLuint colorLocation; - GLuint depthLocation; - glm::mat4 mvp; - 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: PostEffectModule(int width, int height); ~PostEffectModule(); 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 setShader(Shader* myShader); }; #endif // POSTEFFECTMODULE_H diff --git a/src/sparrowrenderer.cpp b/src/sparrowrenderer.cpp index 6a9951e..552f486 100644 --- a/src/sparrowrenderer.cpp +++ b/src/sparrowrenderer.cpp @@ -61,12 +61,14 @@ void SparrowRenderer::resizeGL(int w, int h) height = h; glAssert(glViewport(0, 0, width, height)); camera->resize(width, height); + for(ModuleNode &mn : modules) + mn.module->resize(w, h); } void SparrowRenderer::renderGL() { - glAssert(glViewport(0, 0, width, height)); FrameBuffer::screen->bindFBO(); + glAssert(glViewport(0, 0, width, height)); glAssert(glClearColor(clearColor.r, clearColor.g, clearColor.b, 1)); glAssert(glClearDepth(1.0)); glAssert(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); diff --git a/src/texture.cpp b/src/texture.cpp index 257ab9c..19cf3d3 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -8,46 +8,77 @@ Texture::Texture(GLenum format, int height, GLenum dataType, 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(glBindTexture(target, texId)); - if(target == GL_TEXTURE_2D) + glAssert(glBindTexture(m_target, texId)); + 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); setFiltering(GL_LINEAR); - } - else if(target == GL_TEXTURE_CUBE_MAP) - { + break; + 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) glAssert(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, width, height, 0, format, dataType, NULL)); setWrap(GL_CLAMP_TO_EDGE); 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(glBindTexture(target, texId)); + glAssert(glBindTexture(m_target, texId)); initPixels(myImage, GL_TEXTURE_2D); setWrap(GL_REPEAT); 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(glBindTexture(target, texId)); - + glAssert(glBindTexture(m_target, texId)); for(int i=0; i<6; ++i) - { initPixels(myCubemapImages[i], GL_TEXTURE_CUBE_MAP_POSITIVE_X + i); - setWrap(GL_CLAMP_TO_EDGE); - setFiltering(GL_LINEAR); + setWrap(GL_CLAMP_TO_EDGE); + 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() @@ -60,34 +91,37 @@ void Texture::initPixels(Image* myImage, GLenum target) switch(myImage->depth) { 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; 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; 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; } + glAssert(glTexImage2D(target, 0, m_internal_format, m_width, m_height, 0, m_format, m_dataType, myImage->pixels)); } void Texture::setWrap(GLint wrap) { - glAssert(glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap)); - glAssert(glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap)); - glAssert(glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap)); + glAssert(glTexParameteri(m_target, GL_TEXTURE_WRAP_S, wrap)); + glAssert(glTexParameteri(m_target, GL_TEXTURE_WRAP_T, wrap)); + glAssert(glTexParameteri(m_target, GL_TEXTURE_WRAP_R, wrap)); } void Texture::setFiltering(GLint filter) { - glAssert(glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter)); - glAssert(glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter)); + glAssert(glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, filter)); + glAssert(glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, filter)); } void Texture::bind(int slot) { - GLenum texSlot = GL_TEXTURE0+slot; - glAssert(glActiveTexture(texSlot)); - glAssert(glBindTexture(target, texId)); + glAssert(glActiveTexture(GL_TEXTURE0+slot)); + glAssert(glBindTexture(m_target, texId)); } diff --git a/src/texture.h b/src/texture.h index c913f94..93a18e7 100644 --- a/src/texture.h +++ b/src/texture.h @@ -10,7 +10,12 @@ class Texture { private: 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); public: @@ -24,17 +29,22 @@ public: GLenum dataType = GL_UNSIGNED_BYTE, GLenum texTarget = GL_TEXTURE_2D); // creates a standard texture from an image - Texture(Image* myImage); + Texture(Image* myImage); // creates a cubeMap from 6 images Texture(Image* myCubemapImages[6]); + // creates a texture from another + Texture(Texture* tex, bool halfDim = false); ~Texture(); void bind(int slot); GLuint getId() {return texId;} - GLenum getTarget() {return target;} + GLenum getTarget() {return m_target;} void setWrap(GLint wrap); 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 diff --git a/src/textureblur.cpp b/src/textureblur.cpp new file mode 100644 index 0000000..0ba2f96 --- /dev/null +++ b/src/textureblur.cpp @@ -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; isetWrap(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; ibindFBO(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; igetTexture(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); +} diff --git a/src/textureblur.h b/src/textureblur.h new file mode 100644 index 0000000..2fcef62 --- /dev/null +++ b/src/textureblur.h @@ -0,0 +1,36 @@ +#ifndef TEXTUREBLUR_H +#define TEXTUREBLUR_H + +#include + +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 diff --git a/src/textureredux.cpp b/src/textureredux.cpp new file mode 100644 index 0000000..caaa610 --- /dev/null +++ b/src/textureredux.cpp @@ -0,0 +1,61 @@ +#include "textureredux.h" +#include "texture.h" +#include "glassert.h" +#include "shader.h" +#include + +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; +} diff --git a/src/textureredux.h b/src/textureredux.h new file mode 100644 index 0000000..2295e98 --- /dev/null +++ b/src/textureredux.h @@ -0,0 +1,36 @@ +#ifndef TEXTUREREDUX_H +#define TEXTUREREDUX_H + +#include "framebuffer.h" +#include + +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