diff --git a/FrameBuffer.cpp b/FrameBuffer.cpp index 71faa38c..c8e924ff 100644 --- a/FrameBuffer.cpp +++ b/FrameBuffer.cpp @@ -405,7 +405,7 @@ void FrameBufferList::renderBuffer(u32 _address) } #if 1 - PostProcessor::get().processTexture(pBuffer->m_pTexture); + PostProcessor::get().process(pBuffer); #endif // glDisable(GL_SCISSOR_TEST) does not affect glBlitFramebuffer, at least on AMD glScissor(0, 0, ogl.getScreenWidth(), ogl.getScreenHeight()); diff --git a/PostProcessor.cpp b/PostProcessor.cpp index 7bc75fe4..8c4ad342 100644 --- a/PostProcessor.cpp +++ b/PostProcessor.cpp @@ -3,9 +3,9 @@ #include "N64.h" #include "gSP.h" #include "PostProcessor.h" -#include "GLSLCombiner.h" +#include "FrameBuffer.h" -const char * vertexShader = +static const char * vertexShader = "#version 330 core \n" "in highp vec2 aPosition; \n" "in highp vec2 aTexCoord; \n" @@ -16,7 +16,159 @@ const char * vertexShader = "} \n" ; -static const char* bloomShader = +static const char* extractBloomShader = +"#version 330 core \n" +"in mediump vec2 vTexCoord; \n" +"layout(binding = 0) uniform sampler2D tex; \n" +"out lowp vec4 fragColor; \n" +" \n" +"uniform lowp int ThresholdLevel; \n" +" \n" +"void main() \n" +"{ \n" +" lowp vec4 color = texture2D(tex, vTexCoord); \n" +" \n" +" mediump float lum = dot(vec4(0.30, 0.59, 0.11, 0.0), color);\n" +" mediump float scale = lum; \n" +" lowp int level = clamp(ThresholdLevel, 2, 6); \n" +" for (int i = 1; i < level; ++i) \n" +" scale *= lum; \n" +" fragColor = scale*color; \n" +" fragColor.a = 1.0; \n" +"} \n" +; + +static const char* seperableBlurShader = +/// Author: Nathaniel Meyer +/// +/// Copyright: Nutty Software +/// http://www.nutty.ca +/// +/// Fragment shader for performing a seperable blur on the specified texture. +"#version 330 core \n" +"#ifdef GL_ES \n" +" precision highp float; \n" +"#endif \n" +" \n" +// Uniform variables. +"layout(binding = 0) uniform sampler2D Sample0; \n" +"uniform vec2 TexelSize; \n" +" \n" +"uniform int Orientation; \n" +"uniform int BlurAmount; \n" +"uniform float BlurScale; \n" +"uniform float BlurStrength; \n" +" \n" +"in mediump vec2 vTexCoord; \n" +"out lowp vec4 fragColor; \n" +" \n" +// Gets the Gaussian value in the first dimension. +// "x" Distance from origin on the x-axis. +// "deviation" Standard deviation. +// returns The gaussian value on the x-axis. +"float Gaussian (float x, float deviation) \n" +"{ \n" +" return (1.0 / sqrt(2.0 * 3.141592 * deviation)) * exp(-((x * x) / (2.0 * deviation))); \n" +"} \n" +" \n" +// Fragment shader entry. +"void main () \n" +"{ \n" +" // Locals \n" +" float halfBlur = float(BlurAmount) * 0.5; \n" +" vec4 colour = vec4(0.0); \n" +" \n" +" // Gaussian deviation \n" +" float deviation = halfBlur * 0.35; \n" +" deviation *= deviation; \n" +" float strength = 1.0 - BlurStrength; \n" +" \n" +" if ( Orientation == 0 ) \n" +" { \n" +" // Horizontal blur \n" +" for (int i = 0; i < BlurAmount; ++i) \n" +" { \n" +" float offset = float(i) - halfBlur; \n" +" colour += texture2D(Sample0, vTexCoord + vec2(offset * TexelSize.x * BlurScale, 0.0)) * Gaussian(offset * strength, deviation); \n" +" } \n" +" } \n" +" else \n" +" { \n" +" // Vertical blur \n" +" for (int i = 0; i < BlurAmount; ++i) \n" +" { \n" +" float offset = float(i) - halfBlur; \n" +" colour += texture2D(Sample0, vTexCoord + vec2(0.0, offset * TexelSize.y * BlurScale)) * Gaussian(offset * strength, deviation); \n" +" } \n" +" } \n" +" \n" +" // Apply colour \n" +" fragColor = clamp(colour, 0.0, 1.0); \n" +" fragColor.a = 1.0; \n" +"} \n" +; + +static const char* glowShader = +/// Author: Nathaniel Meyer +/// +/// Copyright: Nutty Software +/// http://www.nutty.ca +/// +/// Fragment shader for blending two textures using an algorithm that overlays the glowmap. +"#version 330 core \n" +"#ifdef GL_ES \n" +" precision highp float; \n" +"#endif \n" +" \n" +// Uniform variables. +"layout(binding = 0) uniform sampler2D Sample0; \n" +"layout(binding = 1) uniform sampler2D Sample1; \n" +"uniform int BlendMode; \n" +" \n" +"in mediump vec2 vTexCoord; \n" +"out lowp vec4 fragColor; \n" +" \n" +// Fragment shader entry. +"void main () \n" +"{ \n" +" vec4 dst = texture2D(Sample0, vTexCoord); // rendered scene \n" +" vec4 src = texture2D(Sample1, vTexCoord); // glowmap \n" +" \n" +" if ( BlendMode == 0 ) \n" +" { \n" +" // Additive blending (strong result, high overexposure) \n" +" fragColor = min(src + dst, 1.0); \n" +" } \n" +" else if ( BlendMode == 1 ) \n" +" { \n" +" // Screen blending (mild result, medium overexposure) \n" +" fragColor = clamp((src + dst) - (src * dst), 0.0, 1.0); \n" +" fragColor.a = 1.0; \n" +" } \n" +" else if ( BlendMode == 2 ) \n" +" { \n" +" // Softlight blending (light result, no overexposure) \n" +" // Due to the nature of soft lighting, we need to bump the black region of the glowmap \n" +" // to 0.5, otherwise the blended result will be dark (black soft lighting will darken \n" +" // the image). \n" +" src = (src * 0.5) + 0.5; \n" +" \n" +" fragColor.rgb = vec3((src.x <= 0.5) ? (dst.x - (1.0 - 2.0 * src.x) * dst.x * (1.0 - dst.x)) : (((src.x > 0.5) && (dst.x <= 0.25)) ? (dst.x + (2.0 * src.x - 1.0) * (4.0 * dst.x * (4.0 * dst.x + 1.0) * (dst.x - 1.0) + 7.0 * dst.x)) : (dst.x + (2.0 * src.x - 1.0) * (sqrt(dst.x) - dst.x))), \n" +" (src.y <= 0.5) ? (dst.y - (1.0 - 2.0 * src.y) * dst.y * (1.0 - dst.y)) : (((src.y > 0.5) && (dst.y <= 0.25)) ? (dst.y + (2.0 * src.y - 1.0) * (4.0 * dst.y * (4.0 * dst.y + 1.0) * (dst.y - 1.0) + 7.0 * dst.y)) : (dst.y + (2.0 * src.y - 1.0) * (sqrt(dst.y) - dst.y))), \n" +" (src.z <= 0.5) ? (dst.z - (1.0 - 2.0 * src.z) * dst.z * (1.0 - dst.z)) : (((src.z > 0.5) && (dst.z <= 0.25)) ? (dst.z + (2.0 * src.z - 1.0) * (4.0 * dst.z * (4.0 * dst.z + 1.0) * (dst.z - 1.0) + 7.0 * dst.z)) : (dst.z + (2.0 * src.z - 1.0) * (sqrt(dst.z) - dst.z))));\n" +" fragColor.a = 1.0; \n" +" } \n" +" else \n" +" { \n" +" // Show just the glow map \n" +" fragColor = src; \n" +" } \n" +"} \n" +; + +static const char* simpleBloomShader = +// glsl bloom shader by krischan +// http://wp.applesandoranges.eu/ "#version 330 core \n" "in mediump vec2 vTexCoord; \n" "layout(binding = 0) uniform sampler2D bgl_RenderedTexture; \n" @@ -28,41 +180,52 @@ static const char* bloomShader = " int j; \n" " int i; \n" " \n" -" for( i= -4 ; i < 4; i++) \n" -" { \n" +" for( i= -4 ; i < 4; i++) { \n" " for (j = -3; j < 3; j++) \n" -" { \n" " sum += texture2D(bgl_RenderedTexture, texcoord + vec2(j, i)*0.004) * 0.25; \n" -" } \n" " } \n" -" if (texture2D(bgl_RenderedTexture, texcoord).r < 0.3) \n" -" { \n" -" fragColor = sum*sum*0.012 + texture2D(bgl_RenderedTexture, texcoord); \n" -" } \n" -" else \n" -" { \n" +" if (texture2D(bgl_RenderedTexture, texcoord).r < 0.3) { \n" +" fragColor = sum*sum*0.012 + texture2D(bgl_RenderedTexture, texcoord); \n" +" } else { \n" " if (texture2D(bgl_RenderedTexture, texcoord).r < 0.5) \n" -" { \n" " fragColor = sum*sum*0.009 + texture2D(bgl_RenderedTexture, texcoord); \n" -" } \n" " else \n" -" { \n" " fragColor = sum*sum*0.0075 + texture2D(bgl_RenderedTexture, texcoord); \n" -" } \n" " } \n" "} \n" ; -static const char* copyShader = -"#version 330 core \n" -"in mediump vec2 vTexCoord; \n" -"layout(binding = 0) uniform sampler2D bgl_RenderedTexture; \n" -"out lowp vec4 fragColor; \n" -"void main() \n" -"{ \n" -" fragColor = texture2D(bgl_RenderedTexture, vTexCoord); \n" -"} \n" -; +static const GLsizei nShaderLogSize = 1024; +static +bool check_shader_compile_status(GLuint obj) +{ + GLint status; + glGetShaderiv(obj, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) + { + GLchar shader_log[nShaderLogSize]; + GLsizei nLogSize = nShaderLogSize; + glGetShaderInfoLog(obj, nShaderLogSize, &nLogSize, shader_log); + shader_log[nLogSize] = 0; + return false; + } + return true; +} + +static +bool check_program_link_status(GLuint obj) +{ + GLint status; + glGetProgramiv(obj, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + GLsizei nLogSize = nShaderLogSize; + GLchar shader_log[nShaderLogSize]; + glGetProgramInfoLog(obj, nShaderLogSize, &nLogSize, shader_log); + return false; + } + return true; +} static GLuint _createShaderProgram(const char * _strVertex, const char * _strFragment) @@ -70,10 +233,12 @@ GLuint _createShaderProgram(const char * _strVertex, const char * _strFragment) GLuint vertex_shader_object = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader_object, 1, &_strVertex, NULL); glCompileShader(vertex_shader_object); + assert(check_shader_compile_status(vertex_shader_object)); GLuint fragment_shader_object = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader_object, 1, &_strFragment, NULL); glCompileShader(fragment_shader_object); + assert(check_shader_compile_status(fragment_shader_object)); GLuint program = glCreateProgram(); glBindAttribLocation(program, SC_POSITION, "aPosition"); @@ -83,61 +248,127 @@ GLuint _createShaderProgram(const char * _strVertex, const char * _strFragment) glLinkProgram(program); glDeleteShader(vertex_shader_object); glDeleteShader(fragment_shader_object); + assert(check_program_link_status(program)); return program; } - -void PostProcessor::init() +static +CachedTexture * _createTexture() { - m_bloomProgram = _createShaderProgram(vertexShader, bloomShader); - m_copyProgram = _createShaderProgram(vertexShader, copyShader); + CachedTexture * pTexture = textureCache().addFrameBufferTexture(); + pTexture->format = G_IM_FMT_RGBA; + pTexture->clampS = 1; + pTexture->clampT = 1; + pTexture->frameBufferTexture = TRUE; + pTexture->maskS = 0; + pTexture->maskT = 0; + pTexture->mirrorS = 0; + pTexture->mirrorT = 0; + pTexture->realWidth = video().getWidth(); + pTexture->realHeight = video().getHeight(); + pTexture->textureBytes = pTexture->realWidth * pTexture->realHeight * 4; + textureCache().addFrameBufferTextureSize(pTexture->textureBytes); + glBindTexture(GL_TEXTURE_2D, pTexture->glName); - m_pTexture = textureCache().addFrameBufferTexture(); - m_pTexture->format = G_IM_FMT_RGBA; - m_pTexture->clampS = 1; - m_pTexture->clampT = 1; - m_pTexture->frameBufferTexture = TRUE; - m_pTexture->maskS = 0; - m_pTexture->maskT = 0; - m_pTexture->mirrorS = 0; - m_pTexture->mirrorT = 0; - m_pTexture->realWidth = video().getWidth(); - m_pTexture->realHeight = video().getHeight(); - m_pTexture->textureBytes = m_pTexture->realWidth * m_pTexture->realHeight * 4; - textureCache().addFrameBufferTextureSize(m_pTexture->textureBytes); - glBindTexture(GL_TEXTURE_2D, m_pTexture->glName); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_pTexture->realWidth, m_pTexture->realHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pTexture->realWidth, pTexture->realHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); + return pTexture; +} - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glGenFramebuffers(1, &m_FBO); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTexture->glName, 0); - GLuint attachments[1] = { GL_COLOR_ATTACHMENT0 }; - glDrawBuffers(1, attachments); - // check if everything is OK +static +GLuint _createFBO(CachedTexture * _pTexture) +{ + GLuint FBO; + glGenFramebuffers(1, &FBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FBO); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _pTexture->glName, 0); assert(checkFBO()); - assert(!isGLError()); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + return FBO; +} +void PostProcessor::init() +{ + m_extractBloomProgram = _createShaderProgram(vertexShader, extractBloomShader); + glUseProgram(m_extractBloomProgram); + int loc = glGetUniformLocation(m_extractBloomProgram, "ThresholdLevel"); + assert(loc >= 0); + glUniform1i(loc, 4); + + m_seperableBlurProgram = _createShaderProgram(vertexShader, seperableBlurShader); + glUseProgram(m_seperableBlurProgram); + loc = glGetUniformLocation(m_seperableBlurProgram, "TexelSize"); + assert(loc >= 0); + glUniform2f(loc, 1.0f/video().getWidth(), 1.0f/video().getHeight()); + loc = glGetUniformLocation(m_seperableBlurProgram, "Orientation"); + assert(loc >= 0); + glUniform1i(loc, 0); + loc = glGetUniformLocation(m_seperableBlurProgram, "BlurAmount"); + assert(loc >= 0); + glUniform1i(loc, 10); + loc = glGetUniformLocation(m_seperableBlurProgram, "BlurScale"); + assert(loc >= 0); + glUniform1f(loc, 1.0f); + loc = glGetUniformLocation(m_seperableBlurProgram, "BlurStrength"); + assert(loc >= 0); + glUniform1f(loc, 0.5f); + + m_glowProgram = _createShaderProgram(vertexShader, glowShader); + glUseProgram(m_glowProgram); + loc = glGetUniformLocation(m_glowProgram, "BlendMode"); + assert(loc >= 0); + glUniform1i(loc, 1); + + m_bloomProgram = _createShaderProgram(vertexShader, simpleBloomShader); + + glUseProgram(0); + + m_pTextureOriginal = _createTexture(); + m_pTextureGlowMap = _createTexture(); + m_pTextureBlur = _createTexture(); + + m_FBO_original = _createFBO(m_pTextureOriginal); + m_FBO_glowMap = _createFBO(m_pTextureGlowMap); + m_FBO_blur = _createFBO(m_pTextureBlur); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void PostProcessor::destroy() { + if (m_extractBloomProgram != 0) + glDeleteProgram(m_extractBloomProgram); + m_extractBloomProgram = 0; + if (m_seperableBlurProgram != 0) + glDeleteProgram(m_seperableBlurProgram); + m_seperableBlurProgram = 0; + if (m_glowProgram != 0) + glDeleteProgram(m_glowProgram); + m_glowProgram = 0; if (m_bloomProgram != 0) glDeleteProgram(m_bloomProgram); + m_bloomProgram = 0; - if (m_copyProgram != 0) - glDeleteProgram(m_copyProgram); + if (m_FBO_original != 0) + glDeleteFramebuffers(1, &m_FBO_original); + m_FBO_original = 0; + if (m_FBO_glowMap != 0) + glDeleteFramebuffers(1, &m_FBO_glowMap); + m_FBO_glowMap = 0; + if (m_FBO_blur != 0) + glDeleteFramebuffers(1, &m_FBO_blur); + m_FBO_blur = 0; - if (m_FBO != 0) - glDeleteFramebuffers(1, &m_FBO); - - if (m_pTexture != NULL) - textureCache().removeFrameBufferTexture(m_pTexture); + if (m_pTextureOriginal != NULL) + textureCache().removeFrameBufferTexture(m_pTextureOriginal); + m_pTextureOriginal = NULL; + if (m_pTextureGlowMap != NULL) + textureCache().removeFrameBufferTexture(m_pTextureGlowMap); + m_pTextureGlowMap = NULL; + if (m_pTextureBlur != NULL) + textureCache().removeFrameBufferTexture(m_pTextureBlur); + m_pTextureBlur = NULL; } PostProcessor & PostProcessor::get() @@ -169,28 +400,54 @@ void _setGLState() { glViewport(0, 0, video().getWidth(), video().getHeight()); } -void PostProcessor::processTexture(CachedTexture * _pTexture) +void PostProcessor::process(FrameBuffer * _pBuffer) { _setGLState(); + OGLVideo & ogl = video(); - textureCache().activateTexture(0, _pTexture); + glBindFramebuffer(GL_READ_FRAMEBUFFER, _pBuffer->m_FBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO_original); + glBlitFramebuffer( + 0, 0, ogl.getWidth(), ogl.getHeight(), + 0, 0, ogl.getWidth(), ogl.getHeight(), + GL_COLOR_BUFFER_BIT, GL_LINEAR + ); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO); +#if 0 + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _pBuffer->m_FBO); + textureCache().activateTexture(0, m_pTextureOriginal); glUseProgram(m_bloomProgram); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +#else + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - textureCache().activateTexture(0, m_pTexture); - GLuint copyFBO = 0; - glGenFramebuffers(1, ©FBO); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, copyFBO); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _pTexture->glName, 0); - assert(checkFBO()); - glUseProgram(m_copyProgram); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO_glowMap); + textureCache().activateTexture(0, m_pTextureOriginal); + glUseProgram(m_extractBloomProgram); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO_blur); + textureCache().activateTexture(0, m_pTextureGlowMap); + glUseProgram(m_seperableBlurProgram); + int loc = glGetUniformLocation(m_seperableBlurProgram, "Orientation"); + assert(loc >= 0); + glUniform1i(loc, 0); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO_glowMap); + textureCache().activateTexture(0, m_pTextureBlur); + glUniform1i(loc, 1); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _pBuffer->m_FBO); + textureCache().activateTexture(0, m_pTextureOriginal); + textureCache().activateTexture(1, m_pTextureGlowMap); + glUseProgram(m_glowProgram); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +#endif + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, ©FBO); video().getRender().dropRenderState(); glUseProgram(0); gSP.changed = CHANGED_VIEWPORT | CHANGED_TEXTURE; diff --git a/PostProcessor.h b/PostProcessor.h index 6d6f017c..3df0b699 100644 --- a/PostProcessor.h +++ b/PostProcessor.h @@ -10,19 +10,29 @@ public: void init(); void destroy(); - void processTexture(CachedTexture * _pTexture); + void process(FrameBuffer * _pBuffer); static PostProcessor & get(); private: - PostProcessor() : m_bloomProgram(0), m_copyProgram(0), m_FBO(0), m_pTexture(NULL) {}; + PostProcessor() : + m_extractBloomProgram(0), m_seperableBlurProgram(0), m_glowProgram(0), m_bloomProgram(0), + m_FBO_original(0), m_FBO_glowMap(0), m_FBO_blur(0), + m_pTextureOriginal(NULL), m_pTextureGlowMap(NULL), m_pTextureBlur(NULL) {}; PostProcessor(const PostProcessor & _other); + GLuint m_extractBloomProgram; + GLuint m_seperableBlurProgram; + GLuint m_glowProgram; GLuint m_bloomProgram; - GLuint m_copyProgram; - GLuint m_FBO; - CachedTexture * m_pTexture; + GLuint m_FBO_original; + GLuint m_FBO_glowMap; + GLuint m_FBO_blur; + + CachedTexture * m_pTextureOriginal; + CachedTexture * m_pTextureGlowMap; + CachedTexture * m_pTextureBlur; }; #endif // POST_PROCESSOR_H