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/light.cpp
src/posteffectmodule.cpp
src/textureblur.cpp
src/textureredux.cpp
)
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;
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
}

View File

@ -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);
}

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)
renderTarget = target;

View File

@ -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];

View File

@ -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)

View File

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

View File

@ -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

View File

@ -3,75 +3,220 @@
#include "glassert.h"
#include "texture.h"
#include "shader.h"
#include "textureblur.h"
#include "textureredux.h"
#include "shadersource.h"
#include <glm/ext.hpp>
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; i<NB_SHADERS; ++i)
shaders[i] = NULL;
// set up vao
glAssert(glGenVertexArrays(1, &vao));
glAssert(glBindVertexArray(vao));
glAssert(glGenBuffers(1, &vbo));
glAssert(glBindBuffer(GL_ARRAY_BUFFER, vbo));
glAssert(glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GLfloat), vertices, GL_STATIC_DRAW));
glAssert(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, NULL));
glAssert(glBufferData(GL_ARRAY_BUFFER, 3 * 2 * sizeof(GLfloat), vertices, GL_STATIC_DRAW));
glAssert(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, NULL));
glAssert(glEnableVertexAttribArray(0));
mvp = glm::ortho(0, width, 0, height);
resize(width, height);
}
PostEffectModule::~PostEffectModule()
{
glAssert(glDeleteVertexArrays(1, &vao));
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)
{
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";

View File

@ -3,35 +3,81 @@
#include <glew/glew.h>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include "module.h"
#include <string>
#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

View File

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

View File

@ -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));
}

View File

@ -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

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