diff --git a/features/Lens Effects/Shaders/Features/LensEffects.ini b/features/Lens Effects/Shaders/Features/LensEffects.ini new file mode 100644 index 0000000000..19f01444dc --- /dev/null +++ b/features/Lens Effects/Shaders/Features/LensEffects.ini @@ -0,0 +1,2 @@ +[Info] +Version = 1-0-0 \ No newline at end of file diff --git a/features/Lens Effects/Shaders/LensEffects/LensEffects.hlsl b/features/Lens Effects/Shaders/LensEffects/LensEffects.hlsl new file mode 100644 index 0000000000..ea8d252933 --- /dev/null +++ b/features/Lens Effects/Shaders/LensEffects/LensEffects.hlsl @@ -0,0 +1,857 @@ +#include "Common/FrameBuffer.hlsli" +#include "Common/Math.hlsli" +#include "Common/CoordMath.hlsli" +#include "Common/Random.hlsli" +#include "Common/Color.hlsli" +#include "Common/SharedData.hlsli" + +//// Structs //////////////////////////////////////////////////////////////////////////// + +struct VertexShaderInput +{ + float4 Position : POSITION; + float2 TexCoord : TEXCOORD; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float2 TexCoord : TEXCOORD0; +}; + +struct MaskVSOutput +{ + float4 Position : SV_POSITION; +}; + +struct StarburstVSOutput +{ + float4 Position : SV_POSITION; + float2 TexCoord : TEXCOORD0; + nointerpolation float4 SunInt : DATA1; + nointerpolation float4 Color : DATA2; + nointerpolation float Scale : DATA3; + nointerpolation float2 ApertureBlades[16] : DATA4; +}; + +struct GhostVSOutput +{ + float4 Position : SV_POSITION; + float2 TexCoord : TEXCOORD0; + float4 AtlasCoords : TEXCOORD1; + nointerpolation float4 SunInt : DATA1; + nointerpolation float4 Color : DATA2; + nointerpolation float2 CADispacement : DATA4; + nointerpolation float2 Vertices[10] : DATA6; +}; + +struct HaloVertexOutput +{ + float4 Position : SV_POSITION; + float4 TexCoord : TEXCOORD0; + nointerpolation float3 SunColor : DATA0; + nointerpolation float4 SunInt : DATA1; +}; + +struct LensGlareVertexOutput +{ + float4 Position : SV_POSITION; + float4 TexCoord : TEXCOORD0; + nointerpolation float3 SunColor : DATA0; + nointerpolation float4 SunInt : DATA1; +}; + +struct SunGlareVertexOutput +{ + float4 Position : SV_POSITION; + float4 TexCoord : TEXCOORD0; + nointerpolation float4 SunInt : DATA0; +}; + +struct IceVertexOutput +{ + float4 Position : SV_POSITION; + float4 TexCoord : TEXCOORD0; + float3 Color : DATA0; +}; +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Resources ////////////////////////////////////////////////////////////////////////// + +cbuffer Settings : register(b1){ + float4 ScreenSize; + uint Frame; + float Precip; + float WeatherBasedFadeout; + float4 SunPosition; + float4 SunBlendColor; + + float UIBurstScale; + float UIBurstInt; + + uint UIEnableBlades; + float UIBladeInt; + float UIBladeVerts; + float UIBladeSplay; + float UIBladeRotation; + float UIBladeLength; + float UIBladeBaseWidth; + float UIBladeWidth; + float UIBladeTaper; + float UIBladeFeather; + float UIBladeFadePow; + float UIBladeFadeDist; + float BladeSplayLen; + + uint UIEnableRays; + float UIRaysInt; + float UIRaysVolume; + float UIRaysLength; + float UIRaysWidth; + + float UIGhostScale; + float UIGhostInt; + float UIGhostSat; + uint UIGhostClampEnable; + float UIGhostClampOffset; + + float UIGhostSize; + float UIGhostOffset; + float UIGhostShape; + float UIGhostRoundness; + float UIGhostRotation; + float UIGhostFeather; + float UIGhostCAFactor; + float UIGhostInsideInt; + + float UIGlareScale; + float UIGlareInt; + uint UIGlareDynXPos; + float UIGlareXOffset; + float UIGlareYOffset; + float UIGlareMaxRot; + float UIGlareCutDepth; + float UIGlareRadius; + float UIGlareTipFade; + + float UIHaloScale; + float UIHaloInt; + uint UIHaloEnableExp; + uint UIHaloFlipExpOffset; + float UIHaloExpMinSize; + float UIHaloExpMaxSize; + float UIHaloRotationSpeed; + float UIHaloIncr; + float UIHaloLength; + float UIHaloWidth; + float UIHaloTaper; + float UIHaloCrShift; + + float UISunGlareScale; + float UISunGlareInt; + float UISunGlareOuterInt; + float UISunGlareFade; + + float UICAIntensity; + float UICAThreshold; + float UICAMaxOffset; + uint UICAOnlyOffsetRed; + + float UIFrostInt; + float SnowPrecipValue; + + float4 UIBurstColor; + float4 UISunGlareColor; + float4 UIHaloColor; + float4 UIGhostColor; + float4 UIGhostAtlas; + float4 UIFrostColor; +}; + +cbuffer SunFlare : register(b2) { + float4 Unused; + float4 Unused2; + float4 SunPositionUV; +}; + +SamplerState Linear_Sampler : register(s10); +SamplerState Point_Sampler : register(s11); +SamplerState PointMirror_Sampler : register(s12); +SamplerComparisonState Depth_Sampler : register(s13); + +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Safe Macros //////////////////////////////////////////////////////////////////////// + +static const float SUNCLIP = 0.05; +static const int OCCLSAMPLES = 20; + +inline float inv(float x) {return 1.0 - x;} +inline float2 inv(float2 x) {return 1.0 - x;} +inline float delta(float x) {return max(x, EPSILON_DIVISION);} +inline float2 delta(float2 x) {return max(x, EPSILON_DIVISION);} +inline float LinearStep(float x, float y, float z) {return Math::LinearStep(x, y, z);} +inline float2 DegreesToVector(float x) {return CoordMath::DegreesToVector(x);} +inline float Luma(float3 x) {return Color::RGBToLuminance(x);} +inline float Chroma(float3 x) {return Color::RGBToChrominance(x);} + +///////////////////////////////////////////////////////////////////////////////////////// + + + +///// Occlusion Shader ////////////////////////////////////////////////////////////////// + +#ifdef OCCLUSION_PIXEL_SHADER + +cbuffer CB2 : register(b2){ + float4 buffer[16];}; + +Texture2D DepthTexture : register(t0); +Texture2D ColorTexture : register(t1); +RWTexture2D SunLUT : register(u0); +RWTexture2D SunLUT_AT : register(u1); +Texture2D MotionVector : register(t2); + + +static const int nFrames = 10; + +float GetTemporalAverage(float Value, uint idx){ + SunLUT[int2(idx, 0)] = Value.xxxx; + float Sum = 0.0; + [unroll] for(int i=0; i < nFrames; ++i) + Sum += SunLUT.Load(int2(5+i, 0)).x; + + return Sum / nFrames; +} + +float UpdateDepthFactor(float2 SunCoords, float SunRadius){ + float DepthFactor = 0.00001; + [loop] for(int i=0; i 0), 1.0 - UIRaysVolume, CoronaDist); + Rays = pow(saturate(InvDist), RandomRays * (6 / delta(Rays))); + + RayMask = smoothstep(RayLength, 0.1, CoronaDist); + RayMask = RayMask * Rays * UIRaysInt; + } + + + float Starburst = BladeMask * UIEnableBlades; + + Starburst *= LinearStep(0.2, 0.6, pow(input.SunInt.x, 2)); + Starburst += RayMask * LinearStep(0.3, 1.0, input.SunInt.x); + Starburst *= input.SunInt.y * input.SunInt.z; + + return float4(Starburst * UIBurstColor.xyz * UIBurstInt, 0.0); +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Ghost Vertex Shader //////////////////////////////////////////////////////////////// + +#ifdef GHOST_VERTEX_SHADER + +Texture2D OcclusionLUT : register(t0); + + +GhostVSOutput main(VertexShaderInput input) +{ + GhostVSOutput output; + output.CADispacement = float2(0,0); + output.AtlasCoords = float4(0,0,0,0); + output.Color = float4(0,0,0,0); + output.Vertices = (float2[10])0; + output.Position.xy = float2(0,0); + + output.TexCoord.xy = (input.TexCoord.xy * 2.0 - 1.0) * UIGhostCAFactor; + output.SunInt = OcclusionLUT.Load(0); + output.SunInt.x = LinearStep(0.4, 0.8, output.SunInt.y * output.SunInt.x) * output.SunInt.z; + + [branch] if(output.SunInt.w > SUNCLIP && UIGhostColor.w > 0.0) + { + float2 SunPosition = SunPositionUV.xy * 2.0 - 1.0; + float SunDist = length(SunPosition); + float4 SunColor_Radius = OcclusionLUT.Load(int3(1,0,0)); + + float GhostScale = Math::MapRange(SunColor_Radius.w, 0.02, 0.04, UIGhostScale * 0.8, UIGhostScale); + GhostScale *= UIGhostSize * UIGhostCAFactor; + + float2x2 GhostRotation = DegreesToVector(UIGhostRotation).xyyx * float2(1.0, -1.0).xyxx; + float RDelta = mad(SunDist, -0.25, 1.0); + + float2 ClampPosition = lerp(float2(UIGhostClampOffset, -1.0), -SunPosition, SunDist); + ClampPosition *= rsqrt(delta(dot(ClampPosition, ClampPosition))) * RDelta; + + float2 GhostPosition = (UIGhostClampEnable) ? ClampPosition : SunDist * normalize(-SunPosition); + GhostPosition = mad(GhostPosition - SunPosition, UIGhostOffset, SunPosition); + + output.Position.xy = mul(GhostRotation, input.Position.xy) * float2(1.0, ScreenSize.z); + output.Position.xy = mad(output.Position.xy, GhostScale, GhostPosition); + + [unroll] for(int i=0; i<9; ++i) + output.Vertices[i] = DegreesToVector(360.0 / UIGhostShape * i); + output.Vertices[9] = output.Vertices[0]; + + float2 AtlasCoords = (Random::RandomSH(UIGhostOffset * 10.0) * 0.5 + 0.5) * UIGhostAtlas.z * output.TexCoord.xy; + output.AtlasCoords = float4(CoordMath::AtlasFetch2x2(AtlasCoords * 0.5 + 0.5, UIGhostAtlas.x), UIGhostAtlas.y, 0); + + float2 CADispacement = normalize(SunPosition - GhostPosition) * float2(1.0, -1.0); + output.CADispacement = mul(GhostRotation, CADispacement * (UIGhostCAFactor - 1.0) * 0.5); + + output.Color.xyz = lerp(SunColor_Radius.xyz, UIGhostColor.xyz, UIGhostSat); + } + + output.Position = float4(output.Position.xy, 0.0, 1.0); + + return output; +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Ghost Pixel Shader ///////////////////////////////////////////////////////////////// + +#ifdef GHOST_PIXEL_SHADER + +Texture2D AtlasTexture : register(t1); + + +float4 main(GhostVSOutput input) : SV_Target +{ + clip(min(input.SunInt.w - SUNCLIP, UIGhostColor.w - 0.01)); + + float2 GhostOffset[3]; + float3 Ghost; + + [unroll] for(int x=0; x<3; ++x){ + GhostOffset[x] = input.TexCoord.xy + (input.CADispacement * (x-1)); + Ghost[x] = saturate(inv(length(GhostOffset[x]))); + } + clip(Math::min3(Ghost)); + + [loop] for(int i = 0; i < UIGhostShape; ++i){ + float2 Edge = ((input.Vertices[i+1] - input.Vertices[i]) * float2(-1.0, 1.0)).yx; + [unroll] for(int j=0; j<3; ++j){ + float Local = dot(Edge, input.Vertices[i] - GhostOffset[j]); + float EdgeDist = lerp(Local, Ghost[j], inv(Local * Local) * UIGhostRoundness); + + Ghost[j] = min(Ghost[j], EdgeDist); + } + } + clip(Math::max3(Ghost)); + + float NSGhost = abs(Math::max3(Ghost)); + + float EdgeEffect = mad(0.5, exp(-5 * NSGhost), exp(-100 * NSGhost)) * 0.5; + EdgeEffect = LinearStep(0.0, 0.6, EdgeEffect); + + Ghost = smoothstep(-0.05, UIGhostFeather, Ghost); + + float3 Color = Ghost * input.Color.xyz; + Color *= lerp(UIGhostInsideInt, 1.5, EdgeEffect); + + float3 Atlas = AtlasTexture.Sample(Linear_Sampler, input.AtlasCoords.xy).xyz; + Color += Atlas * input.AtlasCoords.z * inv(EdgeEffect) * 0.5; + + Color *= UIGhostInt * UIGhostColor.w * input.SunInt.x; + + return float4(Color, 1.0); + +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Halo Vertex Shader ///////////////////////////////////////////////////////////////// + +#ifdef HALO_VERTEX_SHADER + +Texture2D OcclusionLUT : register(t0); + + +HaloVertexOutput main(VertexShaderInput input) +{ + HaloVertexOutput output; + output.TexCoord = input.TexCoord.xyxy * 2.0 - 1.0; + output.Position = float4(0.0, 0.0, 0.0, 1.0); + + float4 SunParams = OcclusionLUT.Load(int3(1,0,0)); + output.SunColor = SunParams.xyz; + output.SunInt = OcclusionLUT.Load(0); + output.SunInt.x *= output.SunInt.y; + + float Scale = Math::MapRange(SunParams.w, 0.02, 0.05, UIHaloScale * 0.8, UIHaloScale); + output.SunInt.z *= Math::MapRange(SunParams.w, 0.02, 0.05, 0.8, 1.0); + + if (output.SunInt.w > SUNCLIP){ + float2 SunPosition = SunPositionUV.xy * 2.0 - 1.0; + float Dist = length(SunPosition); + + float2x2 RotateOffset = DegreesToVector(-SunPosition.x * (360 * UIHaloRotationSpeed)).xyyx; + + output.Position.xy = mul(RotateOffset * float2(1.0, -1.0).xyxx, input.Position.xy); + output.Position.y *= ScreenSize.z; + + float Flipped = (UIHaloFlipExpOffset) ? 1.0 - Dist : Dist; + + float ScaleOffset = lerp(UIHaloExpMinSize, UIHaloExpMaxSize * 5.0, saturate(Flipped)); + ScaleOffset = lerp(Scale, Scale * ScaleOffset, UIHaloEnableExp); + + output.Position = float4(mad(output.Position.xy, ScaleOffset, SunPosition), 0.0, 1.0); + } + + return output; +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Halo Pixel Shader ////////////////////////////////////////////////////////////////// + +#ifdef HALO_PIXEL_SHADER + + +float4 main(HaloVertexOutput input) : SV_Target +{ + clip(input.SunInt.w - SUNCLIP); + + float Radial = length(input.TexCoord.xy); + float IDist = inv(UIHaloLength * 0.5); + float ODist = 1.0; + + float Ring = max(Radial - ODist, -(Radial - IDist)); + + clip(-Ring); + + float3 ColorShift = saturate(1.0 - abs(LinearStep(IDist, ODist, Radial) * 2 - float3(0, 1, 2))); + + float3 Color = lerp(UIHaloColor.xyz, ColorShift, UIHaloCrShift); + + float2 Polar = CoordMath::CartesianToPolar(input.TexCoord.xy); + float DeltaStep = radians(UIHaloIncr); + float HalfDelta = DeltaStep * 0.5; + + float DeltaMap = Math::ufmod(Polar.y + HalfDelta, DeltaStep) - HalfDelta; + + float Taper = min(Radial - IDist, ODist - Radial); + Taper = saturate(Taper / delta(UIHaloTaper * UIHaloLength * 2.0)); + + float RectifyMask = abs(Polar.x * sin(DeltaMap)); + + RectifyMask = inv(smoothstep(0.0, UIHaloWidth * Taper, RectifyMask)) * UIHaloInt; + RectifyMask *= LinearStep(0.4, 0.8, input.SunInt.x) * input.SunInt.z; + + return float4(Color * RectifyMask, 0.0); +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Lens Glare Vertex Shader /////////////////////////////////////////////////////////// + +#ifdef LENSGLARE_VERTEX_SHADER + +Texture2D OcclusionLUT : register(t0); + + +LensGlareVertexOutput main(VertexShaderInput input) +{ + LensGlareVertexOutput output; + output.TexCoord = input.TexCoord.xyxy * 2.0 - 1.0; + + float2 SunPosition = SunPositionUV.xy * 2.0 - 1.0; + + float angle = lerp(0.0, UIGlareMaxRot, -SunPosition.x) + 180.0; + float2x2 GlareRotMat = DegreesToVector(angle).xyyx * float2(1.0, -1.0).xyxx; + + float2 GlarePos = float2(UIGlareXOffset, UIGlareYOffset) * 2.0 - 1.0; + + [branch] if(UIGlareDynXPos) { + float SunDist = length(SunPosition); + float RadialDelta = mad(SunDist, -0.25, 1.0); + float2 ClampPosition = lerp(float2(GlarePos.x, -1.0), -SunPosition, SunDist); + GlarePos.x = ClampPosition.x * rsqrt(delta(dot(ClampPosition, ClampPosition))) * RadialDelta * ScreenSize.z; + } + + GlarePos = lerp(GlarePos + UIGlareScale, GlarePos - UIGlareScale, GlarePos * 0.5 + 0.5); + + output.Position.xy = mul(GlareRotMat, input.Position.xy); + output.Position.xy = mad(output.Position.xy, UIGlareScale * float2(1.0, ScreenSize.z), GlarePos); + + output.SunInt = OcclusionLUT.Load(0); + output.SunColor = OcclusionLUT.Load(int3(1,0,0)).xyz; + + output.Position = float4(output.Position.xy, 0.0, 1.0); + + return output; +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Lens Glare Pixel Shader //////////////////////////////////////////////////////////// + +#ifdef LENSGLARE_PIXEL_SHADER + + +float4 main(LensGlareVertexOutput input) : SV_Target +{ + float2 Coords = input.TexCoord.xy; + float Mask = length(Coords) - 0.9; + float Mask2 = length(Coords - float2(0.0, UIGlareCutDepth)) - UIGlareRadius; + + clip(min(-max(Mask, -Mask2), input.SunInt.w - SUNCLIP)); + + float Glare = lerp(0.0, 0.6, abs(max(Mask, -Mask2))) * UIGlareInt; + + float GlareMask = inv(smoothstep(0.5, 1.0, distance(Coords.y, -UIGlareTipFade))); + GlareMask *= Glare * input.SunInt.w; + + return float4(float3(1.0, 1.0, 1.0) * GlareMask, 0.0); +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Chromatic Aberration /////////////////////////////////////////////////////////////// + +#ifdef CHROMATIC_ABERRATION_PIXEL_SHADER + +Texture2D Main : register(t1); +Texture2D MotionVector : register(t2); + + +float4 main(VertexShaderOutput input) : SV_Target +{ + float4 Aberration = Main.Load(int3(input.Position.xy, 0)); + + float2 Offset = UICAIntensity * (1.0 + 5 * (UICAThreshold == 0.0)); + if(UICAThreshold > 0.0){ + float2 Motion = MotionVector.Load(int3(input.Position.xy, 0)).xy - 1e-6; + Offset *= saturate(length(Motion) - UICAThreshold); + Offset = min(UICAMaxOffset.xx, Offset) * ScreenSize.xy * normalize(Motion); + } + + [branch] if(UICAOnlyOffsetRed){ + Aberration.x = Main.Load(int3(clamp(int2(input.Position.xy) + int2(-Offset), int2(0,0), int2(ScreenSize.xy - 1)), 0)).x; + }else{ + [unroll] for(int i=0; i<3; ++i){ + int2 Coords = int2(input.Position.xy) + int2(Offset * (i-1)); + Aberration[i] = Main.Load(int3(clamp(Coords, int2(0,0), int2(ScreenSize.xy - 1)), 0))[i]; + } + } + + return Aberration; +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Sun Glare Vertex Shader //////////////////////////////////////////////////////////// + +#ifdef SUNGLARE_VERTEX_SHADER + +Texture2D OcclusionLUT : register(t0); + + +SunGlareVertexOutput main(VertexShaderInput input) +{ + SunGlareVertexOutput output; + output.TexCoord = input.TexCoord.xyxy; + output.TexCoord.zw = output.TexCoord.xy * 2.0 - 1.0; + + output.Position.xy = mad(input.Position.xy * float2(1.0, ScreenSize.z), UISunGlareScale, SunPositionUV.xy * 2.0 - 1.0); + output.Position = float4(output.Position.xy, 0.0, 1.0); + + output.SunInt = OcclusionLUT.Load(0); + + return output; +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Sun Glare Pixel Shader ///////////////////////////////////////////////////////////// + +#ifdef SUNGLARE_PIXEL_SHADER + +Texture2D DepthTexture : register(t0); +Texture2D SceneTexture : register(t1); + + +float4 main(SunGlareVertexOutput input) : SV_Target +{ + float Dist = length(input.TexCoord.zw); + float InvDist = inv(Dist); + clip(InvDist); + + float Intensity = UISunGlareInt; + + float sigma = max(0.01, Intensity); + float Glow = exp(-(Dist * Dist) / (2.0 * sigma * sigma)); + Glow *= pow(saturate(InvDist), Intensity) * Intensity * 3.0; + + float2 SampleCoords = FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(input.Position.xy / ScreenSize.xy); + + float Depth = DepthTexture.SampleCmpLevelZero(Depth_Sampler, SampleCoords, 1).x; + Depth = saturate(Depth + input.SunInt.x); + + float Mask = Glow * smoothstep(0.0, UISunGlareFade, InvDist); + + float3 Color = UISunGlareColor.xyz * Mask * Depth * input.SunInt.y; + + return float4(Color, 1.0); +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Ice Vertex Shader ////////////////////////////////////////////////////////////////// + +#ifdef ICE_VERTEX_SHADER + + +IceVertexOutput main(VertexShaderInput input) +{ + IceVertexOutput output; + + output.TexCoord.xy = input.TexCoord.xy; + output.TexCoord.zw = input.TexCoord * 2.0 - 1.0; + + output.Color = UIFrostColor.xyz * UIFrostInt; + + output.Position = float4(input.Position.xy, 0.0, 1.0); + + return output; +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Ice Pixel Shader /////////////////////////////////////////////////////////////////// + +#ifdef ICE_PIXEL_SHADER + +Texture2D IceTexture : register(t0); +Texture2D Main : register(t1); +Texture2D MotionVector : register(t2); + + +float4 main(IceVertexOutput input) : SV_Target +{ + float Dist = length(input.TexCoord.zw); + + float3 Texture = IceTexture.Sample(Point_Sampler, input.TexCoord.xy).xyz; + + float FadeFactor = LinearStep(inv(SnowPrecipValue), 1.0, saturate(Dist-0.35)); + FadeFactor *= LinearStep(0.0, 0.1, SnowPrecipValue); + + float3 Color = Texture * input.Color * FadeFactor; + + return float4(Color, 0.0); +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + +//// Bypass VS ////////////////////////////////////////////////////////////////////////// + +#ifdef BYPASS_VERTEX_SHADER + + +VertexShaderOutput main(VertexShaderInput input) +{ + VertexShaderOutput output; + output.TexCoord = input.TexCoord; + output.Position = float4(input.Position.xy, 0.0, 1.0); + + return output; +} +#endif +///////////////////////////////////////////////////////////////////////////////////////// + + + diff --git a/features/Lens Effects/Shaders/LensEffects/Textures/Atlas.dds b/features/Lens Effects/Shaders/LensEffects/Textures/Atlas.dds new file mode 100644 index 0000000000..cbf8556b03 Binary files /dev/null and b/features/Lens Effects/Shaders/LensEffects/Textures/Atlas.dds differ diff --git a/features/Lens Effects/Shaders/LensEffects/Textures/Frost.dds b/features/Lens Effects/Shaders/LensEffects/Textures/Frost.dds new file mode 100644 index 0000000000..ab493c79aa Binary files /dev/null and b/features/Lens Effects/Shaders/LensEffects/Textures/Frost.dds differ diff --git a/package/Shaders/Common/Color.hlsli b/package/Shaders/Common/Color.hlsli index c7770044fc..23aed984cb 100644 --- a/package/Shaders/Common/Color.hlsli +++ b/package/Shaders/Common/Color.hlsli @@ -3,6 +3,10 @@ #include "Common/Math.hlsli" +#define LUM_601 float3(0.299, 0.587, 0.114) +#define LUM_709 float3(0.212, 0.715, 0.072) +#define LUM_202 float3(0.262, 0.678, 0.059) + namespace Color { static float GammaCorrectionValue = 2.2; @@ -37,6 +41,11 @@ namespace Color return dot(color, float3(0.299, 0.587, 0.114)); } + float RGBToChrominance(float3 color) + { + return Math::max3(color.rgb) - Math::min3(color.rgb); + } + float3 RGBToYCoCg(float3 color) { float tmp = 0.25 * (color.r + color.b); @@ -56,6 +65,22 @@ namespace Color tmp - color.y); } + float3 RGBtoHSV(float3 c) + { + float4 K = float4(0.0, -1.0/3.0, 2.0/3.0, -1.0); + float4 p = (c.g < c.b) ? float4(c.b, c.g, K.w, K.z) : float4(c.g, c.b, K.x, K.y); + float4 q = (c.r < p.x) ? float4(p.x, p.y, p.z, c.r) : float4(c.r, p.y, p.z, p.x); + float d = q.x - min(q.w, q.y); float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); + } + + float3 HSVtoRGB(float3 c) + { + float4 K = float4(1.0, 2.0/3.0, 1.0/3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); + } + float3 Saturation(float3 color, float saturation) { float grey = RGBToLuminance(color); diff --git a/package/Shaders/Common/CoordMath.hlsli b/package/Shaders/Common/CoordMath.hlsli new file mode 100644 index 0000000000..2f963eb500 --- /dev/null +++ b/package/Shaders/Common/CoordMath.hlsli @@ -0,0 +1,37 @@ +#ifndef __COORDMATH_DEPENDENCY_HLSL__ +#define __COORDMATH_DEPENDENCY_HLSL__ + +namespace CoordMath +{ + float2 DegreesToVector(float degrees) {float2 outV; sincos(radians(degrees), outV.y, outV.x); return outV;} + float4 DegreesToVector(float2 degrees) {float4 outV; sincos(radians(degrees), outV.yw, outV.xz); return outV;} + + float ChebyshevDistance(float2 Coords) {return max(abs(Coords.x), abs(Coords.y));} + + float2 CartesianToPolar(float2 coords){ + float2 output = {length(coords), atan2(coords.y, coords.x)}; + output.y = (output.x == 0.0) ? 0.0 : output.y; + return output;} + + float2 PolarToCartesian(float2 polar){ + float2 coords; sincos(polar.y, coords.y, coords.x); + return coords * polar.x;} + + float2 AtlasFetch2x2(float2 coords, uint texNum){ + texNum = clamp(texNum, 1, 4); + static const float2 pos[4] = { + float2(0.0, 0.0), float2(0.5, 0.0), + float2(0.0, 0.5), float2(0.5, 0.5)}; + return mad(coords, 0.5, pos[texNum-1]);} + + float2 AtlasFetch1x4(float2 coords, uint texNum){ + texNum = clamp(texNum, 1, 4); + static const float2 pos[4] = { + float2(0.0, 0.00), float2(0.0, 0.25), + float2(0.0, 0.50), float2(0.0, 0.75)}; + return mad(coords, float2(1.0, 0.25), pos[texNum-1]);} + + bool InsideRect(float2 p, float2 rectMin, float2 rectMax){ + return all(step(rectMin, p) * step(p, rectMax)); } +} +#endif \ No newline at end of file diff --git a/package/Shaders/Common/Math.hlsli b/package/Shaders/Common/Math.hlsli index 8e7620578b..c0b697c36f 100644 --- a/package/Shaders/Common/Math.hlsli +++ b/package/Shaders/Common/Math.hlsli @@ -17,6 +17,58 @@ namespace Math static const float PI = 3.1415926535897932384626433832795f; // PI static const float HALF_PI = PI * 0.5f; // PI / 2 static const float TAU = PI * 2.0f; // PI * 2 + + float min2(float2 mn) {return min(mn.x, mn.y);} + float min3(float3 mn) {return min(mn.x, min2(mn.yz));} + float min4(float4 mn) {return min(mn.x, min3(mn.yzw));} + + float max2(float2 mx) {return max(mx.x, mx.y);} + float max3(float3 mx) {return max(mx.x, max2(mx.yz));} + float max4(float4 mx) {return max(mx.x, max3(mx.yzw));} + + float sum3(float3 v) {return v.x + v.y + v.z;} + float sum4(float4 v) {return v.x + v.y + v.z + v.w;} + + float ufmod(float x, float y) {return x - y * floor(x / max(y, EPSILON_DIVISION));} + float2 ufmod(float2 x, float2 y) {return x - y * floor(x / max(y, EPSILON_DIVISION));} + + float nRoot(float x, float n) {return (n == 0) ? 1.0 : pow(max(0, x), rcp(n));} + + float2 sincos2(float In){ float2 V; sincos(In, V.x, V.y); return V;} + + float LinearStep(float edge0, float edge1, float x){ + return saturate((x - edge0) / max((edge1 - edge0), EPSILON_DIVISION));} + + float2 LinearStep(float2 edge0, float2 edge1, float2 x){ + return saturate((x - edge0) / max((edge1 - edge0), EPSILON_DIVISION));} + + float3 LinearStep(float3 edge0, float3 edge1, float3 x){ + return saturate((x - edge0) / max((edge1 - edge0), EPSILON_DIVISION));} + + float MapRange(float x, float oldMin, float oldMax, float newMin, float newMax){ + return newMin + ((x - oldMin) / max(oldMax - oldMin, EPSILON_DIVISION)) * (newMax - newMin);} + + float smootherstep(float edge0, float edge1, float x){ + float t = LinearStep(edge0, edge1, x); + return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);} + + float2 smootherstep(float2 edge0, float2 edge1, float2 x){ + float2 t = LinearStep(edge0, edge1, x); + return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);} + + float3 smootherstep(float3 edge0, float3 edge1, float3 x){ + float3 t = LinearStep(edge0, edge1, x); + return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);} + + float Diffraction(float x, float Frequency, float Phase, float Amplitude){ + float sinc = PI * (x * Frequency + Phase); + sinc = sin(sinc) / max(sinc, EPSILON_DIVISION); + return sinc * sinc * Amplitude;} + + float3 Diffraction(float3 x, float Frequency, float Phase, float Amplitude){ + float3 sinc = PI * (x * Frequency + Phase); + sinc = sin(sinc) / max(sinc, EPSILON_DIVISION); + return sinc * sinc * Amplitude;} } #endif //__MATH_DEPENDENCY_HLSL__ \ No newline at end of file diff --git a/package/Shaders/Common/Random.hlsli b/package/Shaders/Common/Random.hlsli index ed79724cb8..2a4822a9c6 100644 --- a/package/Shaders/Common/Random.hlsli +++ b/package/Shaders/Common/Random.hlsli @@ -35,6 +35,11 @@ namespace Random { 0.07000602455182418f, -0.9868660954557704f }, }; + float RandomSH(float seed){ + return frac(sin(seed * 12.9898) * 43758.5453);} + float RandomSH(float2 seed){ + return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);} + /////////////////////////////////////////////////////////// // WHITE-LIKE HASHES /////////////////////////////////////////////////////////// diff --git a/package/Shaders/ISSAOComposite.hlsl b/package/Shaders/ISSAOComposite.hlsl index 7936864489..793cad453f 100644 --- a/package/Shaders/ISSAOComposite.hlsl +++ b/package/Shaders/ISSAOComposite.hlsl @@ -136,6 +136,7 @@ PS_OUTPUT main(PS_INPUT input) float2 screenPosition = FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(input.TexCoord); float ao = SAOTex.Sample(SAOSampler, screenPosition).x; float4 sourceColor = sourceTex.SampleLevel(sourceSampler, screenPosition, 0); + sourceColor.x = ((asuint(sourceColor.x) & 0x7fffffff) > 0x7f800000) ? 0.0 : sourceColor.x; float4 composedColor = sourceColor; diff --git a/package/Shaders/Sky.hlsl b/package/Shaders/Sky.hlsl index 3e8189d8e0..95e33ad6bc 100644 --- a/package/Shaders/Sky.hlsl +++ b/package/Shaders/Sky.hlsl @@ -168,6 +168,15 @@ Texture2D TexBaseSampler : register(t0); Texture2D TexBlendSampler : register(t1); Texture2D TexNoiseGradSampler : register(t2); +#if defined(LENS_EFFECTS) && defined(DEFERRED) + #if defined(CLOUDS) + RWTexture2D LensEffectsAT : register(u7); + Texture2D LensEffects : register(t30); + #else + RWTexture2D LensEffects : register(u7); + #endif +#endif + cbuffer PerGeometry : register(b2) { float2 PParams : packoffset(c0); @@ -253,9 +262,21 @@ PS_OUTPUT main(PS_INPUT input) float depth = TexDepthSampler.Load(int3(input.Position.xy, 0)); if (depth < input.Position.z) psout.Color.w = 0; - # endif + #if defined(LENS_EFFECTS) && defined(DEFERRED) + #if defined(DITHER) + float4 SunParams = LensEffects.Load(int2(3,0)); + if(input.Position.y == SunParams.y) + LensEffects[int2(2,0)] = psout.Color; + #elif defined(CLOUDS) + float4 SunParams = LensEffects.Load(int3(3,0,0)); uint Out; + if(psout.Color.w > 0.5 && length(input.Position.xy - SunParams.xy) - SunParams.w <= 0.0) + InterlockedAdd(LensEffectsAT[int2(0,0)], 1, Out); + #endif + #endif + return psout; } #endif + diff --git a/src/Feature.cpp b/src/Feature.cpp index e3facbed1d..651322a7a9 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -13,6 +13,7 @@ #include "Features/InteriorSun.h" #include "Features/InverseSquareLighting.h" #include "Features/LODBlending.h" +#include "Features/LensEffects.h" #include "Features/LightLimitFix.h" #include "Features/PerformanceOverlay.h" #include "Features/RenderDoc.h" @@ -227,7 +228,8 @@ const std::vector& Feature::GetFeatureList() &globals::features::ibl, &globals::features::extendedTranslucency, &globals::features::upscaling, - &globals::features::renderDoc + &globals::features::renderDoc, + &globals::features::lensEffects }; if (REL::Module::IsVR()) { diff --git a/src/Features/LensEffects.cpp b/src/Features/LensEffects.cpp new file mode 100644 index 0000000000..aee28984d0 --- /dev/null +++ b/src/Features/LensEffects.cpp @@ -0,0 +1,1024 @@ +#include "LensEffects.h" +#include "State.h" +#include "Upscaling.h" +#include "Upscaling/DX12SwapChain.h" +#include "Util.h" +#include +#include +#include + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( + LensEffects::MainSettings, + SB_Intensity, SB_Scale, SB_EnableBlades, + SB_BladeInt, SB_BladeVertices, SB_BladeSplay, + SB_BladeRotation, SB_BladeLength, SB_BladeBaseWidth, + SB_BladeWidth, SB_BladeTaper, SB_BladeFeather, + SB_EnableRays, SB_RandomRaysInt, SB_RandomRaysVolume, + SB_RandomRaysLength, SB_RandomRaysWidth, + SG_Scale, SG_Intensity, SG_OuterInt, SG_OuterFade, + GH_Intensity, GH_Scale, GH_Saturation, LI_Intensity, + GH_EnableClampOffset, GH_ClampOffset, + GL_Intensity, GL_Scale, GL_DynPosition, GL_XAxisOffset, GL_YAxisOffset, + GL_MaxRotation, GL_CutDepth, GL_Radius, GL_TipFade, + HL_Scale, HL_Intensity, HL_EnableExp, + HL_FlipExpOffset, HL_ExpMinSize, HL_ExpMaxSize, + HL_RotationSpeed, HL_LineVolume, HL_LineLength, + HL_LineWidth, HL_LineTaper, HL_ColorShift, + CA_Intensity, CA_Threshold, CA_MaxOffset, CA_RChannelOnly) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( + LensEffects::ColdSettings, + GH_Params, GH_Params_2, GH_Color, GH_Atlas, + SB_Color, HL_Color, LI_Color, LI_FadeIn, + LI_FadeOut, LI_FadeDuration) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( + LensEffects::Settings, + mainsettings, coldsettings, EnableStarburst, + EnableLensGlare, EnableHalo, EnableGhosts, + EnableIce, EnableSunGlare, EnableCA) + +void LensEffects::CompileShaders() +{ + SunOcclusionMaskPixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "OCCLUSION_PIXEL_SHADER", "" } }, "ps_5_0"); + ChromaticAberrationPixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "CHROMATIC_ABERRATION_PIXEL_SHADER", "" } }, "ps_5_0"); + BypassVertexShader = (ID3D11VertexShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "BYPASS_VERTEX_SHADER", "" } }, "vs_5_0"); + + BurstVertexShader = (ID3D11VertexShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "STARBURST_VERTEX_SHADER", "" } }, "vs_5_0"); + BurstPixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "STARBURST_PIXEL_SHADER", "" } }, "ps_5_0"); + + GhostVertexShader = (ID3D11VertexShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "GHOST_VERTEX_SHADER", "" } }, "vs_5_0"); + GhostPixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "GHOST_PIXEL_SHADER", "" } }, "ps_5_0"); + + SunGlareVertexShader = (ID3D11VertexShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "SUNGLARE_VERTEX_SHADER", "" } }, "vs_5_0"); + SunGlarePixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "SUNGLARE_PIXEL_SHADER", "" } }, "ps_5_0"); + + HaloVertexShader = (ID3D11VertexShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "HALO_VERTEX_SHADER", "" } }, "vs_5_0"); + HaloPixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "HALO_PIXEL_SHADER", "" } }, "ps_5_0"); + + LensGlareVertexShader = (ID3D11VertexShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "LENSGLARE_VERTEX_SHADER", "" } }, "vs_5_0"); + LensGlarePixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "LENSGLARE_PIXEL_SHADER", "" } }, "ps_5_0"); + + IceVertexShader = (ID3D11VertexShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "ICE_VERTEX_SHADER", "" } }, "vs_5_0"); + IcePixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\LensEffects\\LensEffects.hlsl", { { "ICE_PIXEL_SHADER", "" } }, "ps_5_0"); +} + +void LensEffects::SetupResources() +{ + auto device = globals::d3d::device; + + D3D11_SAMPLER_DESC linearSamplerDesc{}; + linearSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + linearSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + linearSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + linearSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + linearSamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + linearSamplerDesc.MinLOD = 0; + linearSamplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + + D3D11_SAMPLER_DESC pointSamplerDesc{ linearSamplerDesc }; + pointSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + + D3D11_SAMPLER_DESC depthSamplerDesc{ linearSamplerDesc }; + depthSamplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; + depthSamplerDesc.ComparisonFunc = D3D11_COMPARISON_EQUAL; + + DX::ThrowIfFailed(device->CreateSamplerState(&linearSamplerDesc, &LinearSampler)); + DX::ThrowIfFailed(device->CreateSamplerState(&pointSamplerDesc, &PointSampler)); + DX::ThrowIfFailed(device->CreateSamplerState(&depthSamplerDesc, &DepthSampler)); + + CD3D11_BLEND_DESC blendDesc(D3D11_DEFAULT); + blendDesc.RenderTarget[0].BlendEnable = TRUE; + + DX::ThrowIfFailed(device->CreateBlendState(&blendDesc, &BlendState[0])); + blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; + DX::ThrowIfFailed(device->CreateBlendState(&blendDesc, &BlendState[1])); + + viewport.TopLeftX = 0.0f; + viewport.TopLeftY = 0.0f; + viewport.Width = 16.0f; + viewport.Height = 1.0f; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + + D3D11_TEXTURE2D_DESC OcclusionTexDesc{}; + OcclusionTexDesc.Width = 16; + OcclusionTexDesc.Height = 1; + OcclusionTexDesc.MipLevels = 1; + OcclusionTexDesc.ArraySize = 1; + OcclusionTexDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + OcclusionTexDesc.Usage = D3D11_USAGE_DEFAULT; + OcclusionTexDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; + OcclusionTexDesc.SampleDesc.Count = 1; + OcclusionTexDesc.SampleDesc.Quality = 0; + OcclusionTexDesc.CPUAccessFlags = 0; + OcclusionTexDesc.MiscFlags = 0; + + D3D11_TEXTURE2D_DESC OcclusionTexDescAT{ OcclusionTexDesc }; + OcclusionTexDescAT.Format = DXGI_FORMAT_R16_UINT; + + D3D11_UNORDERED_ACCESS_VIEW_DESC OcclusionUAVDesc{}; + OcclusionUAVDesc.Format = OcclusionTexDesc.Format; + OcclusionUAVDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + OcclusionUAVDesc.Texture2D.MipSlice = 0; + + D3D11_UNORDERED_ACCESS_VIEW_DESC OcclusionUAVDescAT{ OcclusionUAVDesc }; + OcclusionUAVDescAT.Format = DXGI_FORMAT_R16_UINT; + + DX::ThrowIfFailed(device->CreateTexture2D(&OcclusionTexDesc, nullptr, &SunOcclusionLUT)); + DX::ThrowIfFailed(device->CreateUnorderedAccessView(SunOcclusionLUT, &OcclusionUAVDesc, &SunOcclusionUAV)); + DX::ThrowIfFailed(device->CreateShaderResourceView(SunOcclusionLUT, nullptr, &SunOcclusionSRV)); + + DX::ThrowIfFailed(device->CreateTexture2D(&OcclusionTexDescAT, nullptr, &SunOcclusionLUT_AT)); + DX::ThrowIfFailed(device->CreateUnorderedAccessView(SunOcclusionLUT_AT, &OcclusionUAVDescAT, &SunOcclusionUAV_AT)); + + DX::ThrowIfFailed(DirectX::CreateDDSTextureFromFile(device, L"Data\\Shaders\\LensEffects\\Textures\\Atlas.dds", nullptr, &AtlasTexSRV)); + DX::ThrowIfFailed(DirectX::CreateDDSTextureFromFile(device, L"Data\\Shaders\\LensEffects\\Textures\\Frost.dds", nullptr, &IceTexSRV)); + + SettingsCB = new ConstantBuffer(ConstantBufferDesc()); + + std::sort(weatherDisablesSun.begin(), weatherDisablesSun.end()); + std::sort(weatherDisablesSnow.begin(), weatherDisablesSnow.end()); + + skyrim_FlareData = reinterpret_cast(REL::RelocationID(527915, 414867).address()); + skyrim_RunFlarePtr = reinterpret_cast(REL::RelocationID(527916, 414862).address()); + + skyrim_SunPosition = reinterpret_cast(REL::RelocationID(527924, 414871).address()); + skyrim_SunGlareScale = reinterpret_cast(REL::RelocationID(502611, 370235).address()); + + gFlareApplyFunc = reinterpret_cast(REL::RelocationID(100281, 106995).address()); + + renderdata = new Setup::LF_RenderData; + + renderdata->AddEffect(Shaders::LensCA, settings.EnableCA, 1, { .uncond = true }); + renderdata->AddEffect(Shaders::LensIce, settings.EnableIce, 1, { .weather = true, .deferred = true }); + renderdata->AddEffect(Shaders::LensBurst, settings.EnableStarburst, 1); + renderdata->AddEffect(Shaders::LensSunGlare, settings.EnableSunGlare, 1); + renderdata->AddEffect(Shaders::LensGlare, settings.EnableLensGlare, 1); + renderdata->AddEffect(Shaders::LensHalo, settings.EnableHalo, 1); + renderdata->AddEffect(Shaders::LensGhosts, settings.EnableGhosts, ghostpasses); + + renderdata->SetRenderData(); + + CompileShaders(); +} + +void LensEffects::CheckOverride() +{ + static Util::FrameChecker frame_checker; + + if (overrideShader) { + if (frame_checker.IsNewFrame()) { + SettingsCB->Update(UpdateBufferValues()); + UpdateWeatherBasedDisable(); + frameIdx = (frameIdx < 16) ? ++frameIdx : 5; + } + LookupShader(shaderdesc); + } +} + +void LensEffects::LookupShader(int desc) +{ + static const std::unordered_map effects{ + { Shaders::Bypass, &LensEffects::BypassShader }, + { Shaders::AttachOcclusionLUT, &LensEffects::AppendOcclusionLUT }, + { Shaders::OcclusionLUT, &LensEffects::SetupOcclusionMask }, + { Shaders::LensIce, &LensEffects::SetupIceEffect }, + { Shaders::LensBurst, &LensEffects::SetupBurstEffect }, + { Shaders::LensSunGlare, &LensEffects::SetupSunGlareEffect }, + { Shaders::LensGlare, &LensEffects::SetupLensGlareEffect }, + { Shaders::LensHalo, &LensEffects::SetupHaloEffect }, + { Shaders::LensGhosts, &LensEffects::SetupGhostEffect }, + { Shaders::LensCA, &LensEffects::SetupCAEffect }, + }; + auto it = effects.find(desc); + if (it != effects.cend()) + (this->*(it->second))(); +} + +LensEffects::ConstBuffer LensEffects::UpdateBufferValues() +{ + float2 screenSize = Util::ConvertToDynamic(globals::state->screenSize); + float4 screenParams = { screenSize.x, screenSize.y, screenSize.x / screenSize.y, 0.0f }; + ConstBuffer data{}; + data.screensize = VectorToXMFloat(screenParams); + data.frame = frameIdx; + data.precip = GetWeatherPrecip(); + data.sunFXFade = weatherFadeout; + data.SunParams = GetSunPosition(); + data.suncolor = GetSunColor(); + data.shadersettings = UpdateSettings(); + + return data; +} + +void LensEffects::SetupOcclusionMask() +{ + auto context = globals::d3d::context; + auto renderer = globals::game::renderer; + auto buffer = SettingsCB->CB(); + + auto& mainTex = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].texture; + auto& mainTexCopy = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].textureCopy; + context->CopyResource(mainTexCopy, mainTex); + + auto& mainSRV = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].SRV; + auto& motionVectorSRV = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR].SRV; + + ID3D11UnorderedAccessView* OcclusionLUTs[2] = { SunOcclusionUAV, SunOcclusionUAV_AT }; + context->OMSetRenderTargetsAndUnorderedAccessViews(0, nullptr, nullptr, 0, 2, OcclusionLUTs, nullptr); + + context->RSSetViewports(1, &viewport); + + context->PSSetShader(SunOcclusionMaskPixelShader, NULL, NULL); + + context->PSSetConstantBuffers(1, 1, &buffer); + context->VSSetConstantBuffers(1, 1, &buffer); + + context->PSSetShaderResources(1, 1, &mainSRV); + context->PSSetShaderResources(2, 1, &motionVectorSRV); + + context->PSSetSamplers(10, 1, &LinearSampler); + context->PSSetSamplers(11, 1, &PointSampler); + context->PSSetSamplers(13, 1, &DepthSampler); + + if (!Raster) { + context->RSGetState(&Raster); + if (Raster) { + D3D11_RASTERIZER_DESC rastDesc = {}; + Raster->GetDesc(&rastDesc); + rastDesc.CullMode = D3D11_CULL_NONE; + DX::ThrowIfFailed(globals::d3d::device->CreateRasterizerState(&rastDesc, &Raster)); + } + } + + overrideShader = false; +} + +void LensEffects::SetupBurstEffect() +{ + auto context = globals::d3d::context; + + context->VSSetShader(BurstVertexShader, NULL, NULL); + context->PSSetShader(BurstPixelShader, NULL, NULL); + + context->VSSetShaderResources(0, 1, &SunOcclusionSRV); + + overrideShader = false; +} + +void LensEffects::SetupSunGlareEffect() +{ + auto context = globals::d3d::context; + + context->VSSetShader(SunGlareVertexShader, NULL, NULL); + context->PSSetShader(SunGlarePixelShader, NULL, NULL); + + auto& mainCopySRV = globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].SRVCopy; + context->VSSetShaderResources(0, 1, &SunOcclusionSRV); + context->PSSetShaderResources(1, 1, &mainCopySRV); + + overrideShader = false; +} + +void LensEffects::SetupLensGlareEffect() +{ + auto context = globals::d3d::context; + + context->VSSetShader(LensGlareVertexShader, NULL, NULL); + context->PSSetShader(LensGlarePixelShader, NULL, NULL); + + context->VSSetShaderResources(0, 1, &SunOcclusionSRV); + + overrideShader = false; +} + +void LensEffects::SetupHaloEffect() +{ + auto context = globals::d3d::context; + + context->VSSetShader(HaloVertexShader, NULL, NULL); + context->PSSetShader(HaloPixelShader, NULL, NULL); + + context->VSSetShaderResources(0, 1, &SunOcclusionSRV); + + overrideShader = false; +} + +void LensEffects::SetupGhostEffect() +{ + auto context = globals::d3d::context; + + auto it = renderdata->GetEffect(Shaders::LensGhosts).passesdone - 1; + + float4& params = settings.coldsettings.GH_Params[it]; + float4& params_2 = settings.coldsettings.GH_Params_2[it]; + + ConstBuffer data = UpdateBufferValues(); + data.shadersettings.GH_Size = params.x; + data.shadersettings.GH_Offset = params.y; + data.shadersettings.GH_Shape = params.z; + data.shadersettings.GH_Roundness = params.w; + data.shadersettings.GH_Rotation = params_2.x; + data.shadersettings.GH_Feather = params_2.y; + data.shadersettings.GH_CAScale = params_2.z; + data.shadersettings.GH_InnerInt = params_2.w; + data.shadersettings.GH_Color = VectorToXMFloat(settings.coldsettings.GH_Color[it]); + data.shadersettings.GH_Atlas = VectorToXMFloat(settings.coldsettings.GH_Atlas[it]); + + SettingsCB->Update(data); + + context->VSSetShader(GhostVertexShader, NULL, NULL); + context->PSSetShader(GhostPixelShader, NULL, NULL); + + context->VSSetShaderResources(0, 1, &SunOcclusionSRV); + context->PSSetShaderResources(1, 1, &AtlasTexSRV); + + overrideShader = false; +} + +void LensEffects::SetupCAEffect() +{ + auto context = globals::d3d::context; + auto renderer = globals::game::renderer; + + context->OMSetBlendState(BlendState[0], nullptr, 0xFFFFFFFF); + + context->VSSetShader(BypassVertexShader, NULL, NULL); + context->PSSetShader(ChromaticAberrationPixelShader, NULL, NULL); + + auto& mainCopy = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].SRVCopy; + auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR].SRV; + context->PSSetShaderResources(1, 1, &mainCopy); + context->PSSetShaderResources(2, 1, &motionVector); + + overrideShader = false; +} + +void LensEffects::SetupIceEffect() +{ + auto context = globals::d3d::context; + + if (auto& UIBuffer = globals::features::upscaling.dx12SwapChain.uiBufferWrapped) { + auto UIRTV = UIBuffer->rtv; + context->OMSetRenderTargets(1, &UIRTV, nullptr); + } + + context->RSSetState(Raster); + context->OMSetBlendState(BlendState[1], nullptr, 0xFFFFFFFF); + + auto& mainCopy = globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].SRVCopy; + + context->VSSetShader(IceVertexShader, NULL, NULL); + context->PSSetShader(IcePixelShader, NULL, NULL); + + context->PSSetShaderResources(0, 1, &IceTexSRV); + context->PSSetShaderResources(1, 1, &mainCopy); + + overrideShader = false; +} + +void LensEffects::AppendOcclusionLUT() +{ + auto context = globals::d3d::context; + + ID3D11RenderTargetView* rtvs[8] = {}; + ID3D11DepthStencilView* dsv = nullptr; + + context->OMGetRenderTargets(8, rtvs, &dsv); + + UINT numRTs = 0; + for (int i = 0; i < 8; ++i) + if (rtvs[i]) + numRTs++; + + if (numRTs < 8) { + if (!useCloudLUT) { + context->OMSetRenderTargetsAndUnorderedAccessViews(numRTs, rtvs, dsv, 7, 1, &SunOcclusionUAV, nullptr); + } else { + context->OMSetRenderTargetsAndUnorderedAccessViews(numRTs, rtvs, dsv, 7, 1, &SunOcclusionUAV_AT, nullptr); + context->PSSetShaderResources(30, 1, &SunOcclusionSRV); + useCloudLUT = false; + } + } else { + logger::error("[Lens Effects] failed to bind UAV"); + } + + for (int i = 0; i < 8; ++i) + if (rtvs[i]) + rtvs[i]->Release(); + if (dsv) + dsv->Release(); + + overrideShader = false; +} + +void LensEffects::BypassShader() +{ + auto context = globals::d3d::context; + + context->VSSetShader(BypassVertexShader, NULL, NULL); + context->PSSetShader(NULL, NULL, NULL); + + overrideShader = false; +} + +void LensEffects::GetWeatherShader() +{ + if (shaderdesc == Shaders::LensIce) { + if (auto sky = globals::game::sky) { + float lastUpdate = sky->lastWeatherUpdate / 24.0f; + static float transEpoch = lastUpdate; + static float epochDelta = 0; + static int weatherRole = 0; + static float fade; + static float* UIFadeSetting = nullptr; + + if (CheckWeatherChange()) { + transEpoch = lastUpdate; + weatherRole = false; + if (auto currWeather = sky->currentWeather; currWeather && currWeather->data.flags.any(RE::TESWeather::WeatherDataFlag::kSnow)) { + if (!std::binary_search(weatherDisablesSnow.begin(), weatherDisablesSnow.end(), WeatherID)) { + fade = std::max(sky->currentWeather->data.precipitationBeginFadeIn / 255.0f / 24.0f / 7.0f, 0.0018f); + UIFadeSetting = &settings.coldsettings.LI_FadeIn; + weatherRole = 1; + epochDelta = 0; + } + } else if (auto prevWeather = sky->lastWeather; prevWeather && prevWeather->data.flags.any(RE::TESWeather::WeatherDataFlag::kSnow)) { + if (!std::binary_search(weatherDisablesSnow.begin(), weatherDisablesSnow.end(), PrevWeatherID)) { + fade = std::max(sky->lastWeather->data.precipitationEndFadeOut / 255.0f / 24.0f / 7.0f, 0.00025f); + UIFadeSetting = &settings.coldsettings.LI_FadeOut; + weatherRole = 2; + epochDelta = snowPrecipValue; + } + } + } + + if (weatherRole) { + float time = RE::Calendar::GetSingleton()->GetCurrentGameTime(); + bool IsInside = RE::PlayerCharacter::GetSingleton()->GetParentCell()->IsInteriorCell(); + static bool lastIsInside = IsInside; + if (IsInside != lastIsInside) { + lastIsInside = IsInside; + epochDelta = snowPrecipValue; + transEpoch = time; + UIFadeSetting = (IsInside) ? &settings.coldsettings.LI_FadeOut : &settings.coldsettings.LI_FadeIn; + fade = 0; + } + float duration = settings.coldsettings.LI_FadeDuration / 24.0f / 4.0f; + duration = (IsInside) ? duration * 0.5f : duration; + + float UIFadeFactor = *UIFadeSetting / 24.0f / 4.0f; + float fadeValue = std::clamp((time - (transEpoch + (fade + UIFadeFactor))) / duration, 0.0f, 1.0f); + + if (!IsInside && weatherRole == 1) + snowPrecipValue = std::clamp(epochDelta + fadeValue, 0.0f, 1.0f); + else + snowPrecipValue = std::clamp(epochDelta - fadeValue, 0.0f, 1.0f); + } else { + snowPrecipValue = 0.0f; + shaderdesc = Shaders::Bypass; + } + } else { + shaderdesc = Shaders::Bypass; + } + } +} + +float LensEffects::GetWeatherPrecip() +{ + float precip = 0.0f; + if (auto sky = globals::game::sky; sky && sky->mode.get() == RE::Sky::Mode::kFull) { + if (auto prevweather = sky->lastWeather; prevweather && prevweather->precipitationData) + precip = 1.0f - sky->currentWeatherPct; + if (auto currweather = sky->currentWeather; currweather && currweather->precipitationData) + precip = (precip) ? 1.0f : sky->currentWeatherPct; + } + return precip; +} + +bool LensEffects::CheckWeatherChange() +{ + if (auto sky = globals::game::sky) { + if (!WeatherID || std::floor(sky->lastWeatherUpdate * 60) == std::floor(sky->currentGameHour * 60)) { + WeatherID = (sky->currentWeather) ? sky->currentWeather->formID : 0; + PrevWeatherID = (sky->lastWeather) ? sky->lastWeather->formID : 0; + return true; + } + } + return false; +} + +void LensEffects::UpdateWeatherBasedDisable() +{ + static bool currWeatherRole = false; + static bool prevWeatherRole = false; + + if (CheckWeatherChange()) { + disableSunFX = false; + weatherFadeout = 0.0f; + currWeatherRole = std::binary_search(weatherDisablesSun.begin(), weatherDisablesSun.end(), WeatherID); + prevWeatherRole = std::binary_search(weatherDisablesSun.begin(), weatherDisablesSun.end(), PrevWeatherID); + } + + if (auto sky = globals::game::sky; sky && (currWeatherRole || prevWeatherRole)) { + auto weatherTransition = LinearStep(0.3, 1.0, sky->currentWeatherPct); + if ((weatherTransition == 1.0f && currWeatherRole) || (currWeatherRole && prevWeatherRole)) + disableSunFX = true; + weatherFadeout = (currWeatherRole) ? weatherTransition : 1.0f - weatherTransition; + } +} + +DirectX::XMFLOAT4A LensEffects::GetSunPosition() +{ + static float sunRadius = 0.0f; + float2 screenSize = Util::ConvertToDynamic(globals::state->screenSize); + auto sunWSPos = *skyrim_SunPosition; + float4 sunWorldPos = { sunWSPos.x, sunWSPos.y, sunWSPos.z, 1.0f }; + + Matrix viewMatrix = Util::GetCameraData(0).viewMat; + Matrix projMatrix = Util::GetCameraData(0).projMat; + + float4 sunViewPos = float4::Transform(sunWorldPos, viewMatrix); + float SunSSRad = (*skyrim_SunGlareScale * SunScale) * (projMatrix._22 / sunViewPos.z) * (screenSize.y * 0.5f); + sunViewPos.w = sunRadius = (SunSSRad > 10.0f && SunSSRad < 100.0f) ? SunSSRad : sunRadius; + + return VectorToXMFloat(sunViewPos); +} + +DirectX::XMFLOAT4A LensEffects::GetSunColor() +{ + static float4 sunColor = { 1.0f, 1.0f, 1.0f, 1.0f }; + if (auto sky = globals::game::sky; sky && sky->sun && sky->sun->sunBase) + if (auto property = skyrim_cast(sky->sun->sunBase->GetGeometryRuntimeData().properties[1].get())) + sunColor = { property->kBlendColor.red, property->kBlendColor.green, property->kBlendColor.blue, 1.0f }; + return VectorToXMFloat(sunColor); +} + +void LensEffects::Hooks::BSSkyShader_SetupMaterial::thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags) +{ + auto& lens = globals::features::lensEffects; + auto skyProperty = reinterpret_cast(Pass->shaderProperty); + + if (skyProperty) { + if (skyProperty->uiSkyObjectType == RE::BSSkyShaderProperty::SkyObject::SO_SUN_GLARE) { + lens.overrideShader = true; + lens.shaderdesc = Shaders::Bypass; + } + + else if (skyProperty->uiSkyObjectType == RE::BSSkyShaderProperty::SkyObject::SO_ATMOSPHERE) { + if (Pass->passEnum == 0x5C00005E && RenderFlags == 65) { + lens.overrideShader = true; + lens.shaderdesc = Shaders::AttachOcclusionLUT; + } + } + + else if (skyProperty->uiSkyObjectType == RE::BSSkyShaderProperty::SkyObject::SO_CLOUDS) { + if ((Pass->passEnum == 0x5C000062 || Pass->passEnum == 0x5C000063 || Pass->passEnum == 0x5C000064) && RenderFlags == 65) { + lens.overrideShader = true; + lens.shaderdesc = Shaders::AttachOcclusionLUT; + lens.useCloudLUT = true; + } + } + + else if (skyProperty->uiSkyObjectType == RE::BSSkyShaderProperty::SkyObject::SO_SUN) { + if (lens.SunScale == 0.0f && Pass->passEnum == 0x5C000061 && (RenderFlags == 65 || RenderFlags == 8)) { + D3D11_TEXTURE2D_DESC sunDesc = {}; + skyProperty->pBaseTexture->rendererTexture->texture->GetDesc(&sunDesc); + lens.SunScale = 24.0f / sunDesc.Width; + } + } + } + func(This, Pass, RenderFlags); +} + +void LensEffects::Hooks::LensFlare_CheckResources::thunk() +{ + auto& lens = globals::features::lensEffects; + + lens.renderdata->CheckRefData(); + + if (!*lens.skyrim_FlareData) + *lens.skyrim_FlareData = reinterpret_cast(lens.renderdata); +} + +void LensEffects::Hooks::LensFlareVisibility_CheckRenderCondition::thunk(RE::NiCamera* camera, void* shader) +{ + auto& lens = globals::features::lensEffects; + + func(camera, shader); //set skyrim_RunFlarePtr + + lens.skyrim_SunVisble = static_cast(*lens.skyrim_RunFlarePtr); + + if (lens.disableSunFX) + lens.skyrim_SunVisble = false; + + if (*lens.skyrim_FlareData && !globals::game::ui->GameIsPaused()) { + lens.renderdata->CreateRenderList(lens.skyrim_SunVisble); + *lens.skyrim_RunFlarePtr = 1; + } else + *lens.skyrim_RunFlarePtr = 0; +} + +void LensEffects::Hooks::BSImagespaceShader_Render::thunk(void* shader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) +{ + auto& lens = globals::features::lensEffects; + + lens.overrideShader = true; + lens.shaderdesc = Shaders::OcclusionLUT; + + func(shader, shape, param); +} + +#pragma warning(push) +#pragma warning(disable: 4189) +bool LensEffects::Hooks::LensFlare_CheckRenderCondition::thunk(void* shader, RE::NiCamera* camera, uint64_t unk) +{ + auto& lens = globals::features::lensEffects; + + if (!lens.gFlareShader && shader) + lens.gFlareShader = shader; + + bool result = func(shader, camera, unk); + + return true; +} +#pragma warning(pop) + +void LensEffects::Hooks::BSImagespaceShader_Render::thunk(void* shader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) +{ + auto& lens = globals::features::lensEffects; + + lens.overrideShader = true; + lens.shaderdesc = lens.renderdata->UpdateCurrentEffect(); + + if (lens.renderdata->GetEffect(lens.shaderdesc).IsWeatherShader()) { + lens.GetWeatherShader(); + } + + func(shader, shape, param); +} + +void LensEffects::Hooks::Main_PostProcessing::thunk(RE::ImageSpaceManager* a1, uint32_t a3, uint32_t er8_) +{ + auto& lens = globals::features::lensEffects; + + if (*lens.skyrim_FlareData && lens.gFlareShader && !globals::game::ui->GameIsPaused()) { + lens.renderdata->CreateRenderList(lens.skyrim_SunVisble, true); + lens.gFlareApplyFunc(RE::Main::WorldRootCamera(), lens.gFlareShader, 0); + } + + func(a1, a3, er8_); +} + +void LensEffects::DrawSettings() +{ + auto& mainSettings = settings.mainsettings; + auto& coldSettings = settings.coldsettings; + + if (ImGui::BeginTabBar("Effects")) { + if (ImGui::BeginTabItem("General")) { + ImGui::SeparatorText("Quick Enable"); + if (ImGui::Checkbox("Enable Sun Glare", &settings.EnableSunGlare)) + renderdata->GetEffect(Shaders::LensSunGlare).Toggle(settings.EnableSunGlare); + if (ImGui::Checkbox("Enable Starburst", &settings.EnableStarburst)) + renderdata->GetEffect(Shaders::LensBurst).Toggle(settings.EnableStarburst); + if (ImGui::Checkbox("Enable Ghosts", &settings.EnableGhosts)) + renderdata->GetEffect(Shaders::LensGhosts).Toggle(settings.EnableGhosts); + if (ImGui::Checkbox("Enable Lens Glare", &settings.EnableLensGlare)) + renderdata->GetEffect(Shaders::LensGlare).Toggle(settings.EnableLensGlare); + if (ImGui::Checkbox("Enable Sun Halo", &settings.EnableHalo)) + renderdata->GetEffect(Shaders::LensHalo).Toggle(settings.EnableHalo); + if (ImGui::Checkbox("Enable Chromatic Aberration", &settings.EnableCA)) + renderdata->GetEffect(Shaders::LensCA).Toggle(settings.EnableCA); + if (ImGui::Checkbox("Enable Lens Ice", &settings.EnableIce)) + renderdata->GetEffect(Shaders::LensIce).Toggle(settings.EnableIce); + + ImGui::EndTabItem(); + } + + //// Lens Starburst //// + if (ImGui::BeginTabItem("Starburst Flare")) { + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Description")) { + ImGui::Text("Creates diffraction spikes radiating from the sun, mimicking light scattering off the lens aperture."); + ImGui::Text("Starburst has two adjustable styles: blades and rays. You can use either or both. By default blades are disabled."); + } + ImGui::SeparatorText("General"); + ImGui::Spacing(); + if (ImGui::Checkbox("Enable Starburst", &settings.EnableStarburst)) + renderdata->GetEffect(Shaders::LensBurst).Toggle(settings.EnableStarburst); + + ImGui::SliderFloat("Burst Size", &mainSettings.SB_Scale, 0.1f, 1.0f); + ImGui::SliderFloat("Burst Intensity", &mainSettings.SB_Intensity, 0.01f, 2.0f); + ImGui::Spacing(); + ImGui::Spacing(); + ImGui::SeparatorText("Advanced"); + ImGui::Spacing(); + ImGui::Checkbox("Enable Blades", (bool*)&mainSettings.SB_EnableBlades); + ImGui::SliderFloat("Blade Intensity", &mainSettings.SB_BladeInt, 0.0f, 1.0f); + ImGui::SliderFloat("Blade Points", &mainSettings.SB_BladeVertices, 2.0f, 15.0f, "%.0f"); + ImGui::SliderFloat("Blade Splay", &mainSettings.SB_BladeSplay, 0.0f, 1.0f); + ImGui::SliderFloat("Blade Rotation", &mainSettings.SB_BladeRotation, 0.0f, 360.0f, "%.0f"); + ImGui::SliderFloat("Blade Length", &mainSettings.SB_BladeLength, 0.4f, 0.9f); + ImGui::SliderFloat("Blade Base Width", &mainSettings.SB_BladeBaseWidth, 1.0f, 3.0f); + ImGui::SliderFloat("Blade Width", &mainSettings.SB_BladeWidth, 1.0f, 3.0f); + ImGui::SliderFloat("Blade Taper", &mainSettings.SB_BladeTaper, 1.0f, 10.0f); + ImGui::SliderFloat("Blade Sharpness", &mainSettings.SB_BladeFeather, 10.0f, 100.0f, "%.0f"); + ImGui::SliderFloat("Blade Fade Power", &mainSettings.SB_BladeFadePow, 0.0f, 1.0f); + ImGui::SliderFloat("Blade Fade Distance", &mainSettings.SB_BladeFadeDist, 0.0f, 10.0f); + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::Checkbox("Enable Rays", (bool*)&mainSettings.SB_EnableRays); + ImGui::SliderFloat("Rays Intensity", &mainSettings.SB_RandomRaysInt, 0.0f, 5.0f); + ImGui::SliderFloat("Rays Volume", &mainSettings.SB_RandomRaysVolume, 0.0f, 0.6f); + ImGui::SliderFloat("Rays Length", &mainSettings.SB_RandomRaysLength, 0.2f, 1.0f); + ImGui::SliderFloat("Rays Width", &mainSettings.SB_RandomRaysWidth, 0.0f, 1.0f); + if (auto _tt = Util::HoverTooltipWrapper()) + ImGui::Text("Changing this also changes the shape of the effect"); + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::Text("Color "); + ImGui::SameLine(); + float4& SBcolor = coldSettings.SB_Color; + if (ImGui::ColorButton("Pick", ImVec4(SBcolor.x, SBcolor.y, SBcolor.z, SBcolor.w))) + ImGui::OpenPopup("ColorPopup_"); + + if (ImGui::BeginPopup("ColorPopup_", ImGuiWindowFlags_NoMove)) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::GetFontSize() + ImGui::GetStyle().ItemInnerSpacing.x * 2); + if (ImGui::SmallButton("X")) + ImGui::CloseCurrentPopup(); + ImGui::ColorPicker4("RGBA", &SBcolor.x); + ImGui::EndPopup(); + } + ImGui::EndTabItem(); + } + + //// Lens Ghosts //// + if (ImGui::BeginTabItem("Lens Ghosting")) { + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Description")) { + ImGui::Text("Adds a procession of faint flares that move with the camera, simulating reflections bouncing inside the lens stack."); + ImGui::Text("By default only the first 10 flares are actively used."); + } + ImGui::SeparatorText("General"); + ImGui::Spacing(); + if (ImGui::Checkbox("Enable Ghosts", &settings.EnableGhosts)) + renderdata->GetEffect(Shaders::LensGhosts).Toggle(settings.EnableGhosts); + + ImGui::SliderFloat("Ghost Size", &mainSettings.GH_Scale, 0.1f, 1.5f); + ImGui::SliderFloat("Ghost Intensity", &mainSettings.GH_Intensity, 0.0f, 2.0f); + ImGui::SliderFloat("Ghost Saturation", &mainSettings.GH_Saturation, 0.0f, 1.0f); + ImGui::Spacing(); + ImGui::Spacing(); + ImGui::SeparatorText("Advanced"); + ImGui::Spacing(); + ImGui::Checkbox("Ghost Enable Clamp", (bool*)&mainSettings.GH_EnableClampOffset); + ImGui::SliderFloat("Ghost Clamp Offset", &mainSettings.GH_ClampOffset, -1.0f, 1.0f); + ImGui::Spacing(); + ImGui::Spacing(); + for (int i = 0; i < ghostpasses; i++) { + if (ImGui::TreeNodeEx(("------------------------ Ghost #" + std::to_string(i + 1) + " ------------------------").c_str())) { + float4& params = coldSettings.GH_Params[i]; + float4& params_2 = coldSettings.GH_Params_2[i]; + float4& color = coldSettings.GH_Color[i]; + float4& atlas = coldSettings.GH_Atlas[i]; + ImGui::PushID(i); + + ImGui::Text("General"); + ImGui::SliderFloat("Position", ¶ms.y, -1.0f, 1.0f); + ImGui::SliderFloat("Size", ¶ms.x, 0.02f, 1.0f); + ImGui::SliderFloat("Shape", ¶ms.z, 3.0f, 9.0f, "%.0f"); + ImGui::SliderFloat("Roundness", ¶ms.w, 0.0f, 1.0f); + ImGui::SliderFloat("Rotation", ¶ms_2.x, 0.0f, 360.0f, "%.0f"); + ImGui::SliderFloat("Intensity", &color.w, 0.1f, 2.0f); + ImGui::SliderFloat("Inner Intensity", ¶ms_2.w, 0.0f, 1.0f); + ImGui::SliderFloat("Edge Feather", ¶ms_2.y, 0.0f, 1.0f); + ImGui::SliderFloat("CA Scale", ¶ms_2.z, 1.0f, 2.0f); + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::Text("Atlas"); + ImGui::SliderFloat("Texture", &atlas.x, 1.0f, 4.0f, "%.0f"); + ImGui::SliderFloat("Visibility", &atlas.y, 0.0f, 1.0f); + ImGui::SliderFloat("Scale", &atlas.z, 0.5f, 1.0f); + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::Text("Color "); + ImGui::SameLine(); + if (ImGui::ColorButton("Pick", ImVec4(color.x, color.y, color.z, color.w))) + ImGui::OpenPopup("ColorPopup_"); + + if (ImGui::BeginPopup("ColorPopup_", ImGuiWindowFlags_NoMove)) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::GetFontSize() + ImGui::GetStyle().ItemInnerSpacing.x * 2); + if (ImGui::SmallButton("X")) + ImGui::CloseCurrentPopup(); + ImGui::ColorPicker4("RGB", &color.x); + ImGui::EndPopup(); + } + ImGui::Text(""); + ImGui::PopID(); + ImGui::TreePop(); + } + } + ImGui::EndTabItem(); + } + + //// Lens Glare //// + if (ImGui::BeginTabItem("Lens Glare")) { + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Description")) { + ImGui::Text("A crescent shaped light leak that forms near the lower edge of the lens, decreasing contrast and giving a washed out look."); + ImGui::Text("By default glare position is static, when 'dynamic x' is enabled the horizontal position will track with the sun, and 'x axis offset' becomes a base offset."); + } + ImGui::SeparatorText("General"); + if (ImGui::Checkbox("Enable Lens Glare", &settings.EnableLensGlare)) + renderdata->GetEffect(Shaders::LensGlare).Toggle(settings.EnableLensGlare); + + ImGui::SliderFloat("Glare Scale", &mainSettings.GL_Scale, 0.1f, 1.0f); + ImGui::SliderFloat("Glare Intensity", &mainSettings.GL_Intensity, 0.1f, 2.0f); + ImGui::Spacing(); + ImGui::Spacing(); + ImGui::SeparatorText("Advanced"); + ImGui::Checkbox("Dynamic X", (bool*)&mainSettings.GL_DynPosition); + ImGui::SliderFloat("X Axis Offset", &mainSettings.GL_XAxisOffset, 0.0f, 1.0f); + ImGui::SliderFloat("Y Axis Offset", &mainSettings.GL_YAxisOffset, 0.0f, 1.0f); + ImGui::SliderFloat("Max Rotation", &mainSettings.GL_MaxRotation, 0.0f, 180.0f, "%.0f"); + ImGui::SliderFloat("Shape", &mainSettings.GL_CutDepth, 0.5f, 1.0f); + ImGui::SliderFloat("Radius", &mainSettings.GL_Radius, 0.5f, 1.0f); + ImGui::SliderFloat("Tip Fade", &mainSettings.GL_TipFade, 0.0f, 1.5f); + + ImGui::EndTabItem(); + } + + //// Lens Halo //// + if (ImGui::BeginTabItem("Lens Halo")) { + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Description")) { + ImGui::Text("Creates a faint segmented halo around the radius of the sun, which expands and contracts depending on the angle it's viewed from."); + } + ImGui::SeparatorText("General"); + if (ImGui::Checkbox("Enable Sun Halo", &settings.EnableHalo)) + renderdata->GetEffect(Shaders::LensHalo).Toggle(settings.EnableHalo); + + ImGui::SliderFloat("Halo Scale", &mainSettings.HL_Scale, 0.1f, 1.0f); + ImGui::SliderFloat("Halo Intensity", &mainSettings.HL_Intensity, 0.01f, 1.0f); + ImGui::Spacing(); + ImGui::Spacing(); + ImGui::SeparatorText("Advanced"); + ImGui::Checkbox("Enable Halo Expansion", (bool*)&mainSettings.HL_EnableExp); + ImGui::Checkbox("Flip Halo Expansion", (bool*)&mainSettings.HL_FlipExpOffset); + ImGui::SliderFloat("Min Expansion", &mainSettings.HL_ExpMinSize, 0.2f, 1.0f); + ImGui::SliderFloat("Max Expansion", &mainSettings.HL_ExpMaxSize, 0.2f, 1.0f); + ImGui::SliderFloat("Rotation Speed", &mainSettings.HL_RotationSpeed, 0.0f, 1.0f); + ImGui::SliderFloat("Rake Increment (degrees)", &mainSettings.HL_LineVolume, 1.0f, 45.0f, "%.0f"); + ImGui::SliderFloat("Length", &mainSettings.HL_LineLength, 0.05f, 0.8f); + ImGui::SliderFloat("Width", &mainSettings.HL_LineWidth, 0.03f, 0.5f); + ImGui::SliderFloat("Taper", &mainSettings.HL_LineTaper, 0.0f, 0.15f); + ImGui::SliderFloat("Chromatic Shift", &mainSettings.HL_ColorShift, 0.0f, 1.0f); + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::Text("Color "); + ImGui::SameLine(); + float4& Color = coldSettings.HL_Color; + if (ImGui::ColorButton("Pick", ImVec4(Color.x, Color.y, Color.z, Color.w))) + ImGui::OpenPopup("ColorPopup_"); + + if (ImGui::BeginPopup("ColorPopup_", ImGuiWindowFlags_NoMove)) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::GetFontSize() + ImGui::GetStyle().ItemInnerSpacing.x * 2); + if (ImGui::SmallButton("X")) + ImGui::CloseCurrentPopup(); + ImGui::ColorPicker4("RGBA", &Color.x); + ImGui::EndPopup(); + } + ImGui::EndTabItem(); + } + + //// Lens Ice //// + if (ImGui::BeginTabItem("Lens Frost")) { + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Description")) { + ImGui::Text("A vignette that slowly forms around the edge of the camera lens during snow based weathers."); + } + ImGui::SeparatorText("General"); + if (ImGui::Checkbox("Enable Lens Frost", &settings.EnableIce)) + renderdata->GetEffect(Shaders::LensIce).Toggle(settings.EnableIce); + + ImGui::SliderFloat("Frost Intensity", &mainSettings.LI_Intensity, 0.01f, 1.0f); + ImGui::SliderFloat("Fade Duration", &coldSettings.LI_FadeDuration, 0.01f, 2.0f); + ImGui::SliderFloat("Fade In Delay", &coldSettings.LI_FadeIn, 0.01f, 1.0f); + ImGui::SliderFloat("Fade Out Delay", &coldSettings.LI_FadeOut, 0.01f, 1.0f); + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::Text("Color "); + ImGui::SameLine(); + float4& Color = coldSettings.LI_Color; + if (ImGui::ColorButton("Pick", ImVec4(Color.x, Color.y, Color.z, Color.w))) + ImGui::OpenPopup("ColorPopup_"); + + if (ImGui::BeginPopup("ColorPopup_", ImGuiWindowFlags_NoMove)) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::GetFontSize() + ImGui::GetStyle().ItemInnerSpacing.x * 2); + if (ImGui::SmallButton("X")) + ImGui::CloseCurrentPopup(); + ImGui::ColorPicker4("RGBA", &Color.x); + ImGui::EndPopup(); + } + ImGui::EndTabItem(); + } + + //// Chromatic Aberration //// + if (ImGui::BeginTabItem("Chromatic Aberration")) { + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Description")) { + ImGui::Text("Chromatic Aberration splits the red, green and blue colour channels slightly apart which creates colored fringes on edges and mild image blur."); + ImGui::Text("By default the amount of chromatic aberration is dependant on camera motion. The motion clamp limits the maximum color offset under motion."); + ImGui::Text("Note: Setting low motion thresholds without also decreasing intensity/clamp will cause image warping/skipping."); + } + ImGui::SeparatorText("General"); + if (ImGui::Checkbox("Enable Chromatic Aberration", &settings.EnableCA)) + renderdata->GetEffect(Shaders::LensCA).Toggle(settings.EnableCA); + + ImGui::SliderFloat("CA Intensity", &mainSettings.CA_Intensity, 0.0f, 2.0f); + ImGui::SliderFloat("CA Motion Threshold", &mainSettings.CA_Threshold, 0.0f, 0.1f); + if (auto _tt = Util::HoverTooltipWrapper()) + ImGui::Text("Set to 0 for constant CA offset"); + ImGui::SliderFloat("CA Motion Clamp", &mainSettings.CA_MaxOffset, 0.001f, 0.015f); + ImGui::Checkbox("Offset Red Channel Only", (bool*)&mainSettings.CA_RChannelOnly); + if (auto _tt = Util::HoverTooltipWrapper()) + ImGui::Text("This will yield better performance"); + + ImGui::EndTabItem(); + } + + //// Sun Glare //// + if (ImGui::BeginTabItem("Sun Glare")) { + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Description")) { + ImGui::Text("Allows modification of sun glare."); + ImGui::Text("Note: This shader is strongly effected by weather mod settings for bloom, tonemapping and color grading and is therefore disabled by default."); + } + ImGui::SeparatorText("General"); + if (ImGui::Checkbox("Enable Sun Glare", &settings.EnableSunGlare)) + renderdata->GetEffect(Shaders::LensSunGlare).Toggle(settings.EnableSunGlare); + + ImGui::SliderFloat("Glare Scale ##sun", &mainSettings.SG_Scale, 0.25f, 1.0f); + ImGui::SliderFloat("Glare Intensity ##sun", &mainSettings.SG_Intensity, 0.0f, 1.0f); + ImGui::SliderFloat("Glare Outer Fade ##sun", &mainSettings.SG_OuterFade, 0.0f, 1.0f); + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::Text("Color "); + ImGui::SameLine(); + float4& SGColor = coldSettings.SG_Color; + if (ImGui::ColorButton("Pick", ImVec4(SGColor.x, SGColor.y, SGColor.z, SGColor.w))) + ImGui::OpenPopup("ColorPopup_"); + + if (ImGui::BeginPopup("ColorPopup_", ImGuiWindowFlags_NoMove)) { + ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::GetFontSize() + ImGui::GetStyle().ItemInnerSpacing.x * 2); + if (ImGui::SmallButton("X")) + ImGui::CloseCurrentPopup(); + ImGui::ColorPicker4("RGBA", &SGColor.x, ImGuiColorEditFlags_AlphaBar); + ImGui::EndPopup(); + } + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } +} + +void LensEffects::RefreshToggles() +{ + if (renderdata) { + renderdata->GetEffect(Shaders::LensIce).Toggle(settings.EnableIce); + renderdata->GetEffect(Shaders::LensCA).Toggle(settings.EnableCA); + renderdata->GetEffect(Shaders::LensBurst).Toggle(settings.EnableStarburst); + renderdata->GetEffect(Shaders::LensSunGlare).Toggle(settings.EnableSunGlare); + renderdata->GetEffect(Shaders::LensGlare).Toggle(settings.EnableLensGlare); + renderdata->GetEffect(Shaders::LensHalo).Toggle(settings.EnableHalo); + renderdata->GetEffect(Shaders::LensGhosts).Toggle(settings.EnableGhosts); + } +} + +void LensEffects::LoadSettings(json& o_json) +{ + settings = o_json; + RefreshToggles(); +} + +void LensEffects::SaveSettings(json& o_json) +{ + o_json = settings; +} + +void LensEffects::RestoreDefaultSettings() +{ + settings = {}; + RefreshToggles(); +} diff --git a/src/Features/LensEffects.h b/src/Features/LensEffects.h new file mode 100644 index 0000000000..18d9d7eb12 --- /dev/null +++ b/src/Features/LensEffects.h @@ -0,0 +1,601 @@ +#pragma once +#include "Feature.h" + +struct LensEffects : Feature +{ + static LensEffects* GetSingleton() + { + static LensEffects singleton; + return &singleton; + } + + virtual inline std::string GetName() override { return "Lens Effects"; } + virtual inline std::string GetShortName() override { return "LensEffects"; } + virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; } + virtual inline std::string_view GetShaderDefineName() override { return "LENS_EFFECTS"; } + virtual std::string_view GetCategory() const override { return "Post-Processing"; } + virtual inline bool SupportsVR() override { return false; }; // + virtual std::pair> GetFeatureSummary() override + { + return { + "Lens effects mimic how camera lenses respond to different levels of light and environmental factors. The result is stronger highlights and clearer visual cues that help guide attention.", + { "Motion based chromatic aberration", + "Starburst lens flare", + "Lens ghosting", + "Lens halo effect", + "Lens glare", + "Adjustable sun glare", + "Weather based frost vignette" } + }; + } + + virtual inline void DataLoaded() override { RE::GetINISetting("bLensFlare:Imagespace")->data.b = true; } + virtual inline void PostPostLoad() override { Hooks::Install(); } + virtual void SetupResources() override; + void CompileShaders(); + + virtual void CheckOverride(); + void LookupShader(int desc); + + void AppendOcclusionLUT(); + void SetupOcclusionMask(); + void SetupBurstEffect(); + void SetupSunGlareEffect(); + void SetupLensGlareEffect(); + void SetupHaloEffect(); + void SetupGhostEffect(); + void SetupIceEffect(); + void BypassShader(); + void SetupCAEffect(); + + ConstantBuffer* SettingsCB = nullptr; + ID3D11BlendState* BlendState[2] = {}; + ID3D11RasterizerState* Raster = nullptr; + D3D11_VIEWPORT viewport{}; + + ID3D11SamplerState* LinearSampler = nullptr; + ID3D11SamplerState* PointSampler = nullptr; + ID3D11SamplerState* DepthSampler = nullptr; + + ID3D11ShaderResourceView* AtlasTexSRV = nullptr; + ID3D11ShaderResourceView* IceTexSRV = nullptr; + ID3D11ShaderResourceView* SunTexSRV = nullptr; + + ID3D11Texture2D* SunOcclusionLUT = nullptr; + ID3D11Texture2D* SunOcclusionLUT_AT = nullptr; + ID3D11UnorderedAccessView* SunOcclusionUAV = nullptr; + ID3D11UnorderedAccessView* SunOcclusionUAV_AT = nullptr; + ID3D11ShaderResourceView* SunOcclusionSRV = nullptr; + + ID3D11PixelShader* SunOcclusionMaskPixelShader = nullptr; + ID3D11PixelShader* ChromaticAberrationPixelShader = nullptr; + ID3D11VertexShader* BypassVertexShader = nullptr; + + ID3D11VertexShader* BurstVertexShader = nullptr; + ID3D11PixelShader* BurstPixelShader = nullptr; + + ID3D11VertexShader* SunGlareVertexShader = nullptr; + ID3D11PixelShader* SunGlarePixelShader = nullptr; + + ID3D11VertexShader* LensGlareVertexShader = nullptr; + ID3D11PixelShader* LensGlarePixelShader = nullptr; + + ID3D11VertexShader* HaloVertexShader = nullptr; + ID3D11PixelShader* HaloPixelShader = nullptr; + + ID3D11VertexShader* GhostVertexShader = nullptr; + ID3D11PixelShader* GhostPixelShader = nullptr; + + ID3D11VertexShader* IceVertexShader = nullptr; + ID3D11PixelShader* IcePixelShader = nullptr; + + uintptr_t* skyrim_FlareData = nullptr; + uint32_t* skyrim_RunFlarePtr = nullptr; + + RE::NiPoint3* skyrim_SunPosition = nullptr; + float* skyrim_SunGlareScale = nullptr; + bool skyrim_SunVisble; + + void(__fastcall* gFlareApplyFunc)(RE::NiCamera*, void*, uint64_t) = nullptr; + void* gFlareShader = nullptr; + + static constexpr int ghostpasses = 20; + + bool overrideShader = false; + bool useCloudLUT = false; + uint frameIdx = 5; + + DirectX::XMFLOAT4A GetSunPosition(); + DirectX::XMFLOAT4A GetSunColor(); + void GetWeatherShader(); + float GetWeatherPrecip(); + bool CheckWeatherChange(); + void UpdateWeatherBasedDisable(); + + bool disableSunFX = false; + float weatherFadeout = 0.0f; + float snowPrecipValue = 0.0f; + uint32_t PrevWeatherID = 0; + uint32_t WeatherID = 0; + float SunScale; + static inline std::array weatherDisablesSun = { { 0x00D299E, 0x02006AEC, 0x02001407, 0x02018DBB, 0x02018DBC, 0x02018DBD, + 0x02001407, 0x000D9329, 0x0200959F, 0x00105941, 0x000923FD, 0x00048C14, + 0x0010FEF8, 0x0010D9EC, 0x000923FD, 0x00105941, 0x0010199F, 0x000C821E, + 0x0010FE7E, 0x0010A7A7, 0x0010A23E, 0x0010A239, 0x0010A235, 0x0010A232, + 0x00106635, 0x0010A242, 0x00105945, 0x00105944, 0x00105943, 0x00105942, + 0x000c8221, 0x0401DFF5, 0x04034CFB, 0x0401DFF5, 0x04034CFB, 0x0010d9ec, 0x0010fef8 } }; //Fog weathers, Sovngarde, soul cairn, Apocrypha etc. + + static inline std::array weatherDisablesSnow = { { 0x00048C14, 0x04018471, 0x040374B8, 0x04031AC0, 0x040374B9, + 0x04032336, 0x0401D760, 0x040374BA, 0x0401DFF5, 0x04034CFB } }; //Ash is flaged as snow + + void RefreshToggles(); + virtual void RestoreDefaultSettings() override; + virtual void DrawSettings() override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + struct MainSettings + { + //Starburst + float SB_Scale = 0.23f; + float SB_Intensity = 0.25f; + + uint SB_EnableBlades = false; + float SB_BladeInt = 0.3f; + float SB_BladeVertices = 6.0f; + float SB_BladeSplay = 0.0f; + float SB_BladeRotation = 180.0f; + float SB_BladeLength = 0.8f; + float SB_BladeBaseWidth = 1.5f; + float SB_BladeWidth = 1.1f; + float SB_BladeTaper = 1.0f; + float SB_BladeFeather = 35.0f; + float SB_BladeFadePow = 0.5f; + float SB_BladeFadeDist = 3.0f; + float SBEX_BladeSplayLen = 0.0f; + + uint SB_EnableRays = true; + float SB_RandomRaysInt = 1.2f; + float SB_RandomRaysVolume = 0.3f; + float SB_RandomRaysLength = 1.0f; + float SB_RandomRaysWidth = 0.073f; + + //Ghosts + float GH_Scale = 0.40f; + float GH_Intensity = 0.4f; + float GH_Saturation = 1.0f; + uint GH_EnableClampOffset = true; + float GH_ClampOffset = 0.5f; + + float GH_Size = 0.0f; + float GH_Offset = 0.0f; + float GH_Shape = 0.0f; + float GH_Roundness = 0.0f; + float GH_Rotation = 0.0f; + float GH_Feather = 0.0f; + float GH_CAScale = 0.0f; + float GH_InnerInt = 0.0f; + + //Lens Glare + float GL_Scale = 0.35f; + float GL_Intensity = 0.3f; + uint GL_DynPosition = false; + float GL_XAxisOffset = 0.5f; + float GL_YAxisOffset = 0.1f; + float GL_MaxRotation = 50.0f; + float GL_CutDepth = 0.86f; + float GL_Radius = 0.88f; + float GL_TipFade = 1.0f; + + //Halo + float HL_Scale = 0.45f; + float HL_Intensity = 0.14f; + uint HL_EnableExp = true; + uint HL_FlipExpOffset = false; + float HL_ExpMinSize = 0.5f; + float HL_ExpMaxSize = 0.4f; + float HL_RotationSpeed = 0.22f; + float HL_LineVolume = 5.0f; + float HL_LineLength = 0.11f; + float HL_LineWidth = 0.085f; + float HL_LineTaper = 0.15f; + float HL_ColorShift = 0.52f; + + //Sun Glare + float SG_Scale = 0.5f; + float SG_Intensity = 1.0f; + float SG_OuterInt = 1.0f; + float SG_OuterFade = 0.8f; + + //LensCA + float CA_Intensity = 0.25f; + float CA_Threshold = 0.015f; + float CA_MaxOffset = 0.003f; + uint CA_RChannelOnly = false; + + //LensIce + float LI_Intensity = 0.5f; + float LIEX_FadeFactor = 0.0f; + //64 256 + + DirectX::XMFLOAT4A SB_Color = DirectX::XMFLOAT4A(0.0f, 0.0f, 0.0f, 0.0f); + DirectX::XMFLOAT4A SG_Color = DirectX::XMFLOAT4A(0.0f, 0.0f, 0.0f, 0.0f); + DirectX::XMFLOAT4A HL_Color = DirectX::XMFLOAT4A(0.0f, 0.0f, 0.0f, 0.0f); + DirectX::XMFLOAT4A GH_Color = DirectX::XMFLOAT4A(0.0f, 0.0f, 0.0f, 0.0f); + DirectX::XMFLOAT4A GH_Atlas = DirectX::XMFLOAT4A(0.0f, 0.0f, 0.0f, 0.0f); + DirectX::XMFLOAT4A LI_Color = DirectX::XMFLOAT4A(0.0f, 0.0f, 0.0f, 0.0f); + }; + + struct ColdSettings + { + //Size, Offset, Shape, Roundness + std::array GH_Params = { + float4(0.19f, 1.00f, 7.00f, 0.15f), + float4(0.08f, 0.91f, 6.00f, 0.67f), + float4(0.42f, 0.78f, 6.00f, 0.05f), + float4(0.14f, 0.64f, 6.00f, 0.50f), + float4(0.12f, 0.54f, 6.00f, 0.35f), + float4(0.13f, 0.43f, 5.00f, 0.42f), + float4(0.20f, 0.43f, 7.00f, 1.00f), + float4(0.22f, 0.43f, 6.00f, 0.30f), + float4(0.14f, 0.30f, 9.00f, 0.25f), + float4(0.13f, 0.20f, 6.00f, 0.60f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f), + float4(0.1f, 1.0f, 6.0f, 0.0f) + }; + //Rotation, Feather, CA Scale, Motion + std::array GH_Params_2 = { + float4(0.00f, 0.03f, 1.11f, 0.11f), + float4(215.0f, 0.39f, 2.00f, 0.64f), + float4(45.0f, 0.16f, 1.00f, 0.20f), + float4(40.0f, 0.13f, 1.27f, 0.34f), + float4(63.0f, 0.34f, 1.00f, 0.26f), + float4(0.00f, 1.00f, 1.00f, 0.74f), + float4(31.0f, 0.32f, 1.00f, 0.40f), + float4(60.0f, 0.16f, 1.00f, 0.15f), + float4(90.0f, 0.10f, 1.00f, 0.11f), + float4(75.0f, 0.73f, 1.00f, 0.67f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f), + float4(0.0f, 0.0f, 1.0f, 1.00f) + }; + //Guess + std::array GH_Color = { + float4(0.48f, 0.00f, 0.58f, 0.60f), + float4(1.00f, 1.00f, 1.00f, 0.55f), + float4(0.20f, 0.00f, 0.25f, 0.98f), + float4(1.00f, 0.80f, 0.80f, 0.41f), + float4(0.17f, 0.24f, 0.86f, 0.59f), + float4(0.85f, 0.77f, 0.37f, 0.67f), + float4(1.00f, 0.00f, 0.00f, 0.25f), + float4(0.00f, 0.46f, 0.82f, 0.30f), + float4(1.00f, 0.00f, 0.00f, 0.38f), + float4(0.99f, 0.98f, 0.42f, 0.35f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f), + float4(1.0f, 1.0f, 1.0f, 0.0f) + }; + //Tex, Vis, Scale, NA + std::array GH_Atlas = { + float4(1.00f, 0.42f, 0.50f, 0.00f), + float4(1.00f, 0.00f, 1.00f, 0.00f), + float4(2.00f, 0.11f, 0.77f, 0.00f), + float4(1.00f, 0.28f, 1.00f, 0.00f), + float4(1.00f, 0.14f, 0.55f, 0.00f), + float4(1.00f, 0.00f, 1.00f, 0.00f), + float4(3.00f, 0.24f, 0.50f, 0.00f), + float4(4.00f, 0.00f, 1.00f, 0.00f), + float4(4.00f, 0.61f, 1.00f, 0.00f), + float4(2.00f, 1.00f, 1.00f, 0.00f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(1.0f, 0.0f, 0.0f, 0.0f) + }; + + float4 SB_Color = float4(1.0f, 1.0f, 1.0f, 1.0f); + float4 SG_Color = float4(1.0f, 0.9f, 0.7f, 1.0f); + float4 HL_Color = float4(1.0f, 0.0f, 0.0f, 1.0f); + float4 LI_Color = float4(0.29f, 0.69f, 0.8f, 1.0f); + + float LI_FadeDuration = 1.0f; + float LI_FadeIn = 0.35f; + float LI_FadeOut = 0.01f; + }; + + struct Settings + { + MainSettings mainsettings; + ColdSettings coldsettings; + bool EnableStarburst = true; + bool EnableSunGlare = false; + bool EnableLensGlare = true; + bool EnableHalo = true; + bool EnableGhosts = true; + bool EnableCA = true; + bool EnableIce = true; + + bool useCustomPreset = true; + }; + Settings settings; + + struct alignas(16) ConstBuffer + { + DirectX::XMFLOAT4A screensize; + uint frame; + float precip; + float sunFXFade; + float _pad[1]; + DirectX::XMFLOAT4A SunParams; + DirectX::XMFLOAT4A suncolor; + MainSettings shadersettings; + }; + ConstBuffer UpdateBufferValues(); + + inline DirectX::XMFLOAT4A VectorToXMFloat(float4& value) { return DirectX::XMFLOAT4A(value.x, value.y, value.z, value.w); } + inline float LinearStep(float edge0, float edge1, float x) { return std::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); } + + inline MainSettings UpdateSettings() + { + auto mSettings = settings.mainsettings; + auto& cSettings = settings.coldsettings; + mSettings.SB_BladeTaper = mSettings.SB_BladeTaper + ((mSettings.SB_BladeSplay > 0.01f) * 80); + mSettings.SB_BladeWidth = (1.0f + (mSettings.SB_BladeWidth - 1.0f) * 0.25f) + mSettings.SB_BladeSplay; + mSettings.SB_BladeBaseWidth = 1.0f + (mSettings.SB_BladeBaseWidth - 1.0f) * 0.25f; + mSettings.SB_RandomRaysWidth = std::lerp(11.0f, 7.0f, mSettings.SB_RandomRaysWidth); + mSettings.SBEX_BladeSplayLen = (1 / mSettings.SB_BladeLength - 0.95f) * (mSettings.SB_BladeSplay > 0.01f); + mSettings.SG_Scale = mSettings.SG_Scale * 0.75f; + mSettings.GH_Scale = mSettings.GH_Scale * 0.5f; + mSettings.HL_LineWidth = mSettings.HL_LineWidth * 0.1f; + mSettings.LIEX_FadeFactor = snowPrecipValue; + mSettings.SB_Color = VectorToXMFloat(cSettings.SB_Color); + mSettings.SG_Color = VectorToXMFloat(cSettings.SG_Color); + mSettings.HL_Color = VectorToXMFloat(cSettings.HL_Color); + mSettings.LI_Color = VectorToXMFloat(cSettings.LI_Color); + return mSettings; + } + + struct Shaders + { + enum Enum + { + Bypass = 0, + OcclusionLUT = 1, + AttachOcclusionLUT = 2, + + LensIce = 3, + LensCA = 4, + LensBurst = 5, + LensGlare = 6, + LensHalo = 7, + LensGhosts = 8, + LensSunGlare = 9, + }; + }; + Shaders::Enum shaderdesc; + + struct Setup //expanded version of BGS lens flare system + { + class LF_PassData + { + private: + const uint64_t shaderdesc; + uint64_t active; + const uint64_t numpasses; + const uint64_t weatherpass; + const uint64_t uncondpass; + const uint64_t deferred; + const uint64_t pad[2] = {}; + uint64_t* enginerefs_ptr = nullptr; + + std::array enginerefs{ 1, 8, 0, 0 }; + + public: + LF_PassData(uint64_t desc, uint64_t active, uint64_t numpasses, uint64_t weather, uint64_t nocond, uint64_t defer) : + shaderdesc(desc), active(active), numpasses(numpasses), weatherpass(weather), uncondpass(nocond), deferred(defer) + { + enginerefs_ptr = enginerefs.data(); + } + + int passesdone = 0; + + Shaders::Enum GetDesc() const { return (Shaders::Enum)shaderdesc; } + int PassesRemaining() const { return (int)numpasses - (int)passesdone; } + int PassesTotal() const { return (int)numpasses; } + + void Toggle(bool value) { active = (uint64_t)value; } + bool IsActive() const { return (bool)active; } + bool IsWeatherShader() const { return (bool)weatherpass; } + bool IsUncond() const { return (bool)uncondpass; } + bool IsDeferred() const { return (bool)deferred; } //renders between upscaling and refraction + + void CheckRefs() + { + enginerefs = { 1, 8, 0, 0 }; + enginerefs_ptr = enginerefs.data(); + } + }; + + class LF_RenderData + { + private: + const uint64_t head = 0x3F800000; + std::unique_ptr* passarray_ptr = nullptr; + const uint64_t _pad[1] = {}; + uint64_t passcount = 0; + const uint64_t pad[2] = {}; + + std::vector> Effects; + std::vector> RenderPassList; //engine loops via passarray_ptr and renders for each + size_t currentPass = 0; + + public: + LF_RenderData() + { + RenderPassList.reserve(100); + } + + struct Type + { + bool weather = false; + bool uncond = false; + bool deferred = false; + }; + + void AddPasses(LF_PassData& effect) + { + for (int i = 0; i < effect.PassesTotal(); i++) { + RenderPassList.push_back(std::make_unique(effect)); + } + } + + void AddEffect(int desc, bool active, int passes, Type type = {}) + { + Effects.push_back(std::make_unique(desc, active, passes, type.weather, type.uncond, type.deferred)); + } + + void SetRenderData() + { + passcount = RenderPassList.size(); + passarray_ptr = RenderPassList.data(); + } + + void CreateRenderList(bool sunVisible, bool deferred = false) + { + RenderPassList.clear(); + currentPass = 0; + + for (auto& effect : Effects) { + effect->passesdone = 0; + if (effect->IsActive()) { + if (sunVisible || effect->IsUncond() || effect->IsWeatherShader()) + if (deferred == effect->IsDeferred()) + AddPasses(*effect); + } + } + SetRenderData(); + } + + LF_PassData& GetEffect(int desc) + { + for (auto& effect : Effects) { + if (effect->GetDesc() == desc) + return *effect.get(); + } + throw std::out_of_range(""); + } + + Shaders::Enum UpdateCurrentEffect() + { + auto desc = Shaders::Bypass; + if (currentPass < RenderPassList.size()) { + desc = RenderPassList[currentPass]->GetDesc(); + GetEffect(desc).passesdone++; + currentPass++; + } + return desc; + } + + void CheckRefData() + { + for (auto& pass : RenderPassList) pass->CheckRefs(); + } + }; + }; + Setup::LF_RenderData* renderdata = nullptr; + + struct Hooks + { + struct LensFlare_CheckResources //override main init/integ + { + static void thunk(); + static inline REL::Relocation func; + }; + + struct LensFlareVisibility_CheckRenderCondition + { + static void thunk(RE::NiCamera* camera, void* unk); + static inline REL::Relocation func; + }; + + struct LensFlare_CheckRenderCondition //setup buffers etc, if sun on screen return true (override for non sun FX) + { + static bool thunk(void* shader, RE::NiCamera* camera, uint64_t unk); + static inline REL::Relocation func; + }; + + template + struct BSImagespaceShader_Render + { + static void thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param); + static inline REL::Relocation func; + }; + + struct LensFlare_AssignTexture //remove lensflare texture init/assignment + { + static void thunk(void* previous, uint64_t current) + { + current = 0; + func(previous = ¤t, current); + } + static inline REL::Relocation func; + }; + + struct Main_PostProcessing //run post upscale effects + { + static void thunk(RE::ImageSpaceManager* a1, uint32_t a3, uint32_t er8_); + static inline REL::Relocation func; + }; + + struct BSSkyShader_SetupMaterial //override sun glare, fetch sun scale, append occlusion LUT + { + static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); + static inline REL::Relocation func; + }; + + static void Install() + { + logger::info("[Lens Effects] Installed hooks"); + + stl::detour_thunk(REL::RelocationID(25772, 26327)); + stl::write_thunk_call(REL::RelocationID(100274, 106988).address() + REL::Relocate(0x188, 0x195)); + stl::write_thunk_call(REL::RelocationID(100281, 106995).address() + REL::Relocate(0x14, 0x16)); + stl::write_thunk_call(REL::RelocationID(100280, 106994).address() + REL::Relocate(0x4B, 0x4B)); + + stl::write_thunk_call>(REL::RelocationID(100274, 106988).address() + REL::Relocate(0x1CB, 0x275)); + stl::write_vfunc<0x1, BSImagespaceShader_Render>(RE::VTABLE_BSImagespaceShaderLensFlare[3]); + + stl::write_vfunc<0x6, BSSkyShader_SetupMaterial>(RE::VTABLE_BSSkyShader[0]); + + stl::detour_thunk(REL::RelocationID(99023, 105674)); + } + }; +}; diff --git a/src/Globals.cpp b/src/Globals.cpp index 7d6995ad45..419890bbaf 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -12,6 +12,7 @@ #include "Features/InteriorSun.h" #include "Features/InverseSquareLighting.h" #include "Features/LODBlending.h" +#include "Features/LensEffects.h" #include "Features/LightLimitFix.h" #include "Features/PerformanceOverlay.h" #include "Features/RenderDoc.h" @@ -76,6 +77,7 @@ namespace globals ExtendedTranslucency extendedTranslucency{}; Upscaling upscaling{}; RenderDoc renderDoc{}; + LensEffects lensEffects{}; namespace llf { diff --git a/src/Globals.h b/src/Globals.h index cf52ded482..26e3238330 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -9,6 +9,7 @@ struct HairSpecular; struct IBL; struct LightLimitFix; struct LODBlending; +struct LensEffects; struct InteriorSun; struct InverseSquareLighting; struct ScreenSpaceGI; @@ -80,6 +81,7 @@ namespace globals extern ExtendedTranslucency extendedTranslucency; extern Upscaling upscaling; extern RenderDoc renderDoc; + extern LensEffects lensEffects; namespace llf { diff --git a/src/State.cpp b/src/State.cpp index 3846ab0046..77ad83c0f9 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -7,6 +7,7 @@ #include "Deferred.h" #include "FeatureIssues.h" #include "Features/CloudShadows.h" +#include "Features/LensEffects.h" #include "Features/PerformanceOverlay.h" #include "Features/TerrainBlending.h" #include "Features/TerrainHelper.h" @@ -24,6 +25,7 @@ void State::Draw() auto& terrainBlending = globals::features::terrainBlending; auto& terrainHelper = globals::features::terrainHelper; auto& cloudShadows = globals::features::cloudShadows; + auto& lensEffects = globals::features::lensEffects; auto truePBR = globals::truePBR; auto context = globals::d3d::context; @@ -37,6 +39,9 @@ void State::Draw() if (terrainHelper.loaded) terrainHelper.SetShaderResouces(context); + if (lensEffects.loaded) + lensEffects.CheckOverride(); + truePBR->SetShaderResouces(context); if (permutationData != permutationDataPrevious) {