// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ // *** PPFX Bloom from the Post-Processing Suite 1.03.29 for ReShade // *** SHADER AUTHOR: Pascal Matthäus ( Euda ) // ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++ // DEV_NOTES //+++++++++++++++++++++++++++++ // Updated for compatibility with ReShade 4 and isolated by Marot Satil. #include "ReShade.fxh" //+++++++++++++++++++++++++++++ // CUSTOM PARAMETERS //+++++++++++++++++++++++++++++ #ifndef PPFXBloomDirtTex #define PPFXBloomDirtTex "DirtA.png" #endif // ** HDR ** uniform bool pEnableHDR < ui_category = "HDR & Tonemap"; ui_label = "Enable HDR & Tonemap"; ui_tooltip = "As brightness-increasing effects like bloom will push colors above the maximum brightness of standard displays and thus oversaturate colors and lead to ugly white 'patches' in bright areas, the colors have to be 'remapped' into the displays range.\nSeveral techniques exist for that, actually it's a whole scientific field. Configurable of course."; > = 0; // ** TONEMAP ** uniform int pTonemapMode < ui_category = "HDR & Tonemap"; ui_label = "Tonemap Mode"; ui_tooltip = "Choose a tonemapping algorithm fitting your personal taste."; ui_type = "combo"; ui_items="Linear, recommended for really low bloomIntensity-values\0Square\0Log10-logarithmic + exposure correction)\0"; > = 0; uniform float pTonemapCurve < ui_category = "HDR & Tonemap"; ui_label = "Tonemap Curve"; ui_tooltip = "How 'aggressive' bright colors are compressed. High values may darken the shadows and mid-tones while preserving details in bright regions (almost-bright skies, for instance)."; ui_type = "slider"; ui_min = 1.0; ui_max = 100.0; ui_step = 0.5; > = 3.0; uniform float pTonemapExposure < ui_category = "HDR & Tonemap"; ui_label = "Tonemap Exposure Adjustment"; ui_tooltip = "Every pixel is multiplied by this value before being tonemapped. You can use this as a brightness control or to specify a mid-gray value for Tonemap Contrast."; ui_type = "slider"; ui_min = 0.001; ui_max = 10.0; ui_step = 0.001; > = 1.2; uniform float pTonemapContrast < ui_category = "HDR & Tonemap"; ui_label = "Tonemap Contrast Intensity"; ui_tooltip = "Pixels darker than 1 are darkened, pixels above are exposed by this option. Combine with higher (2 - 7) tonemapExposure-values to create get a desirable look."; ui_type = "slider"; ui_min = 0.1; ui_max = 10.0; ui_step = 0.001; > = 1.020; uniform float pTonemapSaturateBlacks < ui_category = "HDR & Tonemap"; ui_label = "Tonemap Black Saturation"; ui_tooltip = "Some tonemapping algorithms may desaturate your shadows - this option corrects this issue. Dont's use too high values, it is purposed to be a subtle correction."; ui_type = "slider"; ui_min = 0.01; ui_max = 1.0; ui_step = 0.001; > = 0.0; // ** BLOOM ** #ifndef pBloomDownsampling #define pBloomDownsampling 4 // Bloom Downsampling Factor - Downscales the image before calculating the bloom, thus drastically increasing performance. '1' is fullscreen which doesn't really make sense. I suggest 2-4. High values will cause temporal aliasing | 1 - 16 #endif #ifndef pBloomPrecision #define pBloomPrecision RGBA16 // Bloom Sampling Precision - Options: RGBA8 (low quality, high performance) / RGBA16 (high quality, slightly slower depending on your system) / RGBA32F (overkill) #endif uniform float pBloomRadius < ui_category = "Bloom"; ui_label = "Bloom Sample Radius"; ui_tooltip = "Maximum distance within pixels affect each other - directly affects performance: Combine with pBloomDownsampling to increase your effective radius while keeping a high framerate."; ui_type = "slider"; ui_min = 2.0; ui_max = 250.0; ui_step = 1.0; > = 64.0; uniform float pBloomIntensity < ui_category = "Bloom"; ui_label = "Bloom Overall-Intensity"; ui_tooltip = "The bloom's exposure, I strongly suggest combining this with a tonemap if you choose a high value here."; ui_type = "slider"; ui_min = 0.0; ui_max = 10.0; ui_step = 0.2; > = 0.5; uniform int pBloomBlendMode < ui_category = "Bloom"; ui_label = "Bloom Blend Mode"; ui_tooltip = "Controls how the bloom is mixed with the original frame."; ui_type = "combo"; ui_items="Additive (recommended with tonemaps)\0Lighten (great for night scenes)\0Cover (for configuring/debugging)\0"; > = 0; uniform float pBloomThreshold < ui_category = "Bloom"; ui_label = "Bloom Threshold"; ui_tooltip = "Pixels darker than this value won't cast bloom."; ui_type = "slider"; ui_min = 0.0; ui_max = 1.0; ui_step = 0.001; > = 0.4; uniform float pBloomCurve < ui_category = "Bloom"; ui_label = "Bloom Curve"; ui_tooltip = "The effect's gamma curve - the higher, the more will bloom be damped in dark areas - and vice versa."; ui_type = "slider"; ui_min = 0.1; ui_max = 4.0; ui_step = 0.01; > = 1.5; uniform float pBloomSaturation < ui_category = "Bloom"; ui_label = "Bloom Saturation"; ui_tooltip = "The effect's color saturation. 0 means white, uncolored bloom, 1.500-3.000 yields a vibrant effect while everything above should make your eyes bleed."; ui_type = "slider"; ui_min = 0.0; ui_max = 10.0; ui_step = 0.001; > = 2.0; // ** LENSDIRT ** uniform bool pEnableLensdirt < ui_category = "Lensdirt"; ui_label = "Enable Lensdirt"; ui_tooltip = "Simulates a dirty lens. This effect was introduced in Battlefield 3 back in 2011 and since then was used by many further gamestudios.\nIf enabled, the bloom texture will be used for brightness check, thus scaling the intensity with the local luma instead of the current pixels' one."; > = 0; uniform float pLensdirtIntensity < ui_category = "Lensdirt"; ui_label = "Lensdirt Intensity"; ui_tooltip = "The dirt texture's maximum intensity."; ui_type = "slider"; ui_min = 0.0; ui_max = 1.0; ui_step = 0.01; > = 1.0; uniform float pLensdirtCurve < ui_category = "Lensdirt"; ui_label = "Lensdirt Curve"; ui_tooltip = "The curve which the dirt texture's intensity scales with - try higher values to limit visibility solely to bright/almost-white scenes."; ui_type = "slider"; ui_min = 0.0; ui_max = 10.0; ui_step = 0.1; > = 1.2; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++ TEXTURES +++++ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // *** ESSENTIALS *** texture2D texColor : COLOR; texture texColorHDRA { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; }; texture texColorHDRB < pooled = true; > { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; }; // *** FX RTs *** texture texBloomA { Width = BUFFER_WIDTH/pBloomDownsampling; Height = BUFFER_HEIGHT/pBloomDownsampling; // Available formats: R8, R32F, RG8, RGBA8, RGBA16, RGBA16F, RGBA32F Format = pBloomPrecision; }; texture texBloomB < pooled = true; > { Width = BUFFER_WIDTH/pBloomDownsampling; Height = BUFFER_HEIGHT/pBloomDownsampling; Format = pBloomPrecision; }; // *** EXTERNAL TEXTURES *** texture texBDirt < source = PPFXBloomDirtTex; > { Width = 1920; Height = 1080; }; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++ SAMPLERS +++++ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // *** ESSENTIALS *** sampler2D SamplerColor { Texture = texColor; AddressU = BORDER; AddressV = BORDER; MinFilter = LINEAR; MagFilter = LINEAR; #if BUFFER_COLOR_BIT_DEPTH != 10 SRGBTexture = TRUE; #endif }; sampler SamplerColorHDRA { Texture = texColorHDRA; AddressU = BORDER; AddressV = BORDER; MinFilter = LINEAR; MagFilter = LINEAR; }; sampler SamplerColorHDRB { Texture = texColorHDRB; AddressU = BORDER; AddressV = BORDER; MinFilter = LINEAR; MagFilter = LINEAR; }; // *** FX RTs *** sampler SamplerBloomA { Texture = texBloomA; }; sampler SamplerBloomB { Texture = texBloomB; }; // *** EXTERNAL TEXTURES *** sampler SamplerDirt { Texture = texBDirt; SRGBTexture = TRUE; }; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++ VARIABLES +++++ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ static const float2 pxSize = float2(BUFFER_RCP_WIDTH,BUFFER_RCP_HEIGHT); static const float3 lumaCoeff = float3(0.2126f,0.7152f,0.0722f); //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++ STRUCTS +++++ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ struct VS_OUTPUT_POST { float4 vpos : SV_Position; float2 txcoord : TEXCOORD0; }; struct VS_INPUT_POST { uint id : SV_VertexID; }; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++ HELPERS +++++ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ float3 threshold(float3 pxInput, float colThreshold) { return pxInput*saturate(sign(max(pxInput.x,max(pxInput.y,pxInput.z))-colThreshold)); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++ EFFECTS +++++ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // *** Gaussian Blur *** // Gaussian Blur - Horizontal float3 FX_BlurH( float3 pxInput, sampler source, float2 txCoords, float radius, float downsampling ) { const float texelSize = pxSize.x*downsampling; float2 fetchCoords = txCoords; float weight; const float weightDiv = 1.0+5.0/radius; float sampleSum = 0.5; pxInput+=tex2D(source,txCoords).xyz*0.5; [loop] for (float hOffs=1.5; hOffs { pass setOriginal { VertexShader = VS_PostProcess; PixelShader = PS_SetOriginal; RenderTarget0 = texColorHDRA; } pass bloomThresh { VertexShader = VS_PostProcess; PixelShader = PS_BloomThreshold; RenderTarget0 = texBloomA; } pass bloomH_RadA { VertexShader = VS_PostProcess; PixelShader = PS_BloomH_RadA; RenderTarget0 = texBloomB; } pass bloomV_RadA { VertexShader = VS_PostProcess; PixelShader = PS_BloomV_RadA; RenderTarget0 = texBloomA; } pass bloomMix { VertexShader = VS_PostProcess; PixelShader = PS_BloomMix; RenderTarget0 = texColorHDRB; } pass lightFX { VertexShader = VS_PostProcess; PixelShader = PS_LightFX; RenderTarget0 = texColorHDRA; } pass colorFX { VertexShader = VS_PostProcess; PixelShader = PS_ColorFX; RenderTarget0 = texColorHDRB; } pass imageFX { VertexShader = VS_PostProcess; PixelShader = PS_ImageFX; } }