mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-18 15:15:37 +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
|
||||
// will remain valid until the menu is closed. This is way faster than having to
|
||||
// render the shaders for every frame.
|
||||
// const auto backgroundStartTime = std::chrono::system_clock::now();
|
||||
|
||||
std::shared_ptr<TextureResource> mPostprocessedBackground;
|
||||
mPostprocessedBackground = TextureResource::get("");
|
||||
unsigned char* processedTexture = new unsigned char[Renderer::getScreenWidth() *
|
||||
Renderer::getScreenHeight() * 4];
|
||||
|
||||
// Defocus the background using three passes of gaussian blur.
|
||||
Renderer::shaderParameters blurParameters;
|
||||
blurParameters.shaderPasses = 3;
|
||||
// Defocus the background using multiple passes of gaussian blur, with the number
|
||||
// of iterations relative to the screen resolution.
|
||||
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::SHADER_BLUR_VERTICAL, blurParameters, 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);
|
||||
Renderer::SHADER_BLUR_VERTICAL | Renderer::SHADER_DIM,
|
||||
backgroundParameters, processedTexture);
|
||||
|
||||
mPostprocessedBackground->initFromPixels(processedTexture,
|
||||
Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
||||
|
@ -365,11 +374,18 @@ void Window::render()
|
|||
|
||||
delete[] processedTexture;
|
||||
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
|
||||
|
||||
mBackgroundOverlay->render(transform);
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace Renderer
|
|||
float fragmentSaturation;
|
||||
float fragmentDimValue;
|
||||
float fragmentOpacity;
|
||||
unsigned int shaderPasses;
|
||||
unsigned int blurPasses;
|
||||
|
||||
shaderParameters()
|
||||
: textureSize({0.0f, 0.0f}),
|
||||
|
@ -44,7 +44,7 @@ namespace Renderer
|
|||
fragmentSaturation(1.0f),
|
||||
fragmentDimValue(0.4f),
|
||||
fragmentOpacity(1.0f),
|
||||
shaderPasses(1)
|
||||
blurPasses(1)
|
||||
{};
|
||||
};
|
||||
|
||||
|
|
|
@ -259,81 +259,79 @@ namespace Renderer
|
|||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
}
|
||||
else {
|
||||
for (unsigned int i = 0; i < _parameters.shaderPasses; i++) {
|
||||
// If saturation is set below the maximum (default) value, run the
|
||||
// desaturation shader.
|
||||
if (_vertices->saturation < 1.0 || _parameters.fragmentSaturation < 1.0) {
|
||||
Shader* runShader = getShaderProgram(SHADER_DESATURATE);
|
||||
// Only try to use the shader if it has been loaded properly.
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setSaturation(_vertices->saturation);
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
// If saturation is set below the maximum (default) value, run the
|
||||
// desaturation shader.
|
||||
if (_vertices->saturation < 1.0 || _parameters.fragmentSaturation < 1.0) {
|
||||
Shader* runShader = getShaderProgram(SHADER_DESATURATE);
|
||||
// Only try to use the shader if it has been loaded properly.
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setSaturation(_vertices->saturation);
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
}
|
||||
|
||||
if (_vertices->shaders & SHADER_OPACITY) {
|
||||
Shader* runShader = getShaderProgram(SHADER_OPACITY);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
_vertices->opacity < 1.0 ?
|
||||
runShader->setOpacity(_vertices->opacity) :
|
||||
runShader->setOpacity(_parameters.fragmentOpacity);
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
if (_vertices->shaders & SHADER_OPACITY) {
|
||||
Shader* runShader = getShaderProgram(SHADER_OPACITY);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
_vertices->opacity < 1.0 ?
|
||||
runShader->setOpacity(_vertices->opacity) :
|
||||
runShader->setOpacity(_parameters.fragmentOpacity);
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any other shaders are set to be used and if so, run them.
|
||||
if (_vertices->shaders & SHADER_DIM) {
|
||||
Shader* runShader = getShaderProgram(SHADER_DIM);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setDimValue(_parameters.fragmentDimValue);
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
// Check if any other shaders are set to be used and if so, run them.
|
||||
if (_vertices->shaders & SHADER_DIM) {
|
||||
Shader* runShader = getShaderProgram(SHADER_DIM);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setDimValue(_parameters.fragmentDimValue);
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
}
|
||||
|
||||
if (_vertices->shaders & SHADER_BLUR_HORIZONTAL) {
|
||||
Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setTextureSize({width, height});
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
if (_vertices->shaders & SHADER_BLUR_HORIZONTAL) {
|
||||
Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setTextureSize({width, height});
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
}
|
||||
|
||||
if (_vertices->shaders & SHADER_BLUR_VERTICAL) {
|
||||
Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setTextureSize({width, height});
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
if (_vertices->shaders & SHADER_BLUR_VERTICAL) {
|
||||
Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL);
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setTextureSize({width, height});
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
}
|
||||
|
||||
if (_vertices->shaders & SHADER_SCANLINES) {
|
||||
Shader* runShader = getShaderProgram(SHADER_SCANLINES);
|
||||
float shaderWidth = width * 1.2f;
|
||||
// Workaround to get the scanlines to render somehow proportional to the
|
||||
// resolution. A better solution is for sure needed.
|
||||
float shaderHeight = height + height / (static_cast<int>(height) >> 7) * 2.0f;
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setTextureSize({shaderWidth, shaderHeight});
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
if (_vertices->shaders & SHADER_SCANLINES) {
|
||||
Shader* runShader = getShaderProgram(SHADER_SCANLINES);
|
||||
float shaderWidth = width * 1.2f;
|
||||
// Workaround to get the scanlines to render somehow proportional to the
|
||||
// resolution. A better solution is for sure needed.
|
||||
float shaderHeight = height + height / (static_cast<int>(height) >> 7) * 2.0f;
|
||||
if (runShader) {
|
||||
runShader->activateShaders();
|
||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||
runShader->setTextureSize({shaderWidth, shaderHeight});
|
||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||
runShader->deactivateShaders();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -403,6 +401,7 @@ namespace Renderer
|
|||
unsigned char* textureRGBA)
|
||||
{
|
||||
Vertex vertices[4];
|
||||
std::vector<unsigned int>shaderList;
|
||||
GLuint width = getScreenWidth();
|
||||
GLuint height = getScreenHeight();
|
||||
float widthf = static_cast<float>(width);
|
||||
|
@ -415,7 +414,18 @@ namespace Renderer
|
|||
vertices[2] = { { widthf, 0 }, { 1, 1 }, 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)
|
||||
vertices[0].saturation = parameters.fragmentSaturation;
|
||||
|
@ -424,40 +434,53 @@ namespace Renderer
|
|||
GLuint screenTexture = createTexture(Texture::RGBA, false, false, width, height, nullptr);
|
||||
|
||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
|
||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
|
||||
|
||||
// Attach the texture to the shader framebuffer.
|
||||
GL_CHECK_ERROR(glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D,
|
||||
screenTexture,
|
||||
0));
|
||||
for (int i = 0; i < shaderList.size(); i++) {
|
||||
vertices[0].shaders = shaderList[i];
|
||||
int shaderPasses = 1;
|
||||
// For the blur shaders there is an optional variable to set the number of passes
|
||||
// to execute, which proportionally affects the blur amount.
|
||||
if (shaderList[i] == Renderer::SHADER_BLUR_HORIZONTAL ||
|
||||
shaderList[i] == Renderer::SHADER_BLUR_VERTICAL)
|
||||
shaderPasses = parameters.blurPasses;
|
||||
|
||||
// Blit the screen contents to screenTexture.
|
||||
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
||||
for (int p = 0; p < shaderPasses; p++) {
|
||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
|
||||
|
||||
// Apply/render the shaders.
|
||||
drawTriangleStrips(vertices, 4, Transform4x4f::Identity(),
|
||||
Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA, parameters);
|
||||
// Attach the texture to the shader framebuffer.
|
||||
GL_CHECK_ERROR(glFramebufferTexture2D(
|
||||
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
|
||||
// rather than to the screen. The glReadPixels() function is slow, but since this would
|
||||
// typically only run every now and then to create a cached screen texture, it doesn't
|
||||
// really matter.
|
||||
if (textureRGBA) {
|
||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
||||
GL_CHECK_ERROR(glReadPixels(0, 0, width, height,
|
||||
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));
|
||||
// Blit the screen contents to screenTexture.
|
||||
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
||||
|
||||
// Apply/render the shaders.
|
||||
drawTriangleStrips(vertices, 4, Transform4x4f::Identity(),
|
||||
Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA, parameters);
|
||||
|
||||
// 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
|
||||
// since this will typically only run every now and then to create a cached
|
||||
// screen texture, it doesn't really matter.
|
||||
if (textureRGBA) {
|
||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
||||
GL_CHECK_ERROR(glReadPixels(0, 0, width, height,
|
||||
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));
|
||||
|
|
Loading…
Reference in a new issue