From 4f1a636c207d0ba8f8add26677cf6cd40ed4ab82 Mon Sep 17 00:00:00 2001 From: Sergey Lipskiy Date: Fri, 30 Jan 2015 19:59:52 +0600 Subject: [PATCH] Implement PostProcessor class for image post processing effects. --- CMakeLists.txt | 53 +++++------ FrameBuffer.cpp | 4 + GLideN64.vcxproj | 2 + GLideN64.vcxproj.filters | 6 ++ OpenGL.cpp | 3 + OpenGL.h | 2 + PostProcessor.cpp | 197 +++++++++++++++++++++++++++++++++++++++ PostProcessor.h | 28 ++++++ 8 files changed, 269 insertions(+), 26 deletions(-) create mode 100644 PostProcessor.cpp create mode 100644 PostProcessor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 277ed4f6..9c0ecbfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ set(GLideN64_SOURCES ZSort.cpp Textures.cpp TextDrawer.cpp + PostProcessor.cpp VI.cpp common/CommonAPIImpl_common.cpp ) @@ -42,7 +43,7 @@ set(GLideN64_SOURCES if(MUPENPLUSAPI) add_definitions( -DMUPENPLUSAPI - -DTXFILTER_LIB + -DTXFILTER_LIB ) include_directories( inc ) set(GLideN64_SOURCES_UNIX @@ -98,14 +99,14 @@ if(UNIX) ) endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") if( NOT MUPENPLUSAPI) - add_definitions( + add_definitions( -D_DEBUG - ) - find_package(PkgConfig) - pkg_check_modules(GLIB REQUIRED glib-2.0) - include_directories(${GLIB_INCLUDE_DIRS}) - find_package(GTK2 REQUIRED) - include_directories(${GTK2_INCLUDE_DIRS}) + ) + find_package(PkgConfig) + pkg_check_modules(GLIB REQUIRED glib-2.0) + include_directories(${GLIB_INCLUDE_DIRS}) + find_package(GTK2 REQUIRED) + include_directories(${GTK2_INCLUDE_DIRS}) endif( NOT MUPENPLUSAPI) endif(UNIX) @@ -166,43 +167,43 @@ if(NOT OPENGL_FOUND) endif(NOT OPENGL_FOUND) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - SET(GCC_CPP11_COMPILE_FLAGS "-std=c++0x -static-libgcc -static-libstdc++") - SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_CPP11_COMPILE_FLAGS}" ) - SET(GCC_STATIC_LINK_FLAGS "-static-libgcc -static-libstdc++") - SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_STATIC_LINK_FLAGS}" ) + SET(GCC_CPP11_COMPILE_FLAGS "-std=c++0x -static-libgcc -static-libstdc++") + SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_CPP11_COMPILE_FLAGS}" ) + SET(GCC_STATIC_LINK_FLAGS "-static-libgcc -static-libstdc++") + SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_STATIC_LINK_FLAGS}" ) endif() add_library( ${GLideN64_DLL_NAME} SHARED ${GLideN64_SOURCES}) if( CMAKE_BUILD_TYPE STREQUAL "Debug") SET_TARGET_PROPERTIES( - ${GLideN64_DLL_NAME} - PROPERTIES - LINKER_LANGUAGE CXX # Or else we get an error message, because cmake can't figure out from the ".o"-suffix that it is a C-linker we need. - PREFIX "" - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugin/debug + ${GLideN64_DLL_NAME} + PROPERTIES + LINKER_LANGUAGE CXX # Or else we get an error message, because cmake can't figure out from the ".o"-suffix that it is a C-linker we need. + PREFIX "" + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugin/debug ) if(SDL) - target_link_libraries(${GLideN64_DLL_NAME} PRIVATE ${OPENGL_LIBRARIES} ${SDL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQd ) + target_link_libraries(${GLideN64_DLL_NAME} PRIVATE ${OPENGL_LIBRARIES} ${SDL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQd ) else(SDL) - target_link_libraries(${GLideN64_DLL_NAME} PRIVATE ${OPENGL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQd ) + target_link_libraries(${GLideN64_DLL_NAME} PRIVATE ${OPENGL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQd ) endif(SDL) endif( CMAKE_BUILD_TYPE STREQUAL "Debug") if( CMAKE_BUILD_TYPE STREQUAL "Release") SET_TARGET_PROPERTIES( - ${GLideN64_DLL_NAME} - PROPERTIES - LINKER_LANGUAGE CXX # Or else we get an error message, because cmake can't figure out from the ".o"-suffix that it is a C-linker we need. - PREFIX "" - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugin/release + ${GLideN64_DLL_NAME} + PROPERTIES + LINKER_LANGUAGE CXX # Or else we get an error message, because cmake can't figure out from the ".o"-suffix that it is a C-linker we need. + PREFIX "" + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugin/release ) if(SDL) - target_link_libraries(${GLideN64_DLL_NAME} ${OPENGL_LIBRARIES} ${SDL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQ ) + target_link_libraries(${GLideN64_DLL_NAME} ${OPENGL_LIBRARIES} ${SDL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQ ) else(SDL) - target_link_libraries(${GLideN64_DLL_NAME} PRIVATE ${OPENGL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQ ) + target_link_libraries(${GLideN64_DLL_NAME} PRIVATE ${OPENGL_LIBRARIES} ${FREETYPE_LIBRARIES} GLideNHQ ) endif(SDL) endif( CMAKE_BUILD_TYPE STREQUAL "Release") diff --git a/FrameBuffer.cpp b/FrameBuffer.cpp index cb41bffc..71faa38c 100644 --- a/FrameBuffer.cpp +++ b/FrameBuffer.cpp @@ -14,6 +14,7 @@ #include "Types.h" #include "Config.h" #include "Debug.h" +#include "PostProcessor.h" using namespace std; @@ -403,6 +404,9 @@ void FrameBufferList::renderBuffer(u32 _address) srcY1 = srcY0 + VI.real_height; } +#if 1 + PostProcessor::get().processTexture(pBuffer->m_pTexture); +#endif // glDisable(GL_SCISSOR_TEST) does not affect glBlitFramebuffer, at least on AMD glScissor(0, 0, ogl.getScreenWidth(), ogl.getScreenHeight()); glDisable(GL_SCISSOR_TEST); diff --git a/GLideN64.vcxproj b/GLideN64.vcxproj index 8b76d60d..2831a075 100644 --- a/GLideN64.vcxproj +++ b/GLideN64.vcxproj @@ -293,6 +293,7 @@ + @@ -372,6 +373,7 @@ + diff --git a/GLideN64.vcxproj.filters b/GLideN64.vcxproj.filters index 80a862e6..f0093bde 100644 --- a/GLideN64.vcxproj.filters +++ b/GLideN64.vcxproj.filters @@ -192,6 +192,9 @@ Source Files + + Source Files + @@ -335,6 +338,9 @@ Header Files + + Header Files + diff --git a/OpenGL.cpp b/OpenGL.cpp index c177435b..033a7d92 100644 --- a/OpenGL.cpp +++ b/OpenGL.cpp @@ -27,6 +27,7 @@ #include "Config.h" #include "Log.h" #include "TextDrawer.h" +#include "PostProcessor.h" using namespace std; @@ -1275,6 +1276,7 @@ void OGLRender::_initData() Combiner_Init(); TextDrawer::get().init(); TFH.init(); + PostProcessor::get().init(); m_renderState = rsNone; gSP.changed = gDP.changed = 0xFFFFFFFF; @@ -1293,6 +1295,7 @@ void OGLRender::_initData() void OGLRender::_destroyData() { m_renderState = rsNone; + PostProcessor::get().destroy(); TextDrawer::get().destroy(); Combiner_Destroy(); FrameBuffer_Destroy(); diff --git a/OpenGL.h b/OpenGL.h index 7e948dca..31d2c994 100644 --- a/OpenGL.h +++ b/OpenGL.h @@ -80,6 +80,8 @@ public: }; RENDER_STATE getRenderState() const {return m_renderState;} + void dropRenderState() {m_renderState = rsNone;} + #ifdef __TRIBUFFER_OPT u32 getIndexmap(u32 _v) const {return triangles.indexmap[_v];} u32 getIndexmapNew(u32 _index, u32 _num); diff --git a/PostProcessor.cpp b/PostProcessor.cpp new file mode 100644 index 00000000..7bc75fe4 --- /dev/null +++ b/PostProcessor.cpp @@ -0,0 +1,197 @@ +#include + +#include "N64.h" +#include "gSP.h" +#include "PostProcessor.h" +#include "GLSLCombiner.h" + +const char * vertexShader = +"#version 330 core \n" +"in highp vec2 aPosition; \n" +"in highp vec2 aTexCoord; \n" +"out mediump vec2 vTexCoord; \n" +"void main(){ \n" +"gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0);\n" +"vTexCoord = aTexCoord; \n" +"} \n" +; + +static const char* bloomShader = +"#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" +" vec4 sum = vec4(0); \n" +" vec2 texcoord = vTexCoord; \n" +" int j; \n" +" int i; \n" +" \n" +" for( i= -4 ; i < 4; i++) \n" +" { \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.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 +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); + + GLuint fragment_shader_object = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader_object, 1, &_strFragment, NULL); + glCompileShader(fragment_shader_object); + + GLuint program = glCreateProgram(); + glBindAttribLocation(program, SC_POSITION, "aPosition"); + glBindAttribLocation(program, SC_TEXCOORD0, "aTexCoord"); + glAttachShader(program, vertex_shader_object); + glAttachShader(program, fragment_shader_object); + glLinkProgram(program); + glDeleteShader(vertex_shader_object); + glDeleteShader(fragment_shader_object); + return program; +} + + +void PostProcessor::init() +{ + m_bloomProgram = _createShaderProgram(vertexShader, bloomShader); + m_copyProgram = _createShaderProgram(vertexShader, copyShader); + + 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); + 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); + + 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 + assert(checkFBO()); + assert(!isGLError()); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + +} + +void PostProcessor::destroy() +{ + if (m_bloomProgram != 0) + glDeleteProgram(m_bloomProgram); + + if (m_copyProgram != 0) + glDeleteProgram(m_copyProgram); + + if (m_FBO != 0) + glDeleteFramebuffers(1, &m_FBO); + + if (m_pTexture != NULL) + textureCache().removeFrameBufferTexture(m_pTexture); +} + +PostProcessor & PostProcessor::get() +{ + static PostProcessor processor; + return processor; +} + +void _setGLState() { + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + + static const float vert[] = + { + -1.0, -1.0, +0.0, +0.0, + +1.0, -1.0, +1.0, +0.0, + -1.0, +1.0, +0.0, +1.0, + +1.0, +1.0, +1.0, +1.0 + }; + + glEnableVertexAttribArray(SC_POSITION); + glVertexAttribPointer(SC_POSITION, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (float*)vert); + glEnableVertexAttribArray(SC_TEXCOORD0); + glVertexAttribPointer(SC_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (float*)vert + 2); + glDisableVertexAttribArray(SC_COLOR); + glDisableVertexAttribArray(SC_TEXCOORD1); + glDisableVertexAttribArray(SC_NUMLIGHTS); + glViewport(0, 0, video().getWidth(), video().getHeight()); +} + +void PostProcessor::processTexture(CachedTexture * _pTexture) +{ + _setGLState(); + + textureCache().activateTexture(0, _pTexture); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO); + glUseProgram(m_bloomProgram); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + 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); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + 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 new file mode 100644 index 00000000..6d6f017c --- /dev/null +++ b/PostProcessor.h @@ -0,0 +1,28 @@ +#ifndef POST_PROCESSOR_H +#define POST_PROCESSOR_H + +#include "Types.h" +#include "OpenGL.h" +#include "Textures.h" + +class PostProcessor { +public: + void init(); + void destroy(); + + void processTexture(CachedTexture * _pTexture); + + static PostProcessor & get(); + +private: + PostProcessor() : m_bloomProgram(0), m_copyProgram(0), m_FBO(0), m_pTexture(NULL) {}; + PostProcessor(const PostProcessor & _other); + + GLuint m_bloomProgram; + GLuint m_copyProgram; + + GLuint m_FBO; + CachedTexture * m_pTexture; +}; + +#endif // POST_PROCESSOR_H