#pragma once #include "hwrenderer/data/shaderuniforms.h" #include #include struct PostProcessShader; typedef FRenderStyle PPBlendMode; typedef IntRect PPViewport; class PPTexture; class PPShader; enum class PPFilterMode { Nearest, Linear }; enum class PPWrapMode { Clamp, Repeat }; enum class PPTextureType { CurrentPipelineTexture, NextPipelineTexture, PPTexture, SceneColor, SceneFog, SceneNormal, SceneDepth, SwapChain, ShadowMap }; class PPTextureInput { public: PPFilterMode Filter = PPFilterMode::Nearest; PPWrapMode Wrap = PPWrapMode::Clamp; PPTextureType Type = PPTextureType::CurrentPipelineTexture; PPTexture *Texture = nullptr; }; class PPOutput { public: PPTextureType Type = PPTextureType::NextPipelineTexture; PPTexture *Texture = nullptr; }; class PPUniforms { public: PPUniforms() { } PPUniforms(const PPUniforms &src) { Data = src.Data; } ~PPUniforms() { Clear(); } PPUniforms &operator=(const PPUniforms &src) { Data = src.Data; return *this; } void Clear() { Data.Clear(); } template void Set(const T &v) { if (Data.Size() != (int)sizeof(T)) { Data.Resize(sizeof(T)); memcpy(Data.Data(), &v, Data.Size()); } } TArray Data; }; class PPRenderState { public: virtual ~PPRenderState() = default; virtual void PushGroup(const FString &name) = 0; virtual void PopGroup() = 0; virtual void Draw() = 0; void Clear() { Shader = nullptr; Textures = TArray(); Uniforms = PPUniforms(); Viewport = PPViewport(); BlendMode = PPBlendMode(); Output = PPOutput(); ShadowMapBuffers = false; } void SetInputTexture(int index, PPTexture *texture, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp) { if ((int)Textures.Size() < index + 1) Textures.Resize(index + 1); auto &tex = Textures[index]; tex.Filter = filter; tex.Wrap = wrap; tex.Type = PPTextureType::PPTexture; tex.Texture = texture; } void SetInputCurrent(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp) { SetInputSpecialType(index, PPTextureType::CurrentPipelineTexture, filter, wrap); } void SetInputSceneColor(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp) { SetInputSpecialType(index, PPTextureType::SceneColor, filter, wrap); } void SetInputSceneFog(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp) { SetInputSpecialType(index, PPTextureType::SceneFog, filter, wrap); } void SetInputSceneNormal(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp) { SetInputSpecialType(index, PPTextureType::SceneNormal, filter, wrap); } void SetInputSceneDepth(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp) { SetInputSpecialType(index, PPTextureType::SceneDepth, filter, wrap); } void SetInputSpecialType(int index, PPTextureType type, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp) { if ((int)Textures.Size() < index + 1) Textures.Resize(index + 1); auto &tex = Textures[index]; tex.Filter = filter; tex.Wrap = wrap; tex.Type = type; tex.Texture = nullptr; } void SetShadowMapBuffers(bool enable) { ShadowMapBuffers = enable; } void SetOutputTexture(PPTexture *texture) { Output.Type = PPTextureType::PPTexture; Output.Texture = texture; } void SetOutputCurrent() { Output.Type = PPTextureType::CurrentPipelineTexture; Output.Texture = nullptr; } void SetOutputNext() { Output.Type = PPTextureType::NextPipelineTexture; Output.Texture = nullptr; } void SetOutputSceneColor() { Output.Type = PPTextureType::SceneColor; Output.Texture = nullptr; } void SetOutputSwapChain() { Output.Type = PPTextureType::SwapChain; Output.Texture = nullptr; } void SetOutputShadowMap() { Output.Type = PPTextureType::ShadowMap; Output.Texture = nullptr; } void SetNoBlend() { BlendMode.BlendOp = STYLEOP_Add; BlendMode.SrcAlpha = STYLEALPHA_One; BlendMode.DestAlpha = STYLEALPHA_Zero; BlendMode.Flags = 0; } void SetAdditiveBlend() { BlendMode.BlendOp = STYLEOP_Add; BlendMode.SrcAlpha = STYLEALPHA_One; BlendMode.DestAlpha = STYLEALPHA_One; BlendMode.Flags = 0; } void SetAlphaBlend() { BlendMode.BlendOp = STYLEOP_Add; BlendMode.SrcAlpha = STYLEALPHA_Src; BlendMode.DestAlpha = STYLEALPHA_InvSrc; BlendMode.Flags = 0; } PPShader *Shader; TArray Textures; PPUniforms Uniforms; PPViewport Viewport; PPBlendMode BlendMode; PPOutput Output; bool ShadowMapBuffers = false; }; enum class PixelFormat { Rgba8, Rgba16f, R32f, Rg16f, Rgba16_snorm }; class PPResource { public: PPResource() { Next = First; First = this; if (Next) Next->Prev = this; } PPResource(const PPResource &) { Next = First; First = this; if (Next) Next->Prev = this; } virtual ~PPResource() { if (Next) Next->Prev = Prev; if (Prev) Prev->Next = Next; else First = Next; } PPResource &operator=(const PPResource &other) { return *this; } static void ResetAll() { for (PPResource *cur = First; cur; cur = cur->Next) cur->ResetBackend(); } virtual void ResetBackend() = 0; private: static PPResource *First; PPResource *Prev = nullptr; PPResource *Next = nullptr; }; class PPTextureBackend { public: virtual ~PPTextureBackend() = default; }; class PPTexture : public PPResource { public: PPTexture() = default; PPTexture(int width, int height, PixelFormat format, std::shared_ptr data = {}) : Width(width), Height(height), Format(format), Data(data) { } void ResetBackend() override { Backend.reset(); } int Width; int Height; PixelFormat Format; std::shared_ptr Data; std::unique_ptr Backend; }; class PPShaderBackend { public: virtual ~PPShaderBackend() = default; }; class PPShader : public PPResource { public: PPShader() = default; PPShader(const FString &fragment, const FString &defines, const std::vector &uniforms, int version = 330) : FragmentShader(fragment), Defines(defines), Uniforms(uniforms), Version(version) { } void ResetBackend() override { Backend.reset(); } FString VertexShader = "engine/shaders/pp/screenquad.vp"; FString FragmentShader; FString Defines; std::vector Uniforms; int Version = 330; std::unique_ptr Backend; }; ///////////////////////////////////////////////////////////////////////////// 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]) }, }; } }; enum { NumBloomLevels = 4 }; class PPBlurLevel { public: PPViewport Viewport; PPTexture VTexture; PPTexture HTexture; }; class PPBloom { public: void RenderBloom(PPRenderState *renderstate, int sceneWidth, int sceneHeight, int fixedcm); void RenderBlur(PPRenderState *renderstate, int sceneWidth, int sceneHeight, float gameinfobluramount); private: void BlurStep(PPRenderState *renderstate, const BlurUniforms &blurUniforms, PPTexture &input, PPTexture &output, PPViewport viewport, bool vertical); void UpdateTextures(int width, int height); static float ComputeBlurGaussian(float n, float theta); static void ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights); PPBlurLevel levels[NumBloomLevels]; int lastWidth = 0; int lastHeight = 0; PPShader BloomCombine = { "engine/shaders/pp/bloomcombine.fp", "", {} }; PPShader BloomExtract = { "engine/shaders/pp/bloomextract.fp", "", ExtractUniforms::Desc() }; PPShader BlurVertical = { "engine/shaders/pp/blur.fp", "#define BLUR_VERTICAL\n", BlurUniforms::Desc() }; PPShader BlurHorizontal = { "engine/shaders/pp/blur.fp", "#define BLUR_HORIZONTAL\n", BlurUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// struct LensUniforms { float AspectRatio; float Scale; float Padding0, Padding1; FVector4 LensDistortionCoefficient; FVector4 CubicDistortionValue; static std::vector Desc() { return { { "Aspect", UniformType::Float, offsetof(LensUniforms, AspectRatio) }, { "Scale", UniformType::Float, offsetof(LensUniforms, Scale) }, { "Padding0", UniformType::Float, offsetof(LensUniforms, Padding0) }, { "Padding1", UniformType::Float, offsetof(LensUniforms, Padding1) }, { "k", UniformType::Vec4, offsetof(LensUniforms, LensDistortionCoefficient) }, { "kcube", UniformType::Vec4, offsetof(LensUniforms, CubicDistortionValue) } }; } }; class PPLensDistort { public: void Render(PPRenderState *renderstate); private: PPShader Lens = { "engine/shaders/pp/lensdistortion.fp", "", LensUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// struct FXAAUniforms { FVector2 ReciprocalResolution; float Padding0, Padding1; static std::vector Desc() { return { { "ReciprocalResolution", UniformType::Vec2, offsetof(FXAAUniforms, ReciprocalResolution) }, { "Padding0", UniformType::Float, offsetof(FXAAUniforms, Padding0) }, { "Padding1", UniformType::Float, offsetof(FXAAUniforms, Padding1) } }; } }; class PPFXAA { public: void Render(PPRenderState *renderstate); private: void CreateShaders(); int GetMaxVersion(); FString GetDefines(); PPShader FXAALuma; PPShader FXAA; int LastQuality = -1; }; ///////////////////////////////////////////////////////////////////////////// struct ExposureExtractUniforms { FVector2 Scale; FVector2 Offset; static std::vector Desc() { return { { "Scale", UniformType::Vec2, offsetof(ExposureExtractUniforms, Scale) }, { "Offset", UniformType::Vec2, offsetof(ExposureExtractUniforms, Offset) } }; } }; struct ExposureCombineUniforms { float ExposureBase; float ExposureMin; float ExposureScale; float ExposureSpeed; static std::vector Desc() { return { { "ExposureBase", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureBase) }, { "ExposureMin", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureMin) }, { "ExposureScale", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureScale) }, { "ExposureSpeed", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureSpeed) } }; } }; class PPExposureLevel { public: PPViewport Viewport; PPTexture Texture; }; class PPCameraExposure { public: void Render(PPRenderState *renderstate, int sceneWidth, int sceneHeight); PPTexture CameraTexture = { 1, 1, PixelFormat::R32f }; private: void UpdateTextures(int width, int height); std::vector ExposureLevels; bool FirstExposureFrame = true; PPShader ExposureExtract = { "engine/shaders/pp/exposureextract.fp", "", ExposureExtractUniforms::Desc() }; PPShader ExposureAverage = { "engine/shaders/pp/exposureaverage.fp", "", {}, 400 }; PPShader ExposureCombine = { "engine/shaders/pp/exposurecombine.fp", "", ExposureCombineUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// struct ColormapUniforms { FVector4 MapStart; FVector4 MapRange; static std::vector Desc() { return { { "uFixedColormapStart", UniformType::Vec4, offsetof(ColormapUniforms, MapStart) }, { "uFixedColormapRange", UniformType::Vec4, offsetof(ColormapUniforms, MapRange) }, }; } }; ///////////////////////////////////////////////////////////////////////////// class PPTonemap { public: void Render(PPRenderState *renderstate); void ClearTonemapPalette() { PaletteTexture = {}; } private: void UpdateTextures(); PPTexture PaletteTexture; PPShader LinearShader = { "engine/shaders/pp/tonemap.fp", "#define LINEAR\n", {} }; PPShader ReinhardShader = { "engine/shaders/pp/tonemap.fp", "#define REINHARD\n", {} }; PPShader HejlDawsonShader = { "engine/shaders/pp/tonemap.fp", "#define HEJLDAWSON\n", {} }; PPShader Uncharted2Shader = { "engine/shaders/pp/tonemap.fp", "#define UNCHARTED2\n", {} }; PPShader PaletteShader = { "engine/shaders/pp/tonemap.fp", "#define PALETTE\n", {} }; enum TonemapMode { None, Uncharted2, HejlDawson, Reinhard, Linear, Palette, NumTonemapModes }; }; ///////////////////////////////////////////////////////////////////////////// struct LinearDepthUniforms { int SampleIndex; float LinearizeDepthA; float LinearizeDepthB; float InverseDepthRangeA; float InverseDepthRangeB; float Padding0, Padding1, Padding2; FVector2 Scale; FVector2 Offset; static std::vector Desc() { return { { "SampleIndex", UniformType::Int, offsetof(LinearDepthUniforms, SampleIndex) }, { "LinearizeDepthA", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthA) }, { "LinearizeDepthB", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthB) }, { "InverseDepthRangeA", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeA) }, { "InverseDepthRangeB", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeB) }, { "Padding0", UniformType::Float, offsetof(LinearDepthUniforms, Padding0) }, { "Padding1", UniformType::Float, offsetof(LinearDepthUniforms, Padding1) }, { "Padding2", UniformType::Float, offsetof(LinearDepthUniforms, Padding2) }, { "Scale", UniformType::Vec2, offsetof(LinearDepthUniforms, Scale) }, { "Offset", UniformType::Vec2, offsetof(LinearDepthUniforms, Offset) } }; } }; struct SSAOUniforms { FVector2 UVToViewA; FVector2 UVToViewB; FVector2 InvFullResolution; float NDotVBias; float NegInvR2; float RadiusToScreen; float AOMultiplier; float AOStrength; int SampleIndex; float Padding0, Padding1; FVector2 Scale; FVector2 Offset; static std::vector Desc() { return { { "UVToViewA", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewA) }, { "UVToViewB", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewB) }, { "InvFullResolution", UniformType::Vec2, offsetof(SSAOUniforms, InvFullResolution) }, { "NDotVBias", UniformType::Float, offsetof(SSAOUniforms, NDotVBias) }, { "NegInvR2", UniformType::Float, offsetof(SSAOUniforms, NegInvR2) }, { "RadiusToScreen", UniformType::Float, offsetof(SSAOUniforms, RadiusToScreen) }, { "AOMultiplier", UniformType::Float, offsetof(SSAOUniforms, AOMultiplier) }, { "AOStrength", UniformType::Float, offsetof(SSAOUniforms, AOStrength) }, { "SampleIndex", UniformType::Int, offsetof(SSAOUniforms, SampleIndex) }, { "Padding0", UniformType::Float, offsetof(SSAOUniforms, Padding0) }, { "Padding1", UniformType::Float, offsetof(SSAOUniforms, Padding1) }, { "Scale", UniformType::Vec2, offsetof(SSAOUniforms, Scale) }, { "Offset", UniformType::Vec2, offsetof(SSAOUniforms, Offset) }, }; } }; struct DepthBlurUniforms { float BlurSharpness; float PowExponent; float Padding0, Padding1; static std::vector Desc() { return { { "BlurSharpness", UniformType::Float, offsetof(DepthBlurUniforms, BlurSharpness) }, { "PowExponent", UniformType::Float, offsetof(DepthBlurUniforms, PowExponent) }, { "Padding0", UniformType::Float, offsetof(DepthBlurUniforms, Padding0) }, { "Padding1", UniformType::Float, offsetof(DepthBlurUniforms, Padding1) } }; } }; struct AmbientCombineUniforms { int SampleCount; int DebugMode, Padding1, Padding2; FVector2 Scale; FVector2 Offset; static std::vector Desc() { return { { "SampleCount", UniformType::Int, offsetof(AmbientCombineUniforms, SampleCount) }, { "DebugMode", UniformType::Int, offsetof(AmbientCombineUniforms, DebugMode) }, { "Padding1", UniformType::Int, offsetof(AmbientCombineUniforms, Padding1) }, { "Padding2", UniformType::Int, offsetof(AmbientCombineUniforms, Padding2) }, { "Scale", UniformType::Vec2, offsetof(AmbientCombineUniforms, Scale) }, { "Offset", UniformType::Vec2, offsetof(AmbientCombineUniforms, Offset) } }; } }; class PPAmbientOcclusion { public: PPAmbientOcclusion(); void Render(PPRenderState *renderstate, float m5, int sceneWidth, int sceneHeight); private: void CreateShaders(); void UpdateTextures(int width, int height); enum Quality { Off, LowQuality, MediumQuality, HighQuality, NumQualityModes }; int AmbientWidth = 0; int AmbientHeight = 0; int LastQuality = -1; int LastWidth = 0; int LastHeight = 0; PPShader LinearDepth; PPShader LinearDepthMS; PPShader AmbientOcclude; PPShader AmbientOccludeMS; PPShader BlurVertical; PPShader BlurHorizontal; PPShader Combine; PPShader CombineMS; PPTexture LinearDepthTexture; PPTexture Ambient0; PPTexture Ambient1; enum { NumAmbientRandomTextures = 3 }; PPTexture AmbientRandomTexture[NumAmbientRandomTextures]; }; struct PresentUniforms { float InvGamma; float Contrast; float Brightness; float Saturation; int GrayFormula; int WindowPositionParity; // top-of-window might not be top-of-screen FVector2 Scale; FVector2 Offset; float ColorScale; int HdrMode; static std::vector Desc() { return { { "InvGamma", UniformType::Float, offsetof(PresentUniforms, InvGamma) }, { "Contrast", UniformType::Float, offsetof(PresentUniforms, Contrast) }, { "Brightness", UniformType::Float, offsetof(PresentUniforms, Brightness) }, { "Saturation", UniformType::Float, offsetof(PresentUniforms, Saturation) }, { "GrayFormula", UniformType::Int, offsetof(PresentUniforms, GrayFormula) }, { "WindowPositionParity", UniformType::Int, offsetof(PresentUniforms, WindowPositionParity) }, { "UVScale", UniformType::Vec2, offsetof(PresentUniforms, Scale) }, { "UVOffset", UniformType::Vec2, offsetof(PresentUniforms, Offset) }, { "ColorScale", UniformType::Float, offsetof(PresentUniforms, ColorScale) }, { "HdrMode", UniformType::Int, offsetof(PresentUniforms, HdrMode) } }; } }; class PPPresent { public: PPPresent(); PPTexture Dither; PPShader Present = { "engine/shaders/pp/present.fp", "", PresentUniforms::Desc() }; PPShader Checker3D = { "engine/shaders/pp/present_checker3d.fp", "", PresentUniforms::Desc() }; PPShader Column3D = { "engine/shaders/pp/present_column3d.fp", "", PresentUniforms::Desc() }; PPShader Row3D = { "engine/shaders/pp/present_row3d.fp", "", PresentUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// class Postprocess { public: PPBloom bloom; PPLensDistort lens; PPFXAA fxaa; PPCameraExposure exposure; PPTonemap tonemap; PPAmbientOcclusion ssao; PPPresent present; void Pass1(PPRenderState *state, int fixedcm, int sceneWidth, int sceneHeight); void Pass2(PPRenderState* state, int fixedcm, int sceneWidth, int sceneHeight); }; extern Postprocess hw_postprocess;