mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 15:45:38 +00:00
Implemented OpenGL GLSL shader support.
This commit is contained in:
parent
a6c8f8034e
commit
d512c2b11d
3
NEWS.md
3
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.
|
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.
|
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.
|
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
|
* 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 Microsoft Windows, including full UTF-16 (Unicode) support
|
||||||
* Updated the application to compile and work on Apple macOS
|
* 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
|
* 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)
|
* 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)
|
* Core location can be defined relative to the emulator binary using the %EMUPATH% variable in es_systems.cfg (mostly useful for Windows)
|
||||||
|
|
|
@ -68,6 +68,7 @@ set(CORE_HEADERS
|
||||||
|
|
||||||
# Renderers
|
# Renderers
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Shader_GL21.h
|
||||||
|
|
||||||
# Resources
|
# Resources
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h
|
${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.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GL21.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/Renderer_GLES10.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Shader_GL21.cpp
|
||||||
|
|
||||||
# Resources
|
# Resources
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp
|
||||||
|
|
|
@ -18,6 +18,7 @@ GuiComponent::GuiComponent(Window* window)
|
||||||
: mWindow(window),
|
: mWindow(window),
|
||||||
mParent(nullptr),
|
mParent(nullptr),
|
||||||
mOpacity(255),
|
mOpacity(255),
|
||||||
|
mSaturation(1.0),
|
||||||
mPosition(Vector3f::Zero()),
|
mPosition(Vector3f::Zero()),
|
||||||
mOrigin(Vector2f::Zero()),
|
mOrigin(Vector2f::Zero()),
|
||||||
mRotationOrigin(0.5, 0.5),
|
mRotationOrigin(0.5, 0.5),
|
||||||
|
@ -259,23 +260,33 @@ void GuiComponent::setOpacity(unsigned char opacity)
|
||||||
(*it)->setOpacity(opacity);
|
(*it)->setOpacity(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int GuiComponent::getColor() const
|
||||||
|
{
|
||||||
|
return mColor;
|
||||||
|
}
|
||||||
|
|
||||||
void GuiComponent::setColor(unsigned int color)
|
void GuiComponent::setColor(unsigned int color)
|
||||||
{
|
{
|
||||||
mColor = color;
|
mColor = color;
|
||||||
mColorOpacity = mColor & 0x000000FF;
|
mColorOpacity = mColor & 0x000000FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float GuiComponent::getSaturation() const
|
||||||
|
{
|
||||||
|
return mColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiComponent::setSaturation(float saturation)
|
||||||
|
{
|
||||||
|
mSaturation = saturation;
|
||||||
|
}
|
||||||
|
|
||||||
void GuiComponent::setColorShift(unsigned int color)
|
void GuiComponent::setColorShift(unsigned int color)
|
||||||
{
|
{
|
||||||
mColorShift = color;
|
mColorShift = color;
|
||||||
mColorShiftEnd = color;
|
mColorShiftEnd = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int GuiComponent::getColor() const
|
|
||||||
{
|
|
||||||
return mColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Transform4x4f& GuiComponent::getTransform()
|
const Transform4x4f& GuiComponent::getTransform()
|
||||||
{
|
{
|
||||||
mTransform = Transform4x4f::Identity();
|
mTransform = Transform4x4f::Identity();
|
||||||
|
|
|
@ -139,11 +139,13 @@ public:
|
||||||
|
|
||||||
virtual unsigned char getOpacity() const;
|
virtual unsigned char getOpacity() const;
|
||||||
virtual void setOpacity(unsigned char opacity);
|
virtual void setOpacity(unsigned char opacity);
|
||||||
|
virtual unsigned int getColor() const;
|
||||||
virtual void setColor(unsigned int color);
|
virtual void setColor(unsigned int color);
|
||||||
|
virtual float getSaturation() const;
|
||||||
|
virtual void setSaturation(float saturation);
|
||||||
virtual void setColorShift(unsigned int color);
|
virtual void setColorShift(unsigned int color);
|
||||||
virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; };
|
virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; };
|
||||||
virtual void setChangedColor(unsigned int color) { mColorChangedValue = 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.
|
// These functions are used to enable and disable options in menus, i.e. switches and similar.
|
||||||
virtual void setEnabled() { mEnabled = true; };
|
virtual void setEnabled() { mEnabled = true; };
|
||||||
|
@ -191,6 +193,7 @@ protected:
|
||||||
|
|
||||||
unsigned char mOpacity;
|
unsigned char mOpacity;
|
||||||
unsigned int mColor;
|
unsigned int mColor;
|
||||||
|
float mSaturation;
|
||||||
unsigned char mColorOpacity;
|
unsigned char mColorOpacity;
|
||||||
unsigned int mColorShift;
|
unsigned int mColorShift;
|
||||||
unsigned int mColorShiftEnd;
|
unsigned int mColorShiftEnd;
|
||||||
|
|
|
@ -293,6 +293,12 @@ void ImageComponent::setOpacity(unsigned char opacity)
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageComponent::setSaturation(float saturation)
|
||||||
|
{
|
||||||
|
mSaturation = saturation;
|
||||||
|
updateColors();
|
||||||
|
}
|
||||||
|
|
||||||
void ImageComponent::updateVertices()
|
void ImageComponent::updateVertices()
|
||||||
{
|
{
|
||||||
if (!mTexture || !mTexture->isInitialized())
|
if (!mTexture || !mTexture->isInitialized())
|
||||||
|
@ -339,6 +345,10 @@ void ImageComponent::updateColors()
|
||||||
mVertices[1].col = mColorGradientHorizontal ? colorEnd : color;
|
mVertices[1].col = mColorGradientHorizontal ? colorEnd : color;
|
||||||
mVertices[2].col = mColorGradientHorizontal ? color : colorEnd;
|
mVertices[2].col = mColorGradientHorizontal ? color : colorEnd;
|
||||||
mVertices[3].col = 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)
|
void ImageComponent::render(const Transform4x4f& parentTrans)
|
||||||
|
|
|
@ -31,7 +31,6 @@ public:
|
||||||
void setImage(const std::shared_ptr<TextureResource>& texture);
|
void setImage(const std::shared_ptr<TextureResource>& texture);
|
||||||
|
|
||||||
void onSizeChanged() override;
|
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
|
// 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
|
// 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 setColorShiftEnd(unsigned int color);
|
||||||
void setColorGradientHorizontal(bool horizontal);
|
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 setFlipX(bool flip); // Mirror on the X axis.
|
||||||
void setFlipY(bool flip); // Mirror on the Y axis.
|
void setFlipY(bool flip); // Mirror on the Y axis.
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "math/Transform4x4f.h"
|
#include "math/Transform4x4f.h"
|
||||||
#include "math/Vector2i.h"
|
#include "math/Vector2i.h"
|
||||||
|
#include "Shader_GL21.h"
|
||||||
#include "resources/ResourceManager.h"
|
#include "resources/ResourceManager.h"
|
||||||
#include "ImageIO.h"
|
#include "ImageIO.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
@ -155,17 +156,42 @@ namespace Renderer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(LogInfo) << "Created window successfully.";
|
LOG(LogInfo) << "Setting up OpenGL...";
|
||||||
|
|
||||||
|
if (!createContext())
|
||||||
|
return false;
|
||||||
|
|
||||||
createContext();
|
|
||||||
setIcon();
|
setIcon();
|
||||||
setSwapInterval();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroyWindow()
|
static void destroyWindow()
|
||||||
{
|
{
|
||||||
|
#if defined(USE_OPENGL_21)
|
||||||
|
for (auto it = sShaderProgramVector.cbegin();
|
||||||
|
it != sShaderProgramVector.cend(); it++) {
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
destroyContext();
|
destroyContext();
|
||||||
SDL_DestroyWindow(sdlWindow);
|
SDL_DestroyWindow(sdlWindow);
|
||||||
|
|
||||||
|
@ -327,6 +353,34 @@ namespace Renderer
|
||||||
drawTriangleStrips(vertices, 4, _srcBlendFactor, _dstBlendFactor);
|
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; }
|
SDL_Window* getSDLWindow() { return sdlWindow; }
|
||||||
int getWindowWidth() { return windowWidth; }
|
int getWindowWidth() { return windowWidth; }
|
||||||
int getWindowHeight() { return windowHeight; }
|
int getWindowHeight() { return windowHeight; }
|
||||||
|
|
|
@ -9,6 +9,11 @@
|
||||||
#define ES_CORE_RENDERER_RENDERER_H
|
#define ES_CORE_RENDERER_RENDERER_H
|
||||||
|
|
||||||
#include "math/Vector2f.h"
|
#include "math/Vector2f.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Shader_GL21.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class Transform4x4f;
|
class Transform4x4f;
|
||||||
class Vector2i;
|
class Vector2i;
|
||||||
|
@ -16,6 +21,30 @@ struct SDL_Window;
|
||||||
|
|
||||||
namespace Renderer
|
namespace Renderer
|
||||||
{
|
{
|
||||||
|
static std::vector<Shader*> 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
|
namespace Blend
|
||||||
{
|
{
|
||||||
enum Factor {
|
enum Factor {
|
||||||
|
@ -69,6 +98,7 @@ namespace Renderer
|
||||||
Vector2f pos;
|
Vector2f pos;
|
||||||
Vector2f tex;
|
Vector2f tex;
|
||||||
unsigned int col;
|
unsigned int col;
|
||||||
|
float saturation = 1.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
|
@ -94,11 +124,16 @@ namespace Renderer
|
||||||
int getScreenOffsetY();
|
int getScreenOffsetY();
|
||||||
int getScreenRotate();
|
int getScreenRotate();
|
||||||
|
|
||||||
|
unsigned int rgbaToABGR(unsigned int color);
|
||||||
|
unsigned int abgrToRGBA(unsigned int color);
|
||||||
|
|
||||||
|
Shader* getShaderProgram(unsigned int index);
|
||||||
|
|
||||||
// API specific.
|
// API specific.
|
||||||
unsigned int convertColor(const unsigned int _color);
|
unsigned int convertColor(const unsigned int _color);
|
||||||
unsigned int getWindowFlags();
|
unsigned int getWindowFlags();
|
||||||
void setupWindow();
|
void setupWindow();
|
||||||
void createContext();
|
bool createContext();
|
||||||
void destroyContext();
|
void destroyContext();
|
||||||
unsigned int createTexture(
|
unsigned int createTexture(
|
||||||
const Texture::Type _type,
|
const Texture::Type _type,
|
||||||
|
@ -133,7 +168,6 @@ namespace Renderer
|
||||||
void setScissor(const Rect& _scissor);
|
void setScissor(const Rect& _scissor);
|
||||||
void setSwapInterval();
|
void setSwapInterval();
|
||||||
void swapBuffers();
|
void swapBuffers();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ES_CORE_RENDERER_RENDERER_H
|
#endif // ES_CORE_RENDERER_RENDERER_H
|
||||||
|
|
|
@ -6,32 +6,13 @@
|
||||||
|
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "math/Transform4x4f.h"
|
#include "math/Transform4x4f.h"
|
||||||
#include "Log.h"
|
#include "renderers/Renderer.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "Shader_GL21.h"
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <SDL2/SDL_opengl.h>
|
|
||||||
|
|
||||||
namespace Renderer
|
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 SDL_GLContext sdlContext = nullptr;
|
||||||
static GLuint whiteTexture = 0;
|
static GLuint whiteTexture = 0;
|
||||||
|
|
||||||
|
@ -97,12 +78,14 @@ namespace Renderer
|
||||||
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
|
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createContext()
|
bool createContext()
|
||||||
{
|
{
|
||||||
|
bool missingExtension = false;
|
||||||
sdlContext = SDL_GL_CreateContext(getSDLWindow());
|
sdlContext = SDL_GL_CreateContext(getSDLWindow());
|
||||||
|
|
||||||
if (!sdlContext) {
|
if (!sdlContext) {
|
||||||
LOG(LogError) << "Error creating OpenGL context. " << SDL_GetError();
|
LOG(LogError) << "Error creating OpenGL context. " << SDL_GetError();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_GL_MakeCurrent(getSDLWindow(), sdlContext);
|
SDL_GL_MakeCurrent(getSDLWindow(), sdlContext);
|
||||||
|
@ -123,9 +106,31 @@ namespace Renderer
|
||||||
LOG(LogInfo) << "Checking available OpenGL extensions...";
|
LOG(LogInfo) << "Checking available OpenGL extensions...";
|
||||||
std::string glExts = glGetString(GL_EXTENSIONS) ?
|
std::string glExts = glGetString(GL_EXTENSIONS) ?
|
||||||
(const char*)glGetString(GL_EXTENSIONS) : "";
|
(const char*)glGetString(GL_EXTENSIONS) : "";
|
||||||
LOG(LogInfo) << "ARB_texture_non_power_of_two: " <<
|
if (extensions.find("GL_ARB_texture_non_power_of_two") == std::string::npos) {
|
||||||
(extensions.find("ARB_texture_non_power_of_two") !=
|
LOG(LogError) << "GL_ARB_texture_non_power_of_two: MISSING";
|
||||||
std::string::npos ? "ok" : "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};
|
uint8_t data[4] = {255, 255, 255, 255};
|
||||||
whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data);
|
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_VERTEX_ARRAY));
|
||||||
GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||||
GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY));
|
GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyContext()
|
void destroyContext()
|
||||||
|
@ -218,6 +225,20 @@ namespace Renderer
|
||||||
convertBlendFactor(_dstBlendFactor)));
|
convertBlendFactor(_dstBlendFactor)));
|
||||||
|
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices));
|
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(
|
void drawTriangleStrips(
|
||||||
|
@ -234,6 +255,20 @@ namespace Renderer
|
||||||
convertBlendFactor(_dstBlendFactor)));
|
convertBlendFactor(_dstBlendFactor)));
|
||||||
|
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
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)
|
void setProjection(const Transform4x4f& _projection)
|
||||||
|
|
|
@ -16,22 +16,6 @@
|
||||||
|
|
||||||
namespace Renderer
|
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 SDL_GLContext sdlContext = nullptr;
|
||||||
static GLuint whiteTexture = 0;
|
static GLuint whiteTexture = 0;
|
||||||
|
|
||||||
|
@ -91,7 +75,7 @@ namespace Renderer
|
||||||
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
|
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createContext()
|
bool createContext()
|
||||||
{
|
{
|
||||||
sdlContext = SDL_GL_CreateContext(getSDLWindow());
|
sdlContext = SDL_GL_CreateContext(getSDLWindow());
|
||||||
SDL_GL_MakeCurrent(getSDLWindow(), sdlContext);
|
SDL_GL_MakeCurrent(getSDLWindow(), sdlContext);
|
||||||
|
@ -112,9 +96,9 @@ namespace Renderer
|
||||||
LOG(LogInfo) << "Checking available OpenGL extensions...";
|
LOG(LogInfo) << "Checking available OpenGL extensions...";
|
||||||
std::string glExts = glGetString(GL_EXTENSIONS) ?
|
std::string glExts = glGetString(GL_EXTENSIONS) ?
|
||||||
(const char*)glGetString(GL_EXTENSIONS) : "";
|
(const char*)glGetString(GL_EXTENSIONS) : "";
|
||||||
LOG(LogInfo) << "ARB_texture_non_power_of_two: " <<
|
LOG(LogInfo) << "GL_OES_texture_npot: " <<
|
||||||
(extensions.find("ARB_texture_non_power_of_two") !=
|
(extensions.find("GL_OES_texture_npot") !=
|
||||||
std::string::npos ? "ok" : "MISSING");
|
std::string::npos ? "OK" : "MISSING");
|
||||||
|
|
||||||
uint8_t data[4] = {255, 255, 255, 255};
|
uint8_t data[4] = {255, 255, 255, 255};
|
||||||
whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data);
|
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_VERTEX_ARRAY));
|
||||||
GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||||
GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY));
|
GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyContext()
|
void destroyContext()
|
||||||
|
|
204
es-core/src/renderers/Shader_GL21.cpp
Normal file
204
es-core/src/renderers/Shader_GL21.cpp
Normal file
|
@ -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<GLfloat, 4> 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<char> 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<char> 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
|
65
es-core/src/renderers/Shader_GL21.h
Normal file
65
es-core/src/renderers/Shader_GL21.h
Normal file
|
@ -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 <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_opengl.h>
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<GLfloat, 4> 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<std::tuple<std::string, std::string, GLenum>> 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
|
40
resources/shaders/glsl/desaturate.glsl
Normal file
40
resources/shaders/glsl/desaturate.glsl
Normal file
|
@ -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
|
Loading…
Reference in a new issue