mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-18 23:15:38 +00:00
Greatly improved the shader post processing code and fixed some related bugs.
This commit is contained in:
parent
f08b434bc6
commit
ded5b1d29b
|
@ -323,30 +323,39 @@ void Window::render()
|
||||||
// Generate a cache texture of the shaded background when opening the menu, which
|
// Generate a cache texture of the shaded background when opening the menu, which
|
||||||
// will remain valid until the menu is closed. This is way faster than having to
|
// will remain valid until the menu is closed. This is way faster than having to
|
||||||
// render the shaders for every frame.
|
// render the shaders for every frame.
|
||||||
|
// const auto backgroundStartTime = std::chrono::system_clock::now();
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> mPostprocessedBackground;
|
std::shared_ptr<TextureResource> mPostprocessedBackground;
|
||||||
mPostprocessedBackground = TextureResource::get("");
|
mPostprocessedBackground = TextureResource::get("");
|
||||||
unsigned char* processedTexture = new unsigned char[Renderer::getScreenWidth() *
|
unsigned char* processedTexture = new unsigned char[Renderer::getScreenWidth() *
|
||||||
Renderer::getScreenHeight() * 4];
|
Renderer::getScreenHeight() * 4];
|
||||||
|
|
||||||
// Defocus the background using three passes of gaussian blur.
|
// Defocus the background using multiple passes of gaussian blur, with the number
|
||||||
Renderer::shaderParameters blurParameters;
|
// of iterations relative to the screen resolution.
|
||||||
blurParameters.shaderPasses = 3;
|
Renderer::shaderParameters backgroundParameters;
|
||||||
|
float heightModifier = Renderer::getScreenHeightModifier();
|
||||||
|
|
||||||
|
if (heightModifier < 1)
|
||||||
|
backgroundParameters.blurPasses = 2; // Below 1080
|
||||||
|
else if (heightModifier >= 4)
|
||||||
|
backgroundParameters.blurPasses = 12; // 8K
|
||||||
|
else if (heightModifier >= 2.9)
|
||||||
|
backgroundParameters.blurPasses = 10; // 6K
|
||||||
|
else if (heightModifier >= 2.6)
|
||||||
|
backgroundParameters.blurPasses = 8; // 5K
|
||||||
|
else if (heightModifier >= 2)
|
||||||
|
backgroundParameters.blurPasses = 5; // 4K
|
||||||
|
else if (heightModifier >= 1.3)
|
||||||
|
backgroundParameters.blurPasses = 3; // 1440
|
||||||
|
else if (heightModifier >= 1)
|
||||||
|
backgroundParameters.blurPasses = 2; // 1080
|
||||||
|
|
||||||
|
// Also dim the background slightly.
|
||||||
|
backgroundParameters.fragmentDimValue = 0.60f;
|
||||||
|
|
||||||
Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL |
|
Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL |
|
||||||
Renderer::SHADER_BLUR_VERTICAL, blurParameters, processedTexture);
|
Renderer::SHADER_BLUR_VERTICAL | Renderer::SHADER_DIM,
|
||||||
|
backgroundParameters, processedTexture);
|
||||||
mPostprocessedBackground->initFromPixels(processedTexture,
|
|
||||||
Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
|
||||||
|
|
||||||
mBackgroundOverlay->setImage(mPostprocessedBackground);
|
|
||||||
mBackgroundOverlay->render(transform);
|
|
||||||
|
|
||||||
// Dim the background. We need to do this as a separate step as combining
|
|
||||||
// it with the blurring leads to very strange and severe artifacts.
|
|
||||||
// This is for sure a bug that needs to be resolved at some later date.
|
|
||||||
Renderer::shaderParameters blackParameters;
|
|
||||||
blackParameters.fragmentDimValue = 0.6f;
|
|
||||||
Renderer::shaderPostprocessing(Renderer::SHADER_DIM,
|
|
||||||
blackParameters, processedTexture);
|
|
||||||
|
|
||||||
mPostprocessedBackground->initFromPixels(processedTexture,
|
mPostprocessedBackground->initFromPixels(processedTexture,
|
||||||
Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
||||||
|
@ -365,11 +374,18 @@ void Window::render()
|
||||||
|
|
||||||
delete[] processedTexture;
|
delete[] processedTexture;
|
||||||
mCachedBackground = true;
|
mCachedBackground = true;
|
||||||
|
|
||||||
|
// const auto backgroundEndTime = std::chrono::system_clock::now();
|
||||||
|
// LOG(LogDebug) << "Window::render(): Time to create cached background: " <<
|
||||||
|
// std::chrono::duration_cast<std::chrono::milliseconds>
|
||||||
|
// (backgroundEndTime - backgroundStartTime).count() << " ms";
|
||||||
|
}
|
||||||
|
// Fade in the cached background, unless the menu is set to open without any animation.
|
||||||
|
if (Settings::getInstance()->getString("MenuOpeningEffect") != "none") {
|
||||||
|
mBackgroundOverlay->setOpacity(mBackgroundOverlayOpacity);
|
||||||
|
if (mBackgroundOverlayOpacity < 255)
|
||||||
|
mBackgroundOverlayOpacity = Math::clamp(mBackgroundOverlayOpacity + 30, 0, 255);
|
||||||
}
|
}
|
||||||
// Fade in the cached background.
|
|
||||||
mBackgroundOverlay->setOpacity(mBackgroundOverlayOpacity);
|
|
||||||
if (mBackgroundOverlayOpacity < 255)
|
|
||||||
mBackgroundOverlayOpacity = Math::clamp(mBackgroundOverlayOpacity + 30, 0, 255);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mBackgroundOverlay->render(transform);
|
mBackgroundOverlay->render(transform);
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace Renderer
|
||||||
float fragmentSaturation;
|
float fragmentSaturation;
|
||||||
float fragmentDimValue;
|
float fragmentDimValue;
|
||||||
float fragmentOpacity;
|
float fragmentOpacity;
|
||||||
unsigned int shaderPasses;
|
unsigned int blurPasses;
|
||||||
|
|
||||||
shaderParameters()
|
shaderParameters()
|
||||||
: textureSize({0.0f, 0.0f}),
|
: textureSize({0.0f, 0.0f}),
|
||||||
|
@ -44,7 +44,7 @@ namespace Renderer
|
||||||
fragmentSaturation(1.0f),
|
fragmentSaturation(1.0f),
|
||||||
fragmentDimValue(0.4f),
|
fragmentDimValue(0.4f),
|
||||||
fragmentOpacity(1.0f),
|
fragmentOpacity(1.0f),
|
||||||
shaderPasses(1)
|
blurPasses(1)
|
||||||
{};
|
{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -259,81 +259,79 @@ namespace Renderer
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (unsigned int i = 0; i < _parameters.shaderPasses; i++) {
|
// If saturation is set below the maximum (default) value, run the
|
||||||
// If saturation is set below the maximum (default) value, run the
|
// desaturation shader.
|
||||||
// desaturation shader.
|
if (_vertices->saturation < 1.0 || _parameters.fragmentSaturation < 1.0) {
|
||||||
if (_vertices->saturation < 1.0 || _parameters.fragmentSaturation < 1.0) {
|
Shader* runShader = getShaderProgram(SHADER_DESATURATE);
|
||||||
Shader* runShader = getShaderProgram(SHADER_DESATURATE);
|
// Only try to use the shader if it has been loaded properly.
|
||||||
// Only try to use the shader if it has been loaded properly.
|
if (runShader) {
|
||||||
if (runShader) {
|
runShader->activateShaders();
|
||||||
runShader->activateShaders();
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setSaturation(_vertices->saturation);
|
||||||
runShader->setSaturation(_vertices->saturation);
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
runShader->deactivateShaders();
|
||||||
runShader->deactivateShaders();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_vertices->shaders & SHADER_OPACITY) {
|
if (_vertices->shaders & SHADER_OPACITY) {
|
||||||
Shader* runShader = getShaderProgram(SHADER_OPACITY);
|
Shader* runShader = getShaderProgram(SHADER_OPACITY);
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
_vertices->opacity < 1.0 ?
|
_vertices->opacity < 1.0 ?
|
||||||
runShader->setOpacity(_vertices->opacity) :
|
runShader->setOpacity(_vertices->opacity) :
|
||||||
runShader->setOpacity(_parameters.fragmentOpacity);
|
runShader->setOpacity(_parameters.fragmentOpacity);
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if any other shaders are set to be used and if so, run them.
|
// Check if any other shaders are set to be used and if so, run them.
|
||||||
if (_vertices->shaders & SHADER_DIM) {
|
if (_vertices->shaders & SHADER_DIM) {
|
||||||
Shader* runShader = getShaderProgram(SHADER_DIM);
|
Shader* runShader = getShaderProgram(SHADER_DIM);
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setDimValue(_parameters.fragmentDimValue);
|
runShader->setDimValue(_parameters.fragmentDimValue);
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_vertices->shaders & SHADER_BLUR_HORIZONTAL) {
|
if (_vertices->shaders & SHADER_BLUR_HORIZONTAL) {
|
||||||
Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL);
|
Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL);
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setTextureSize({width, height});
|
runShader->setTextureSize({width, height});
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_vertices->shaders & SHADER_BLUR_VERTICAL) {
|
if (_vertices->shaders & SHADER_BLUR_VERTICAL) {
|
||||||
Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL);
|
Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL);
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setTextureSize({width, height});
|
runShader->setTextureSize({width, height});
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_vertices->shaders & SHADER_SCANLINES) {
|
if (_vertices->shaders & SHADER_SCANLINES) {
|
||||||
Shader* runShader = getShaderProgram(SHADER_SCANLINES);
|
Shader* runShader = getShaderProgram(SHADER_SCANLINES);
|
||||||
float shaderWidth = width * 1.2f;
|
float shaderWidth = width * 1.2f;
|
||||||
// Workaround to get the scanlines to render somehow proportional to the
|
// Workaround to get the scanlines to render somehow proportional to the
|
||||||
// resolution. A better solution is for sure needed.
|
// resolution. A better solution is for sure needed.
|
||||||
float shaderHeight = height + height / (static_cast<int>(height) >> 7) * 2.0f;
|
float shaderHeight = height + height / (static_cast<int>(height) >> 7) * 2.0f;
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setTextureSize({shaderWidth, shaderHeight});
|
runShader->setTextureSize({shaderWidth, shaderHeight});
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,6 +401,7 @@ namespace Renderer
|
||||||
unsigned char* textureRGBA)
|
unsigned char* textureRGBA)
|
||||||
{
|
{
|
||||||
Vertex vertices[4];
|
Vertex vertices[4];
|
||||||
|
std::vector<unsigned int>shaderList;
|
||||||
GLuint width = getScreenWidth();
|
GLuint width = getScreenWidth();
|
||||||
GLuint height = getScreenHeight();
|
GLuint height = getScreenHeight();
|
||||||
float widthf = static_cast<float>(width);
|
float widthf = static_cast<float>(width);
|
||||||
|
@ -415,7 +414,18 @@ namespace Renderer
|
||||||
vertices[2] = { { widthf, 0 }, { 1, 1 }, 0 };
|
vertices[2] = { { widthf, 0 }, { 1, 1 }, 0 };
|
||||||
vertices[3] = { { widthf, heightf }, { 1, 0 }, 0};
|
vertices[3] = { { widthf, heightf }, { 1, 0 }, 0};
|
||||||
|
|
||||||
vertices[0].shaders = shaders;
|
if (shaders & Renderer::SHADER_DESATURATE)
|
||||||
|
shaderList.push_back(Renderer::SHADER_DESATURATE);
|
||||||
|
if (shaders & Renderer::SHADER_OPACITY)
|
||||||
|
shaderList.push_back(Renderer::SHADER_OPACITY);
|
||||||
|
if (shaders & Renderer::SHADER_DIM)
|
||||||
|
shaderList.push_back(Renderer::SHADER_DIM);
|
||||||
|
if (shaders & Renderer::SHADER_BLUR_HORIZONTAL)
|
||||||
|
shaderList.push_back(Renderer::SHADER_BLUR_HORIZONTAL);
|
||||||
|
if (shaders & Renderer::SHADER_BLUR_VERTICAL)
|
||||||
|
shaderList.push_back(Renderer::SHADER_BLUR_VERTICAL);
|
||||||
|
if (shaders & Renderer::SHADER_SCANLINES)
|
||||||
|
shaderList.push_back(Renderer::SHADER_SCANLINES);
|
||||||
|
|
||||||
if (parameters.fragmentSaturation < 1.0)
|
if (parameters.fragmentSaturation < 1.0)
|
||||||
vertices[0].saturation = parameters.fragmentSaturation;
|
vertices[0].saturation = parameters.fragmentSaturation;
|
||||||
|
@ -424,40 +434,53 @@ namespace Renderer
|
||||||
GLuint screenTexture = createTexture(Texture::RGBA, false, false, width, height, nullptr);
|
GLuint screenTexture = createTexture(Texture::RGBA, false, false, width, height, nullptr);
|
||||||
|
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
|
|
||||||
|
|
||||||
// Attach the texture to the shader framebuffer.
|
for (int i = 0; i < shaderList.size(); i++) {
|
||||||
GL_CHECK_ERROR(glFramebufferTexture2D(
|
vertices[0].shaders = shaderList[i];
|
||||||
GL_FRAMEBUFFER,
|
int shaderPasses = 1;
|
||||||
GL_COLOR_ATTACHMENT0,
|
// For the blur shaders there is an optional variable to set the number of passes
|
||||||
GL_TEXTURE_2D,
|
// to execute, which proportionally affects the blur amount.
|
||||||
screenTexture,
|
if (shaderList[i] == Renderer::SHADER_BLUR_HORIZONTAL ||
|
||||||
0));
|
shaderList[i] == Renderer::SHADER_BLUR_VERTICAL)
|
||||||
|
shaderPasses = parameters.blurPasses;
|
||||||
|
|
||||||
// Blit the screen contents to screenTexture.
|
for (int p = 0; p < shaderPasses; p++) {
|
||||||
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
|
||||||
|
|
||||||
// Apply/render the shaders.
|
// Attach the texture to the shader framebuffer.
|
||||||
drawTriangleStrips(vertices, 4, Transform4x4f::Identity(),
|
GL_CHECK_ERROR(glFramebufferTexture2D(
|
||||||
Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA, parameters);
|
GL_FRAMEBUFFER,
|
||||||
|
GL_COLOR_ATTACHMENT0,
|
||||||
|
GL_TEXTURE_2D,
|
||||||
|
screenTexture,
|
||||||
|
0));
|
||||||
|
|
||||||
// If textureRGBA has an address, it means that the output should go to this texture
|
// Blit the screen contents to screenTexture.
|
||||||
// rather than to the screen. The glReadPixels() function is slow, but since this would
|
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||||
// typically only run every now and then to create a cached screen texture, it doesn't
|
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
||||||
// really matter.
|
|
||||||
if (textureRGBA) {
|
// Apply/render the shaders.
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
drawTriangleStrips(vertices, 4, Transform4x4f::Identity(),
|
||||||
GL_CHECK_ERROR(glReadPixels(0, 0, width, height,
|
Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA, parameters);
|
||||||
GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
|
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
// If textureRGBA has an address, it means that the output should go to this
|
||||||
}
|
// texture rather than to the screen. The glReadPixels() function is slow, but
|
||||||
else {
|
// since this will typically only run every now and then to create a cached
|
||||||
// Blit the resulting postprocessed texture back to the primary framebuffer.
|
// screen texture, it doesn't really matter.
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
if (textureRGBA) {
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
||||||
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
GL_CHECK_ERROR(glReadPixels(0, 0, width, height,
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
|
||||||
|
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Blit the resulting postprocessed texture back to the primary framebuffer.
|
||||||
|
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
||||||
|
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
||||||
|
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||||
|
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
|
||||||
|
|
Loading…
Reference in a new issue