diff --git a/NEWS.md b/NEWS.md index 6089949ae..ec8c034be 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,8 @@ First release, a major update to the application compared to the RetroPie versio Full navigation sound support has been implemented, and the metadata editor has seen a lot of updates including color coding of all changes done by the user and by the scraper. Favorite games can now also be sorted on top of the gamelists and game collections. +OpenGL GLSL shader support has been added (not for the OpenGL ES renderer though) which will open up many possibilities in the future. + A new default theme rbsimple-DE (based on Recalbox Multi) is bundled with the application and is part of the installation package/installer. However themes created for other EmulationStation ports should still work correctly. Many bugs have been fixed, and numerous features that were only partially implemented or broken have been updated to a fully working state. @@ -28,6 +30,7 @@ Many bugs have been fixed, and numerous features that were only partially implem * Added extensive es_systems.cfg templates for Unix and Windows * Updated the application to compile and work on Microsoft Windows, including full UTF-16 (Unicode) support * Updated the application to compile and work on Apple macOS +* Added support for OpenGL GLSL shaders (OpenGL 2.1 renderer only, no support for OpenGL ES 1.0 renderer) * Seamless (almost) launch of games without showing the desktop when starting and when returning from RetroArch and other emulators * Per-game launch command override, so that different cores or emulators can be used on a per-game basis (saved to gamelist.xml) * Core location can be defined relative to the emulator binary using the %EMUPATH% variable in es_systems.cfg (mostly useful for Windows) diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index a7898583a..831adf7ae 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -68,6 +68,7 @@ set(CORE_HEADERS # Renderers ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Shader_GL21.h # Resources ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h @@ -146,6 +147,7 @@ set(CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GL21.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GLES10.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Shader_GL21.cpp # Resources ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index 14e9c501b..ae04c1500 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -18,6 +18,7 @@ GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(nullptr), mOpacity(255), + mSaturation(1.0), mPosition(Vector3f::Zero()), mOrigin(Vector2f::Zero()), mRotationOrigin(0.5, 0.5), @@ -259,23 +260,33 @@ void GuiComponent::setOpacity(unsigned char opacity) (*it)->setOpacity(opacity); } +unsigned int GuiComponent::getColor() const +{ + return mColor; +} + void GuiComponent::setColor(unsigned int color) { mColor = color; mColorOpacity = mColor & 0x000000FF; } +float GuiComponent::getSaturation() const +{ + return mColor; +} + +void GuiComponent::setSaturation(float saturation) +{ + mSaturation = saturation; +} + void GuiComponent::setColorShift(unsigned int color) { mColorShift = color; mColorShiftEnd = color; } -unsigned int GuiComponent::getColor() const -{ - return mColor; -} - const Transform4x4f& GuiComponent::getTransform() { mTransform = Transform4x4f::Identity(); diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index bcc71a511..92646b941 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -139,11 +139,13 @@ public: virtual unsigned char getOpacity() const; virtual void setOpacity(unsigned char opacity); + virtual unsigned int getColor() const; virtual void setColor(unsigned int color); + virtual float getSaturation() const; + virtual void setSaturation(float saturation); virtual void setColorShift(unsigned int color); virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; }; virtual void setChangedColor(unsigned int color) { mColorChangedValue = color; }; - virtual unsigned int getColor() const; // These functions are used to enable and disable options in menus, i.e. switches and similar. virtual void setEnabled() { mEnabled = true; }; @@ -191,6 +193,7 @@ protected: unsigned char mOpacity; unsigned int mColor; + float mSaturation; unsigned char mColorOpacity; unsigned int mColorShift; unsigned int mColorShiftEnd; diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index f286ec8fe..9c527a3aa 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -293,6 +293,12 @@ void ImageComponent::setOpacity(unsigned char opacity) updateColors(); } +void ImageComponent::setSaturation(float saturation) +{ + mSaturation = saturation; + updateColors(); +} + void ImageComponent::updateVertices() { if (!mTexture || !mTexture->isInitialized()) @@ -339,6 +345,10 @@ void ImageComponent::updateColors() mVertices[1].col = mColorGradientHorizontal ? colorEnd : color; mVertices[2].col = mColorGradientHorizontal ? color : colorEnd; mVertices[3].col = colorEnd; + mVertices[0].saturation = mSaturation; + mVertices[1].saturation = mSaturation; + mVertices[2].saturation = mSaturation; + mVertices[3].saturation = mSaturation; } void ImageComponent::render(const Transform4x4f& parentTrans) diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index e33f5f93e..6eebc67f5 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -31,7 +31,6 @@ public: void setImage(const std::shared_ptr& texture); void onSizeChanged() override; - void setOpacity(unsigned char opacity) override; // Resize the image to fit this size. If one axis is zero, scale that axis to maintain // aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are @@ -66,6 +65,9 @@ public: void setColorShiftEnd(unsigned int color); void setColorGradientHorizontal(bool horizontal); + void setOpacity(unsigned char opacity) override; + void setSaturation(float saturation) override; + void setFlipX(bool flip); // Mirror on the X axis. void setFlipY(bool flip); // Mirror on the Y axis. diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp index f451e7514..0e4744185 100644 --- a/es-core/src/renderers/Renderer.cpp +++ b/es-core/src/renderers/Renderer.cpp @@ -8,6 +8,7 @@ #include "math/Transform4x4f.h" #include "math/Vector2i.h" +#include "Shader_GL21.h" #include "resources/ResourceManager.h" #include "ImageIO.h" #include "Log.h" @@ -155,17 +156,42 @@ namespace Renderer return false; } - LOG(LogInfo) << "Created window successfully."; + LOG(LogInfo) << "Setting up OpenGL..."; + + if (!createContext()) + return false; - createContext(); setIcon(); setSwapInterval(); + #if defined(USE_OPENGL_21) + LOG(LogInfo) << "Loading shaders..."; + + Shader* desaturateShader = new Shader(); + + desaturateShader->loadShaderFile(":/shaders/glsl/desaturate.glsl", GL_VERTEX_SHADER); + desaturateShader->loadShaderFile(":/shaders/glsl/desaturate.glsl", GL_FRAGMENT_SHADER); + + if (!desaturateShader->createProgram()) { + LOG(LogError) << "Could not create shader program."; + return false; + } + + sShaderProgramVector.push_back(desaturateShader); + #endif + return true; } static void destroyWindow() { + #if defined(USE_OPENGL_21) + for (auto it = sShaderProgramVector.cbegin(); + it != sShaderProgramVector.cend(); it++) { + delete *it; + } + #endif + destroyContext(); SDL_DestroyWindow(sdlWindow); @@ -327,6 +353,34 @@ namespace Renderer drawTriangleStrips(vertices, 4, _srcBlendFactor, _dstBlendFactor); } + unsigned int rgbaToABGR(const unsigned int _color) + { + unsigned char red = ((_color & 0xff000000) >> 24) & 255; + unsigned char green = ((_color & 0x00ff0000) >> 16) & 255; + unsigned char blue = ((_color & 0x0000ff00) >> 8) & 255; + unsigned char alpha = ((_color & 0x000000ff)) & 255; + + return alpha << 24 | blue << 16 | green << 8 | red; + } + + unsigned int abgrToRGBA(const unsigned int _color) + { + unsigned char alpha = ((_color & 0xff000000) >> 24) & 255; + unsigned char blue = ((_color & 0x00ff0000) >> 16) & 255; + unsigned char green = ((_color & 0x0000ff00) >> 8) & 255; + unsigned char red = ((_color & 0x000000ff)) & 255; + + return red << 24 | green << 16 | blue << 8 | alpha; + } + + Shader* getShaderProgram(unsigned int index) + { + if (sShaderProgramVector.size() > index) + return sShaderProgramVector[index]; + else + return nullptr; + }; + SDL_Window* getSDLWindow() { return sdlWindow; } int getWindowWidth() { return windowWidth; } int getWindowHeight() { return windowHeight; } diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index 8b7abc544..6c5024922 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -9,6 +9,11 @@ #define ES_CORE_RENDERER_RENDERER_H #include "math/Vector2f.h" +#include "Log.h" +#include "Shader_GL21.h" + +#include +#include class Transform4x4f; class Vector2i; @@ -16,6 +21,30 @@ struct SDL_Window; namespace Renderer { + static std::vector sShaderProgramVector; + + #if !defined(NDEBUG) + #define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) + + static void _GLCheckError(const char* _funcName) + { + const GLenum errorCode = glGetError(); + + if (errorCode != GL_NO_ERROR) { + #if defined(USE_OPENGL_21) + LOG(LogError) << "OpenGL error: " << _funcName << + " failed with error code: 0x" << std::hex << errorCode; + #else + LOG(LogError) << "OpenGLES error: " << _funcName << + " failed with error code: 0x" << std::hex << errorCode; + #endif + + } + } + #else + #define GL_CHECK_ERROR(Function) (Function) + #endif + namespace Blend { enum Factor { @@ -69,6 +98,7 @@ namespace Renderer Vector2f pos; Vector2f tex; unsigned int col; + float saturation = 1.0; }; bool init(); @@ -94,11 +124,16 @@ namespace Renderer int getScreenOffsetY(); int getScreenRotate(); + unsigned int rgbaToABGR(unsigned int color); + unsigned int abgrToRGBA(unsigned int color); + + Shader* getShaderProgram(unsigned int index); + // API specific. unsigned int convertColor(const unsigned int _color); unsigned int getWindowFlags(); void setupWindow(); - void createContext(); + bool createContext(); void destroyContext(); unsigned int createTexture( const Texture::Type _type, @@ -133,7 +168,6 @@ namespace Renderer void setScissor(const Rect& _scissor); void setSwapInterval(); void swapBuffers(); - } #endif // ES_CORE_RENDERER_RENDERER_H diff --git a/es-core/src/renderers/Renderer_GL21.cpp b/es-core/src/renderers/Renderer_GL21.cpp index 299e74251..c7b05fcff 100644 --- a/es-core/src/renderers/Renderer_GL21.cpp +++ b/es-core/src/renderers/Renderer_GL21.cpp @@ -6,32 +6,13 @@ #if defined(USE_OPENGL_21) -#include "renderers/Renderer.h" #include "math/Transform4x4f.h" -#include "Log.h" +#include "renderers/Renderer.h" #include "Settings.h" - -#include -#include +#include "Shader_GL21.h" namespace Renderer { - #if !defined(NDEBUG) - #define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) - - static void _GLCheckError(const char* _funcName) - { - const GLenum errorCode = glGetError(); - - if (errorCode != GL_NO_ERROR) { - LOG(LogError) << "OpenGL error: " << _funcName << - " failed with error code: 0x" << std::hex << errorCode; - } - } - #else - #define GL_CHECK_ERROR(Function) (Function) - #endif - static SDL_GLContext sdlContext = nullptr; static GLuint whiteTexture = 0; @@ -97,12 +78,14 @@ namespace Renderer SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); } - void createContext() + bool createContext() { + bool missingExtension = false; sdlContext = SDL_GL_CreateContext(getSDLWindow()); if (!sdlContext) { LOG(LogError) << "Error creating OpenGL context. " << SDL_GetError(); + return false; } SDL_GL_MakeCurrent(getSDLWindow(), sdlContext); @@ -123,9 +106,31 @@ namespace Renderer LOG(LogInfo) << "Checking available OpenGL extensions..."; std::string glExts = glGetString(GL_EXTENSIONS) ? (const char*)glGetString(GL_EXTENSIONS) : ""; - LOG(LogInfo) << "ARB_texture_non_power_of_two: " << - (extensions.find("ARB_texture_non_power_of_two") != - std::string::npos ? "ok" : "MISSING"); + if (extensions.find("GL_ARB_texture_non_power_of_two") == std::string::npos) { + LOG(LogError) << "GL_ARB_texture_non_power_of_two: MISSING"; + missingExtension = true; + } + else { + LOG(LogInfo) << "GL_ARB_texture_non_power_of_two: OK"; + } + if (extensions.find("GL_ARB_vertex_shader") == std::string::npos) { + LOG(LogError) << "GL_ARB_vertex_shader: MISSING"; + missingExtension = true; + } + else { + LOG(LogInfo) << "GL_ARB_vertex_shader: OK"; + } + if (extensions.find("GL_ARB_fragment_shader") == std::string::npos) { + LOG(LogError) << "GL_ARB_fragment_shader: MISSING"; + missingExtension = true; + } + else { + LOG(LogInfo) << "GL_ARB_fragment_shader: OK"; + } + if (missingExtension) { + LOG(LogError) << "Required OpenGL extensions missing."; + return false; + } uint8_t data[4] = {255, 255, 255, 255}; whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data); @@ -138,6 +143,8 @@ namespace Renderer GL_CHECK_ERROR(glEnableClientState(GL_VERTEX_ARRAY)); GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY)); + + return true; } void destroyContext() @@ -218,6 +225,20 @@ namespace Renderer convertBlendFactor(_dstBlendFactor))); GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices)); + + // If saturation is set below the maximum (default) value, run the desaturation shader. + if (_vertices->saturation < 1.0) { + Shader* desaturateShader = getShaderProgram(Shader::Desaturate); + + // Only try to use the shader if it has been loaded properly. + if (desaturateShader) { + desaturateShader->activateShaders(); + desaturateShader->getVariableLocations(desaturateShader->getProgramID()); + desaturateShader->setVariable(_vertices->saturation); + GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices)); + desaturateShader->deactivateShaders(); + } + } } void drawTriangleStrips( @@ -234,6 +255,20 @@ namespace Renderer convertBlendFactor(_dstBlendFactor))); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); + + // If saturation is set below the maximum (default) value, run the desaturation shader. + if (_vertices->saturation < 1.0) { + Shader* desaturateShader = getShaderProgram(Shader::Desaturate); + + // Only try to use the shader if it has been loaded properly. + if (desaturateShader) { + desaturateShader->activateShaders(); + desaturateShader->getVariableLocations(desaturateShader->getProgramID()); + desaturateShader->setVariable(_vertices->saturation); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); + desaturateShader->deactivateShaders(); + } + } } void setProjection(const Transform4x4f& _projection) diff --git a/es-core/src/renderers/Renderer_GLES10.cpp b/es-core/src/renderers/Renderer_GLES10.cpp index 44d319d78..f37c42963 100644 --- a/es-core/src/renderers/Renderer_GLES10.cpp +++ b/es-core/src/renderers/Renderer_GLES10.cpp @@ -16,22 +16,6 @@ namespace Renderer { - #if !defined(NDEBUG) - #define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) - - static void _GLCheckError(const char* _funcName) - { - const GLenum errorCode = glGetError(); - - if (errorCode != GL_NO_ERROR) { - LOG(LogError) << "OpenGLES error: " << _funcName << - " failed with error code: 0x" << std::hex << errorCode; - } - } - #else - #define GL_CHECK_ERROR(Function) (Function) - #endif - static SDL_GLContext sdlContext = nullptr; static GLuint whiteTexture = 0; @@ -91,7 +75,7 @@ namespace Renderer SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); } - void createContext() + bool createContext() { sdlContext = SDL_GL_CreateContext(getSDLWindow()); SDL_GL_MakeCurrent(getSDLWindow(), sdlContext); @@ -112,9 +96,9 @@ namespace Renderer LOG(LogInfo) << "Checking available OpenGL extensions..."; std::string glExts = glGetString(GL_EXTENSIONS) ? (const char*)glGetString(GL_EXTENSIONS) : ""; - LOG(LogInfo) << "ARB_texture_non_power_of_two: " << - (extensions.find("ARB_texture_non_power_of_two") != - std::string::npos ? "ok" : "MISSING"); + LOG(LogInfo) << "GL_OES_texture_npot: " << + (extensions.find("GL_OES_texture_npot") != + std::string::npos ? "OK" : "MISSING"); uint8_t data[4] = {255, 255, 255, 255}; whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data); @@ -127,6 +111,8 @@ namespace Renderer GL_CHECK_ERROR(glEnableClientState(GL_VERTEX_ARRAY)); GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY)); + + return true; } void destroyContext() diff --git a/es-core/src/renderers/Shader_GL21.cpp b/es-core/src/renderers/Shader_GL21.cpp new file mode 100644 index 000000000..2a8d9f049 --- /dev/null +++ b/es-core/src/renderers/Shader_GL21.cpp @@ -0,0 +1,204 @@ +// +// Shader_GL21.cpp +// +// OpenGL 2.1 GLSL shader functions. +// + +#if defined(USE_OPENGL_21) + +#include "Shader_GL21.h" + +#include "renderers/Renderer.h" +#include "resources/ResourceManager.h" +#include "Log.h" + +namespace Renderer +{ + Renderer::Shader::Shader() + : mProgramID(-1), + shaderFloat_0(-1), + shaderFloat_1(-1), + shaderFloat_2(-1), + shaderVec4_0(-1), + shaderVec4_1(-1), + shaderVec4_2(-1) + { + } + + Renderer::Shader::~Shader() + { + deleteProgram(mProgramID); + } + + void Renderer::Shader::loadShaderFile(const std::string& path, GLenum shaderType) + { + std::string preprocessorDefines; + std::string shaderCode; + + // This will load the entire GLSL source code into the string variable. + const ResourceData& shaderData = ResourceManager::getInstance()->getFileData(path); + shaderCode.assign((const char*)shaderData.ptr.get(), shaderData.length); + + // Define the GLSL version (version 120 = OpenGL 2.1). + preprocessorDefines = "#version 120\n"; + + // Define the preprocessor macros that will let the shader compiler know whether + // the VERTEX or FRAGMENT portion of the code should be used. + if (shaderType == GL_VERTEX_SHADER) + preprocessorDefines += "#define VERTEX\n"; + else if (shaderType == GL_FRAGMENT_SHADER) + preprocessorDefines += "#define FRAGMENT\n"; + + shaderVector.push_back(std::make_tuple( + path, preprocessorDefines + shaderCode, shaderType)); + } + + bool Renderer::Shader::createProgram() + { + GLint programSuccess; + + mProgramID = glCreateProgram(); + + // Compile and attach all shaders that have been loaded. + for (auto it = shaderVector.cbegin(); it != shaderVector.cend(); it++) { + GLuint currentShader = glCreateShader(std::get<2>(*it)); + GLchar const* shaderCodePtr = std::get<1>(*it).c_str(); + + glShaderSource(currentShader, 1, (const GLchar**)&shaderCodePtr, nullptr); + glCompileShader(currentShader); + + GLint shaderCompiled; + glGetShaderiv(currentShader, GL_COMPILE_STATUS, &shaderCompiled); + + if (shaderCompiled != GL_TRUE) { + LOG(LogError) << "OpenGL error: Unable to compile shader " << + currentShader << " (" << std::get<0>(*it) << ")."; + printShaderInfoLog(currentShader); + return false; + } + + GL_CHECK_ERROR(glAttachShader(mProgramID, currentShader)); + } + + glLinkProgram(mProgramID); + + glGetProgramiv(mProgramID, GL_LINK_STATUS, &programSuccess); + if (programSuccess != GL_TRUE) { + LOG(LogError) << "OpenGL error: Unable to link program " << mProgramID << "."; + printProgramInfoLog(mProgramID); + return false; + } + + return true; + } + + void Renderer::Shader::deleteProgram(GLuint programID) + { + GL_CHECK_ERROR(glDeleteProgram(programID)); + } + + void Renderer::Shader::getVariableLocations(GLuint programID) + { + shaderFloat_0 = glGetUniformLocation(mProgramID, "shaderFloat_0"); + shaderFloat_1 = glGetUniformLocation(mProgramID, "shaderFloat_1"); + shaderFloat_2 = glGetUniformLocation(mProgramID, "shaderFloat_2"); + shaderVec4_0 = glGetUniformLocation(mProgramID, "shaderVec4_0"); + shaderVec4_1 = glGetUniformLocation(mProgramID, "shaderVec4_1"); + shaderVec4_2 = glGetUniformLocation(mProgramID, "shaderVec4_2"); + } + + void Renderer::Shader::setVariable(GLfloat shaderFloat, int index) + { + switch (index) { + case 0: + GL_CHECK_ERROR(glUniform1f(shaderFloat_0, shaderFloat)); + break; + case 1: + GL_CHECK_ERROR(glUniform1f(shaderFloat_1, shaderFloat)); + break; + case 2: + GL_CHECK_ERROR(glUniform1f(shaderFloat_2, shaderFloat)); + break; + default: + break; + } + } + + void Renderer::Shader::setVariable(std::array shaderVec4, int index) + { + switch (index) { + case 0: + GL_CHECK_ERROR(glUniform4f(shaderVec4_0, shaderVec4[0], + shaderVec4[1], shaderVec4[2], shaderVec4[3])); + break; + case 1: + GL_CHECK_ERROR(glUniform4f(shaderVec4_1, shaderVec4[0], + shaderVec4[1], shaderVec4[2], shaderVec4[3])); + case 2: + GL_CHECK_ERROR(glUniform4f(shaderVec4_2, shaderVec4[0], + shaderVec4[1], shaderVec4[2], shaderVec4[3])); + default: + break; + } + } + + void Renderer::Shader::activateShaders() + { + GL_CHECK_ERROR(glUseProgram(mProgramID)); + } + + void Renderer::Shader::deactivateShaders() + { + GL_CHECK_ERROR(glUseProgram(0)); + } + + GLuint Renderer::Shader::getProgramID() + { + return mProgramID; + } + + void Renderer::Shader::printProgramInfoLog(GLuint programID) + { + if (glIsProgram(programID)) { + int logLength; + int maxLength; + + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxLength); + std::vector infoLog(maxLength); + + glGetProgramInfoLog(programID, maxLength, &logLength, &infoLog.front()); + + if (logLength > 0) { + LOG(LogDebug) << "Renderer_GL21::printProgramLog():\n" << + std::string(infoLog.begin(), infoLog.end()); + } + } + else { + LOG(LogError) << "OpenGL error: " << programID << " is not a program."; + } + } + + void Renderer::Shader::printShaderInfoLog(GLuint shaderID) + { + if (glIsShader(shaderID)) { + int logLength; + int maxLength; + + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxLength); + std::vector infoLog(maxLength); + + glGetShaderInfoLog(shaderID, maxLength, &logLength, &infoLog.front()); + + if (logLength > 0) { + LOG(LogDebug) << "Renderer_GL21::printShaderLog():\n" << + std::string(infoLog.begin(), infoLog.end()); + } + } + else { + LOG(LogError) << "OpenGL error: " << shaderID << " is not a shader."; + } + } + +} // Renderer + +#endif // USE_OPENGL_21 diff --git a/es-core/src/renderers/Shader_GL21.h b/es-core/src/renderers/Shader_GL21.h new file mode 100644 index 000000000..aff86e015 --- /dev/null +++ b/es-core/src/renderers/Shader_GL21.h @@ -0,0 +1,65 @@ +// +// Shader_GL21.h +// +// OpenGL 2.1 GLSL shader functions. +// + +#ifndef ES_CORE_RENDERER_SHADER_GL21_H +#define ES_CORE_RENDERER_SHADER_GL21_H + +#define GL_GLEXT_PROTOTYPES + +#include +#include +#include +#include +#include + +namespace Renderer +{ + class Shader + { + public: + enum shaderNames { + Desaturate + }; + + Shader(); + ~Shader(); + + // Loads the shader source code only, no compilation done at this point. + void loadShaderFile(const std::string& path, GLenum shaderType); + // Compilation, shader attachment and linking. + bool createProgram(); + // Only used for a clean shutdown. + void deleteProgram(GLuint programID); + // Get references to the variables inside the compiled shaders. + void getVariableLocations(GLuint programID); + // One-way communication with the compiled shaders. + void setVariable(GLfloat shaderFloat, int index = 0); + void setVariable(std::array shaderVec4, int index = 0); + // Sets the shader program to use the loaded shaders. + void activateShaders(); + // Sets the shader program to 0 which reverts to the fixed function pipeline. + void deactivateShaders(); + // Returns the program ID that was generated by glCreateProgram(). + GLuint getProgramID(); + // Only used for error logging if the shaders fail to compile or link. + void printProgramInfoLog(GLuint programID); + void printShaderInfoLog(GLuint shaderID); + + private: + GLuint mProgramID; + std::vector> shaderVector; + + // Variables used for communication with the compiled shaders. + GLint shaderFloat_0; + GLint shaderFloat_1; + GLint shaderFloat_2; + GLint shaderVec4_0; + GLint shaderVec4_1; + GLint shaderVec4_2; + }; +} // Renderer + +#endif // ES_CORE_RENDERER_SHADER_GL21_H diff --git a/resources/shaders/glsl/desaturate.glsl b/resources/shaders/glsl/desaturate.glsl new file mode 100644 index 000000000..83ce046c7 --- /dev/null +++ b/resources/shaders/glsl/desaturate.glsl @@ -0,0 +1,40 @@ +// +// desaturate.glsl +// +// Desaturates textures such as game images. +// The uniform variable 'shaderFloat_0' sets the saturation intensity. +// Setting this to the value 0 results in complete desaturation (grayscale). +// + +// Vertex section of code: +// ----------------------- +#if defined(VERTEX) + +varying vec2 vTexCoord; + +void main(void) +{ + vTexCoord = gl_MultiTexCoord0.xy; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} +#endif + +// Fragment section of code: +// ------------------------- +#ifdef FRAGMENT + +uniform float shaderFloat_0 = 1.0; +uniform sampler2D myTexture; +varying vec2 vTexCoord; + +void main() +{ + float saturation = shaderFloat_0; + vec4 color = texture2D(myTexture, vTexCoord); + vec3 grayscale = vec3(dot(color.rgb, vec3(0.2125, 0.7154, 0.0721))); + + vec3 blendedColor = mix(grayscale, color.rgb, saturation); + gl_FragColor = vec4(blendedColor, color.a); +} + +#endif \ No newline at end of file