From da5ecf1e5bc3d037a8e2c1979afe6767c6f36a95 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 19 Jun 2018 22:16:50 +0200 Subject: [PATCH] - create bloom pass in declarative postprocess form --- src/CMakeLists.txt | 1 + .../postprocessing/hw_postprocess.cpp | 191 ++++++++++++++++++ .../postprocessing/hw_postprocess.h | 190 +++++++++++++++++ 3 files changed, 382 insertions(+) create mode 100644 src/hwrenderer/postprocessing/hw_postprocess.cpp create mode 100644 src/hwrenderer/postprocessing/hw_postprocess.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59b1e706cc..2f7f1c0c37 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1061,6 +1061,7 @@ set (PCH_SOURCES hwrenderer/dynlights/hw_aabbtree.cpp hwrenderer/dynlights/hw_shadowmap.cpp hwrenderer/scene/hw_skydome.cpp + hwrenderer/postprocessing/hw_postprocess.cpp hwrenderer/postprocessing/hw_postprocess_cvars.cpp hwrenderer/postprocessing/hw_postprocessshader.cpp hwrenderer/postprocessing/hw_shadowmapshader.cpp diff --git a/src/hwrenderer/postprocessing/hw_postprocess.cpp b/src/hwrenderer/postprocessing/hw_postprocess.cpp new file mode 100644 index 0000000000..7c10bf13a0 --- /dev/null +++ b/src/hwrenderer/postprocessing/hw_postprocess.cpp @@ -0,0 +1,191 @@ + +#include "v_video.h" +#include "hw_postprocess.h" +#include "hwrenderer/utility/hw_cvars.h" +#include "gl_load/gl_load.h" // for GL_RGBA16F - should we create our own enum instead? + +Postprocess hw_postprocess; + +void PPBloom::DeclareShaders() +{ + PPShader shader; + shader.VertexShader = "shaders/glsl/screenquad.vp"; + shader.FragmentShader = "shaders/glsl/bloomcombine.fp"; + hw_postprocess.Shaders["BloomCombine"] = shader; + + shader.Uniforms = ExtractUniforms::Desc(); + shader.FragmentShader = "shaders/glsl/bloomextract.fp"; + hw_postprocess.Shaders["BloomExtract"] = shader; + + shader.Uniforms = BlurUniforms::Desc(); + shader.FragmentShader = "shaders/glsl/blur.fp"; + shader.Defines = "#define BLUR_VERTICAL\n"; + hw_postprocess.Shaders["BlurVertical"] = shader; + + shader.Defines = "#define BLUR_HORIZONTAL\n"; + hw_postprocess.Shaders["BlurHorizontal"] = shader; +} + +void PPBloom::UpdateTextures(int width, int height) +{ + // No scene, no bloom! + if (width <= 0 || height <= 0) + return; + + int bloomWidth = (width + 1) / 2; + int bloomHeight = (height + 1) / 2; + + for (int i = 0; i < NumBloomLevels; i++) + { + FString vtexture, htexture; + vtexture.Format("Bloom.VTexture.%d", i); + htexture.Format("Bloom.HTexture.%d", i); + + auto &level = levels[i]; + level.Viewport.left = 0; + level.Viewport.top = 0; + level.Viewport.width = (bloomWidth + 1) / 2; + level.Viewport.height = (bloomHeight + 1) / 2; + + PPTextureDesc texture; + texture.Width = level.Viewport.width; + texture.Height = level.Viewport.height; + texture.Format = GL_RGBA16F; + + hw_postprocess.Textures[vtexture] = texture; + hw_postprocess.Textures[htexture] = texture; + + bloomWidth = level.Viewport.width; + bloomHeight = level.Viewport.height; + } +} + +void PPBloom::UpdateSteps(int fixedcm) +{ + TArray steps; + + // Only bloom things if enabled and no special fixed light mode is active + if (!gl_bloom || fixedcm != CM_DEFAULT || gl_ssao_debug) + { + hw_postprocess.Effects["BloomScene"] = steps; + return; + } + + PPStep step; + step.Textures.Resize(1); + + ExtractUniforms extractUniforms; + extractUniforms.Scale = screen->SceneScale(); + extractUniforms.Offset = screen->SceneOffset(); + + auto &level0 = levels[0]; + + // Extract blooming pixels from scene texture: + step.Viewport = level0.Viewport; + step.Textures[0].Type = PPTextureType::CurrentPipelineTexture; + step.Textures[0].Filter = PPFilterMode::Linear; + step.ShaderName = "BloomExtract"; + step.Uniforms.Set(extractUniforms); + step.Output.Type = PPTextureType::PPTexture; + step.Output.Texture = level0.VTexture; + step.BlendMode.BlendOp = STYLEOP_Add; + step.BlendMode.SrcAlpha = STYLEALPHA_One; + step.BlendMode.DestAlpha = STYLEALPHA_Zero; + steps.Push(step); + + const float blurAmount = gl_bloom_amount; + BlurUniforms blurUniforms; + ComputeBlurSamples(7, blurAmount, blurUniforms.SampleWeights); + + // Blur and downscale: + for (int i = 0; i < NumBloomLevels - 1; i++) + { + auto &level = levels[i]; + auto &next = levels[i + 1]; + steps.Push(BlurStep(blurUniforms, level.VTexture, level.HTexture, level.Viewport, false)); + steps.Push(BlurStep(blurUniforms, level.HTexture, next.VTexture, next.Viewport, true)); + } + + // Blur and upscale: + for (int i = NumBloomLevels - 1; i > 0; i--) + { + auto &level = levels[i]; + auto &next = levels[i - 1]; + + steps.Push(BlurStep(blurUniforms, level.VTexture, level.HTexture, level.Viewport, false)); + steps.Push(BlurStep(blurUniforms, level.HTexture, level.VTexture, level.Viewport, true)); + + // Linear upscale: + step.Textures[0].Type = PPTextureType::PPTexture; + step.Textures[0].Filter = PPFilterMode::Linear; + step.Textures[0].Texture = next.VTexture; + step.Output.Texture = next.HTexture; + step.Viewport = next.Viewport; + step.ShaderName = "BloomCombine"; + steps.Push(step); + } + + steps.Push(BlurStep(blurUniforms, level0.VTexture, level0.HTexture, level0.Viewport, false)); + steps.Push(BlurStep(blurUniforms, level0.HTexture, level0.VTexture, level0.Viewport, true)); + + // Add bloom back to scene texture: + step.Textures[0].Type = PPTextureType::PPTexture; + step.Textures[0].Filter = PPFilterMode::Linear; + step.Textures[0].Texture = level0.VTexture; + step.Output.Type = PPTextureType::CurrentPipelineTexture; + step.Viewport = screen->mSceneViewport; + step.ShaderName = "BloomCombine"; + step.BlendMode.BlendOp = STYLEOP_Add; + step.BlendMode.SrcAlpha = STYLEALPHA_One; + step.BlendMode.DestAlpha = STYLEALPHA_One; + step.BlendMode.Flags = 0; + steps.Push(step); + + hw_postprocess.Effects["BloomScene"] = steps; +} + +PPStep PPBloom::BlurStep(const BlurUniforms &blurUniforms, PPTextureName input, PPTextureName output, PPViewport viewport, bool vertical) +{ + PPStep step; + step.Textures.Resize(1); + step.Viewport = viewport; + step.Textures[0].Type = PPTextureType::PPTexture; + step.Textures[0].Filter = PPFilterMode::Nearest; + step.Textures[0].Texture = input; + step.ShaderName = vertical ? "BlurVertical" : "BlurHorizontal"; + step.Uniforms.Set(blurUniforms); + step.Output.Type = PPTextureType::PPTexture; + step.Output.Texture = output; + step.BlendMode.BlendOp = STYLEOP_Add; + step.BlendMode.SrcAlpha = STYLEALPHA_One; + step.BlendMode.DestAlpha = STYLEALPHA_Zero; + step.BlendMode.Flags = 0; + return step; +} + +float PPBloom::ComputeBlurGaussian(float n, float theta) // theta = Blur Amount +{ + return (float)((1.0f / sqrtf(2 * (float)M_PI * theta)) * expf(-(n * n) / (2.0f * theta * theta))); +} + +void PPBloom::ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights) +{ + sampleWeights[0] = ComputeBlurGaussian(0, blurAmount); + + float totalWeights = sampleWeights[0]; + + for (int i = 0; i < sampleCount / 2; i++) + { + float weight = ComputeBlurGaussian(i + 1.0f, blurAmount); + + sampleWeights[i * 2 + 1] = weight; + sampleWeights[i * 2 + 2] = weight; + + totalWeights += weight * 2; + } + + for (int i = 0; i < sampleCount; i++) + { + sampleWeights[i] /= totalWeights; + } +} diff --git a/src/hwrenderer/postprocessing/hw_postprocess.h b/src/hwrenderer/postprocessing/hw_postprocess.h new file mode 100644 index 0000000000..8b570b0de1 --- /dev/null +++ b/src/hwrenderer/postprocessing/hw_postprocess.h @@ -0,0 +1,190 @@ +#pragma once + +#include "hwrenderer/data/shaderuniforms.h" + +typedef FString PPTextureName; +typedef FString PPShaderName; + +typedef FRenderStyle PPBlendMode; +typedef IntRect PPViewport; + +enum class PPFilterMode { Nearest, Linear }; +enum class PPTextureType { CurrentPipelineTexture, NextPipelineTexture, PPTexture }; + +class PPTextureInput +{ +public: + PPFilterMode Filter; + PPTextureType Type; + PPTextureName Texture; +}; + +class PPOutput +{ +public: + PPTextureType Type; + PPTextureName Texture; +}; + +class PPUniforms +{ +public: + PPUniforms() + { + } + + PPUniforms(const PPUniforms &src) + { + if (src.Size > 0) + { + Data = new uint8_t[src.Size]; + Size = src.Size; + memcpy(Data, src.Data, Size); + } + } + + ~PPUniforms() + { + delete[] Data; + } + + PPUniforms &operator=(const PPUniforms &src) + { + if (this != &src) + { + if (src.Size > 0) + { + Data = new uint8_t[src.Size]; + Size = src.Size; + memcpy(Data, src.Data, Size); + } + else + { + delete[] Data; + Data = nullptr; + Size = 0; + } + } + + return *this; + } + + template + void Set(const T &v) + { + if (Size != (int)sizeof(T)) + { + delete[] Data; + Data = nullptr; + Size = 0; + + Data = new uint8_t[Size]; + Size = sizeof(T); + memcpy(Data, &v, Size); + } + } + + void *Data = nullptr; + int Size = 0; +}; + +class PPStep +{ +public: + PPShaderName ShaderName; + TArray Textures; + PPUniforms Uniforms; + PPViewport Viewport; + PPBlendMode BlendMode; + PPOutput Output; +}; + +struct ExtractUniforms +{ + FVector2 Scale; + FVector2 Offset; + + static std::vector Desc() + { + return + { + { "Scale", UniformType::Vec2, offsetof(ExtractUniforms, Scale) }, + { "Offset", UniformType::Vec2, offsetof(ExtractUniforms, Offset) } + }; + } +}; + +struct BlurUniforms +{ + float SampleWeights[8]; + + static std::vector Desc() + { + return + { + { "SampleWeights0", UniformType::Float, offsetof(BlurUniforms, SampleWeights[0]) }, + { "SampleWeights1", UniformType::Float, offsetof(BlurUniforms, SampleWeights[1]) }, + { "SampleWeights2", UniformType::Float, offsetof(BlurUniforms, SampleWeights[2]) }, + { "SampleWeights3", UniformType::Float, offsetof(BlurUniforms, SampleWeights[3]) }, + { "SampleWeights4", UniformType::Float, offsetof(BlurUniforms, SampleWeights[4]) }, + { "SampleWeights5", UniformType::Float, offsetof(BlurUniforms, SampleWeights[5]) }, + { "SampleWeights6", UniformType::Float, offsetof(BlurUniforms, SampleWeights[6]) }, + { "SampleWeights7", UniformType::Float, offsetof(BlurUniforms, SampleWeights[7]) }, + }; + } +}; + +class PPTextureDesc +{ +public: + int Width; + int Height; + int Format; +}; + +class PPShader +{ +public: + FString VertexShader; + FString FragmentShader; + FString Defines; + std::vector Uniforms; +}; + +class Postprocess +{ +public: + TMap> Effects; + TMap Textures; + TMap Shaders; +}; + +extern Postprocess hw_postprocess; + +///////////////////////////////////////////////////////////////////////////// + +enum { NumBloomLevels = 4 }; + +class PPBlurLevel +{ +public: + PPViewport Viewport; + PPTextureName VTexture; + PPTextureName HTexture; +}; + +class PPBloom +{ +public: + void DeclareShaders(); + void UpdateTextures(int sceneWidth, int sceneHeight); + void UpdateSteps(int fixedcm); + +private: + PPStep BlurStep(const BlurUniforms &blurUniforms, PPTextureName input, PPTextureName output, PPViewport viewport, bool vertical); + + static float ComputeBlurGaussian(float n, float theta); + static void ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights); + + PPBlurLevel levels[NumBloomLevels]; +};