added blur, bloom, redux algorithms, added post processing pipeline
This commit is contained in:
parent
53a37e6738
commit
7825ecd12f
@ -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
25
shaders/bloom.frag.glsl
Normal 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
31
shaders/blur.frag.glsl
Normal 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);
|
||||
}
|
12
shaders/hdr.frag.glsl
Normal file
12
shaders/hdr.frag.glsl
Normal 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);
|
||||
}
|
12
shaders/luminance.frag.glsl
Normal file
12
shaders/luminance.frag.glsl
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
27
shaders/reduction.frag.glsl
Normal file
27
shaders/reduction.frag.glsl
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
@ -27,14 +32,19 @@ public:
|
||||
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
112
src/textureblur.cpp
Normal 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
36
src/textureblur.h
Normal 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
61
src/textureredux.cpp
Normal 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
36
src/textureredux.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user