Greatly improved the shader post processing code and fixed some related bugs.

This commit is contained in:
Leon Styhre 2021-03-17 20:29:43 +01:00
parent f08b434bc6
commit ded5b1d29b
3 changed files with 159 additions and 120 deletions

View file

@ -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);

View file

@ -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)
{};
};

View file

@ -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));