diff --git a/features/Grass Lighting/Shaders/RunGrass.hlsl b/features/Grass Lighting/Shaders/RunGrass.hlsl index eae1ea92e..e4e5c5511 100644 --- a/features/Grass Lighting/Shaders/RunGrass.hlsl +++ b/features/Grass Lighting/Shaders/RunGrass.hlsl @@ -297,7 +297,7 @@ float3x3 CalculateTBN(float3 N, float3 p, float2 uv) # endif # if defined(SKYLIGHTING) -# define LinearSampler SampBaseSampler +# define SL_INCL_METHODS # include "Skylighting/Skylighting.hlsli" # endif @@ -459,9 +459,17 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float3 directionalAmbientColor = mul(DirectionalAmbientShared, float4(normal, 1.0)); # if defined(SKYLIGHTING) - float4 skylightingSH = SkylightingTexture.Load(int3(input.HPosition.xy, 0)); - float skylighting = saturate(shUnproject(skylightingSH, normal)); - directionalAmbientColor *= lerp(1.0 / 3.0, 1.0, skylighting); +# if defined(VR) + float3 positionMSSkylight = input.WorldPosition.xyz + (eyeIndex == 1 ? CameraPosAdjust[1] - CameraPosAdjust[0] : 0); +# else + float3 positionMSSkylight = input.WorldPosition.xyz; +# endif + + sh2 skylightingSH = sampleSkylighting(skylightingSettings, SkylightingProbeArray, positionMSSkylight, worldSpaceNormal); + float skylighting = shHallucinateZH3Irradiance(skylightingSH, normalWS); + skylighting = lerp(skylightingSettings.MixParams.x, 1, saturate(skylighting * skylightingSettings.MixParams.y)); + skylighting = applySkylightingFadeout(skylighting, length(positionMSSkylight)); + directionalAmbientColor *= skylighting; # endif diffuseColor += directionalAmbientColor; diff --git a/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli b/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli index 3a5f021d0..98dd43d41 100644 --- a/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli +++ b/features/Skylighting/Shaders/Skylighting/Skylighting.hlsli @@ -1,129 +1,152 @@ -#include "Common/Spherical Harmonics/SphericalHarmonics.hlsli" +// Define SL_INCL_STRUCT and SL_INCL_METHODS to include different parts +// Because this file is included by both forward and deferred shaders -Texture2D OcclusionMapSampler : register(t29); -Texture2D SkylightingTexture : register(t30); - -cbuffer SkylightingData : register(b8) +#ifdef SL_INCL_STRUCT +struct SkylightingSettings { row_major float4x4 OcclusionViewProj; - float4 EyePosition; - float4 ShadowDirection; + float4 OcclusionDir; + + float4 PosOffset; + uint4 ArrayOrigin; + int4 ValidMargin; + + float4 MixParams; // x: min diffuse visibility, y: diffuse mult, z: min specular visibility, w: specular mult + + uint DirectionalDiffuse; + float3 _pad1; }; -float GetVLSkylighting(float3 startPosWS, float3 endPosWS, float2 screenPosition) -{ - const static uint nSteps = 16; - const static float step = 1.0 / float(nSteps); +#endif - float3 worldDir = endPosWS - startPosWS; +#ifdef SL_INCL_METHODS - float noise = InterleavedGradientNoise(screenPosition); +# include "Common/Spherical Harmonics/SphericalHarmonics.hlsli" - startPosWS += worldDir * step * noise; +# ifdef PSHADER +Texture3D SkylightingProbeArray : register(t29); +# endif - float noise2 = noise * 2.0 * M_PI; +const static uint3 SL_ARRAY_DIM = uint3(128, 128, 64); +const static float3 SL_ARRAY_SIZE = float3(8192, 8192, 8192 * 0.5); +const static float3 SL_CELL_SIZE = SL_ARRAY_SIZE / SL_ARRAY_DIM; - half2x2 rotationMatrix = half2x2(cos(noise2), sin(noise2), -sin(noise2), cos(noise2)); +sh2 sampleSkylighting(SkylightingSettings params, Texture3D probeArray, float3 positionMS, float3 normalWS) +{ + float3 positionMSAdjusted = positionMS - params.PosOffset; + float3 uvw = positionMSAdjusted / SL_ARRAY_SIZE + .5; - float vl = 0; + if (any(uvw < 0) || any(uvw > 1)) + return float4(1, 0, 1, 0); - half2 PoissonDisk[16] = { - half2(-0.94201624, -0.39906216), - half2(0.94558609, -0.76890725), - half2(-0.094184101, -0.92938870), - half2(0.34495938, 0.29387760), - half2(-0.91588581, 0.45771432), - half2(-0.81544232, -0.87912464), - half2(-0.38277543, 0.27676845), - half2(0.97484398, 0.75648379), - half2(0.44323325, -0.97511554), - half2(0.53742981, -0.47373420), - half2(-0.26496911, -0.41893023), - half2(0.79197514, 0.19090188), - half2(-0.24188840, 0.99706507), - half2(-0.81409955, 0.91437590), - half2(0.19984126, 0.78641367), - half2(0.14383161, -0.14100790) - }; + float3 cellVxCoord = uvw * SL_ARRAY_DIM; + int3 cell000 = floor(cellVxCoord - 0.5); + float3 trilinearPos = cellVxCoord - 0.5 - cell000; - for (uint i = 0; i < nSteps; ++i) { - float t = saturate(i * step); + sh2 sum = 0; + float wsum = 0; + for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) + for (int k = 0; k < 2; k++) { + int3 offset = int3(i, j, k); + int3 cellID = cell000 + offset; - float shadow = 0; - { - float3 samplePositionWS = startPosWS + worldDir * t; + if (any(cellID < 0) || any(cellID >= SL_ARRAY_DIM)) + continue; - half2 offset = mul(PoissonDisk[(float(i) + noise) % 16].xy, rotationMatrix); - samplePositionWS.xy += offset * 128; + float3 cellCentreMS = cellID + 0.5 - SL_ARRAY_DIM / 2; + cellCentreMS = cellCentreMS * SL_CELL_SIZE; - half3 samplePositionLS = mul(OcclusionViewProj, float4(samplePositionWS.xyz, 1)); - samplePositionLS.y = -samplePositionLS.y; + // https://handmade.network/p/75/monter/blog/p/7288-engine_work__global_illumination_with_irradiance_probes + // basic tangent checks + if (dot(cellCentreMS - positionMSAdjusted, normalWS) <= 0) + continue; - float deltaZ = samplePositionLS.z - (1e-2 * 0.05); + float3 trilinearWeights = 1 - abs(offset - trilinearPos); + float w = trilinearWeights.x * trilinearWeights.y * trilinearWeights.z; - half2 occlusionUV = samplePositionLS.xy * 0.5 + 0.5; + uint3 cellTexID = (cellID + params.ArrayOrigin.xyz) % SL_ARRAY_DIM; + sh2 probe = shScale(probeArray[cellTexID], w); - float4 depths = OcclusionMapSampler.GatherRed(LinearSampler, occlusionUV.xy, 0); + sum = shAdd(sum, probe); + wsum += w; + } - shadow = dot(depths > deltaZ, 0.25); - } - vl += shadow; - } - return vl * step; + sh2 result = shScale(sum, rcp(wsum + 1e-10)); + + return result; } -float GetSkylightOcclusion(float3 worldPosition, float noise) +float getVLSkylighting(SkylightingSettings params, Texture3D probeArray, float3 startPosWS, float3 endPosWS, float2 screenPosition) { const static uint nSteps = 16; const static float step = 1.0 / float(nSteps); - float noise2 = noise * 2.0 * M_PI; + float3 worldDir = endPosWS - startPosWS; + float3 worldDirNormalised = normalize(worldDir); - half2x2 rotationMatrix = half2x2(cos(noise2), sin(noise2), -sin(noise2), cos(noise2)); + float noise = InterleavedGradientNoise(screenPosition); float vl = 0; - half2 PoissonDisk[16] = { - half2(-0.94201624, -0.39906216), - half2(0.94558609, -0.76890725), - half2(-0.094184101, -0.92938870), - half2(0.34495938, 0.29387760), - half2(-0.91588581, 0.45771432), - half2(-0.81544232, -0.87912464), - half2(-0.38277543, 0.27676845), - half2(0.97484398, 0.75648379), - half2(0.44323325, -0.97511554), - half2(0.53742981, -0.47373420), - half2(-0.26496911, -0.41893023), - half2(0.79197514, 0.19090188), - half2(-0.24188840, 0.99706507), - half2(-0.81409955, 0.91437590), - half2(0.19984126, 0.78641367), - half2(0.14383161, -0.14100790) - }; - for (uint i = 0; i < nSteps; ++i) { float t = saturate(i * step); float shadow = 0; { - float3 samplePositionWS = worldPosition; + float3 samplePositionWS = startPosWS + worldDir * t; - half2 offset = mul(PoissonDisk[i].xy, rotationMatrix); - samplePositionWS.xy += offset * 16; + sh2 skylighting = sampleSkylighting(params, probeArray, samplePositionWS, float3(0, 0, 1)); - half3 samplePositionLS = mul(OcclusionViewProj, float4(samplePositionWS.xyz, 1)); - samplePositionLS.y = -samplePositionLS.y; + shadow += lerp(params.MixParams.x, 1, saturate(shUnproject(skylighting, worldDirNormalised) * params.MixParams.y)); + } + vl += shadow; + } + return vl * step; +} - float deltaZ = samplePositionLS.z - (1e-2 * 0.2); +// http://torust.me/ZH3.pdf +// ZH hallucination that makes skylighting more directional +// skipped luminance because it's single channel +float shHallucinateZH3Irradiance(sh2 sh, float3 normal) +{ + const static float factor = sqrt(5.0f / (16.0f * 3.1415926f)); - half2 occlusionUV = samplePositionLS.xy * 0.5 + 0.5; + float3 zonalAxis = normalize(sh.wyz); + float ratio = abs(dot(sh.wyz, zonalAxis)) / sh.x; + float3 zonalL2Coeff = sh.x * (0.08f * ratio + 0.6f * ratio * ratio); - float4 depths = OcclusionMapSampler.GatherRed(LinearSampler, occlusionUV.xy, 0); + float fZ = dot(zonalAxis, normal); + float zhDir = factor * (3.0f * fZ * fZ - 1.0f); - shadow = dot(depths > deltaZ, 0.25); - } - vl += shadow; - } - return sqrt(vl * step); + float result = shFuncProductIntegral(sh, shEvaluateCosineLobe(normal)); + + result += 0.25f * zonalL2Coeff * zhDir; + + return saturate(result); +} + +sh2 fauxSpecularLobeSH(float3 N, float3 V, float roughness) +{ + // https://www.gdcvault.com/play/1026701/Fast-Denoising-With-Self-Stabilizing + // get dominant ggx reflection direction + float f = (1 - roughness) * (sqrt(1 - roughness) + roughness); + float3 R = reflect(-V, N); + float3 D = lerp(N, R, f); + float3 dominantDir = normalize(D); + + sh2 directional = shEvaluate(dominantDir); + sh2 cosineLobe = shEvaluateCosineLobe(dominantDir); + sh2 result = shAdd(shScale(directional, f), shScale(cosineLobe, 1 - f)); + + return result; } + +float applySkylightingFadeout(float x, float dist) +{ + const static float fadeDist = 0.9; + float fadeFactor = saturate((dist * 2 / SL_ARRAY_SIZE.x - fadeDist) / (1 - fadeDist)); + return lerp(x, 1, fadeFactor); +} + +#endif \ No newline at end of file diff --git a/features/Skylighting/Shaders/Skylighting/SkylightingBlurCS.hlsl b/features/Skylighting/Shaders/Skylighting/SkylightingBlurCS.hlsl deleted file mode 100644 index a885c9693..000000000 --- a/features/Skylighting/Shaders/Skylighting/SkylightingBlurCS.hlsl +++ /dev/null @@ -1,73 +0,0 @@ -#include "../Common/FrameBuffer.hlsl" -#include "../Common/GBuffer.hlsli" -#include "../Common/VR.hlsli" - -Texture2D DepthTexture : register(t0); - -Texture2D SkylightingTexture : register(t1); -RWTexture2D SkylightingTextureRW : register(u0); - -cbuffer PerFrame : register(b0) -{ - row_major float4x4 OcclusionViewProj; - float4 EyePosition; - float4 ShadowDirection; - float4 BufferDim; - float4 CameraData; - uint FrameCount; -}; - -SamplerState LinearSampler : register(s0); - -half GetScreenDepth(half depth) -{ - return (CameraData.w / (-depth * CameraData.z + CameraData.x)); -} - -// samples = 8, min distance = 0.5, average samples on radius = 2 -static const float3 g_Poisson8[8] = { - float3(-0.4706069, -0.4427112, +0.6461146), - float3(-0.9057375, +0.3003471, +0.9542373), - float3(-0.3487388, +0.4037880, +0.5335386), - float3(+0.1023042, +0.6439373, +0.6520134), - float3(+0.5699277, +0.3513750, +0.6695386), - float3(+0.2939128, -0.1131226, +0.3149309), - float3(+0.7836658, -0.4208784, +0.8895339), - float3(+0.1564120, -0.8198990, +0.8346850) -}; - -[numthreads(8, 8, 1)] void main(uint3 globalId - : SV_DispatchThreadID) { - half2 uv = half2(globalId.xy + 0.5) * (BufferDim.zw) * DynamicResolutionParams2.xy; - uint eyeIndex = GetEyeIndexFromTexCoord(uv); - - half4 skylightingSH = 0; - half weight = 0.0; - - half rawDepth = DepthTexture[globalId.xy]; - half depth = GetScreenDepth(rawDepth); - - [unroll] for (uint i = 0; i < 8; i++) - { - -#if defined(HORIZONTAL) - half2 testUV = uv + g_Poisson8[i].xy * BufferDim.zw * 8.0; -#else - half2 testUV = uv + g_Poisson8[i].yx * BufferDim.zw * 16.0; -#endif - - if (any(testUV < 0) || any(testUV > 1)) - continue; - - half sampleDepth = GetScreenDepth(DepthTexture.SampleLevel(LinearSampler, testUV, 0)); - half attenuation = g_Poisson8[i].z * lerp(g_Poisson8[i].z * 0.01, 1.0, 1.0 - saturate(0.01 * abs(sampleDepth - depth))); - - [branch] if (attenuation > 0.0) - { - skylightingSH += SkylightingTexture.SampleLevel(LinearSampler, testUV, 0) * attenuation; - weight += attenuation; - } - } - - SkylightingTextureRW[globalId.xy] = skylightingSH / weight; -} \ No newline at end of file diff --git a/features/Skylighting/Shaders/Skylighting/SkylightingCS.hlsl b/features/Skylighting/Shaders/Skylighting/SkylightingCS.hlsl deleted file mode 100644 index 8c56a0946..000000000 --- a/features/Skylighting/Shaders/Skylighting/SkylightingCS.hlsl +++ /dev/null @@ -1,237 +0,0 @@ -#include "../Common/FrameBuffer.hlsl" -#include "../Common/GBuffer.hlsli" -#include "../Common/Spherical Harmonics/SphericalHarmonics.hlsli" -#include "../Common/VR.hlsli" - -Texture2D DepthTexture : register(t0); - -struct PerGeometry -{ - float4 VPOSOffset; - float4 ShadowSampleParam; // fPoissonRadiusScale / iShadowMapResolution in z and w - float4 EndSplitDistances; // cascade end distances int xyz, cascade count int z - float4 StartSplitDistances; // cascade start ditances int xyz, 4 int z - float4 FocusShadowFadeParam; - float4 DebugColor; - float4 PropertyColor; - float4 AlphaTestRef; - float4 ShadowLightParam; // Falloff in x, ShadowDistance squared in z - float4x3 FocusShadowMapProj[4]; - // Since PerGeometry is passed between c++ and hlsl, can't have different defines due to strong typing - float4x3 ShadowMapProj[2][3]; - float4x4 CameraViewProjInverse[2]; -}; - -Texture2DArray TexShadowMapSampler : register(t1); -StructuredBuffer perShadow : register(t2); -Texture2DArray BlueNoise : register(t3); -Texture2D OcclusionMapSampler : register(t4); - -RWTexture2D SkylightingTextureRW : register(u0); - -cbuffer PerFrame : register(b0) -{ - row_major float4x4 OcclusionViewProj; - float4 EyePosition; - float4 ShadowDirection; - float4 BufferDim; - float4 CameraData; - uint FrameCount; -}; - -SamplerState LinearSampler : register(s0); -SamplerComparisonState ShadowSamplerPCF : register(s1); - -half GetBlueNoise(half2 uv) -{ - return BlueNoise[uint3(uv % 128, FrameCount % 64)]; -} - -#if !defined(SHADOWMAP) -[numthreads(8, 8, 1)] void main(uint3 globalId - : SV_DispatchThreadID) { - float2 uv = float2(globalId.xy + 0.5) * (BufferDim.zw) * DynamicResolutionParams2.xy; - uint eyeIndex = GetEyeIndexFromTexCoord(uv); - uv = ConvertFromStereoUV(uv, eyeIndex); - - float rawDepth = DepthTexture[globalId.xy]; - - float4 positionCS = float4(2 * float2(uv.x, -uv.y + 1) - 1, rawDepth, 1); - - float4 positionMS = mul(CameraViewProjInverse[eyeIndex], positionCS); - positionMS.xyz = positionMS.xyz / positionMS.w; - - positionMS.xyz = positionMS.xyz + CameraPosAdjust[eyeIndex] - EyePosition; - - float3 startPositionMS = positionMS; - - half noise = GetBlueNoise(globalId.xy) * 2.0 * shPI; - - half2x2 rotationMatrix = half2x2(cos(noise), sin(noise), -sin(noise), cos(noise)); - - float2 PoissonDisc[] = { - float2(0.881375f, 0.216315f), - float2(0.0872829f, 0.987854f), - float2(0.0710166f, 0.132633f), - float2(0.517563f, 0.643117f) - }; - - uint sampleCount = 4; - - float occlusionThreshold = mul(OcclusionViewProj, float4(positionMS.xyz, 1)).z; - - bool fadeOut = length(startPositionMS) > 1024; - - half weight = 0; - half wetnessWeight = 0; - - sh2 shSkylighting = shZero(); - float wetnessOcclusion = 0; - - half fadeFactor = pow(saturate(length(positionMS.xyz) / 10000.0), 8); - - [unroll] for (uint i = 0; i < sampleCount; i++) - { - float3 rayDir = float3(PoissonDisc[i].xy * 2.0 - 1.0, 0); - rayDir.xy = mul(rayDir.xy, rotationMatrix); - - positionMS.xy = startPositionMS + rayDir.xy * 128; - - half3 occlusionPosition = mul(OcclusionViewProj, float4(positionMS.xyz, 1)); - occlusionPosition.y = -occlusionPosition.y; - half2 occlusionUV = occlusionPosition.xy * 0.5 + 0.5; - - if ((occlusionUV.x == saturate(occlusionUV.x) && occlusionUV.y == saturate(occlusionUV.y)) || !fadeOut) { - float shadowMapValues = OcclusionMapSampler.SampleLevel(LinearSampler, occlusionUV, 0); - - shadowMapValues = 1.0 - shadowMapValues; - occlusionPosition.z = 1.0 - occlusionPosition.z; - - half skylightingContribution = 1.0 - saturate((shadowMapValues - occlusionPosition.z) * 4096); - - sh2 sh = shEvaluate(rayDir); - shSkylighting = shAdd(shSkylighting, shScale(sh, lerp(skylightingContribution, 1.0, fadeFactor))); - - weight++; - } else { - sh2 sh = shEvaluate(rayDir); - shSkylighting = shAdd(shSkylighting, shScale(sh, 1)); - weight++; - } - } - - if (weight > 0.0) { - float shFactor = 4.0 * shPI * 1.0 / weight; - shSkylighting = shScale(shSkylighting, shFactor); - } - - SkylightingTextureRW[globalId.xy] = shSkylighting; -} -#else - -half GetScreenDepth(half depth) -{ - return (CameraData.w / (-depth * CameraData.z + CameraData.x)); -} - -[numthreads(8, 8, 1)] void main(uint3 globalId - : SV_DispatchThreadID) { - float2 uv = float2(globalId.xy + 0.5) * BufferDim.zw * DynamicResolutionParams2.xy; - uint eyeIndex = GetEyeIndexFromTexCoord(uv); - uv = ConvertFromStereoUV(uv, eyeIndex); - - float rawDepth = DepthTexture[globalId.xy]; - - float4 positionCS = float4(2 * float2(uv.x, -uv.y + 1) - 1, rawDepth, 1); - - PerGeometry sD = perShadow[0]; - - sD.EndSplitDistances.x = GetScreenDepth(sD.EndSplitDistances.x); - sD.EndSplitDistances.y = GetScreenDepth(sD.EndSplitDistances.y); - sD.EndSplitDistances.z = GetScreenDepth(sD.EndSplitDistances.z); - sD.EndSplitDistances.w = GetScreenDepth(sD.EndSplitDistances.w); - - float4 positionMS = mul(CameraViewProjInverse[eyeIndex], positionCS); - positionMS.xyz = positionMS.xyz / positionMS.w; - - float3 startPositionMS = positionMS; - - half fadeFactor = pow(saturate(dot(positionMS.xyz, positionMS.xyz) / sD.ShadowLightParam.z), 8); - - fadeFactor = lerp(1.0, fadeFactor, pow(saturate(dot(float3(0, 0, -1), ShadowDirection.xyz)), 0.5)); - - half noise = GetBlueNoise(globalId.xy) * 2.0 * shPI; - - half2x2 rotationMatrix = half2x2(cos(noise), sin(noise), -sin(noise), cos(noise)); - - float2 PoissonDisc[] = { - float2(0.881375f, 0.216315f), - float2(0.0872829f, 0.987854f), - float2(0.0710166f, 0.132633f), - float2(0.517563f, 0.643117f) - }; - - uint sampleCount = 4; - - sh2 shSkylighting = shZero(); - - half weight = 0.0; - - [unroll] for (uint i = 0; i < sampleCount; i++) - { - float3 rayDir = float3(PoissonDisc[i].xy * 2.0 - 1.0, 0); - rayDir.xy = mul(rayDir.xy, rotationMatrix); - - positionMS.xy = startPositionMS + rayDir.xy * 128 + length(rayDir.xy) * ShadowDirection.xy * 128; - - rayDir = normalize(rayDir); - - float shadowMapDepth = length(positionMS.xyz); - - [flatten] if (sD.EndSplitDistances.z > shadowMapDepth) - { - half cascadeIndex = 0; - float4x3 lightProjectionMatrix = sD.ShadowMapProj[eyeIndex][0]; - - [flatten] if (2.5 < sD.EndSplitDistances.w && sD.EndSplitDistances.y < shadowMapDepth) - { - lightProjectionMatrix = sD.ShadowMapProj[eyeIndex][2]; - cascadeIndex = 2; - } - else if (sD.EndSplitDistances.x < shadowMapDepth) - { - lightProjectionMatrix = sD.ShadowMapProj[eyeIndex][1]; - cascadeIndex = 1; - } - - float3 positionLS = mul(transpose(lightProjectionMatrix), float4(positionMS.xyz, 1)).xyz; - - half shadowMapValues = TexShadowMapSampler.SampleLevel(LinearSampler, float3(positionLS.xy, cascadeIndex), 0); - - shadowMapValues = 1.0 - shadowMapValues; - positionLS.z = 1.0 - positionLS.z; - - half skylightingContribution = 1.0 - saturate((shadowMapValues - positionLS.z) * 4096); - - sh2 sh = shEvaluate(rayDir); - shSkylighting = shAdd(shSkylighting, shScale(sh, lerp(skylightingContribution, 1.0, fadeFactor))); - - weight++; - } - else - { - sh2 sh = shEvaluate(rayDir); - shSkylighting = shAdd(shSkylighting, shScale(sh, 1.0)); - - weight++; - } - } - - if (weight > 0.0) { - float shFactor = 4.0 * shPI * 1.0 / weight; - shSkylighting = shScale(shSkylighting, shFactor); - } - - SkylightingTextureRW[globalId.xy] = shSkylighting; -} -#endif \ No newline at end of file diff --git a/features/Skylighting/Shaders/Skylighting/bluenoise.dds b/features/Skylighting/Shaders/Skylighting/bluenoise.dds deleted file mode 100644 index ce41c5100..000000000 Binary files a/features/Skylighting/Shaders/Skylighting/bluenoise.dds and /dev/null differ diff --git a/features/Skylighting/Shaders/Skylighting/updateProbes.cs.hlsl b/features/Skylighting/Shaders/Skylighting/updateProbes.cs.hlsl new file mode 100644 index 000000000..1fb8a0fd5 --- /dev/null +++ b/features/Skylighting/Shaders/Skylighting/updateProbes.cs.hlsl @@ -0,0 +1,54 @@ +#define SL_INCL_STRUCT +#include "Skylighting.hlsli" + +#include "../Common/DeferredShared.hlsli" +#include "../Common/FrameBuffer.hlsl" +#include "../Common/Spherical Harmonics/SphericalHarmonics.hlsli" + +cbuffer SkylightingCB : register(b1) +{ + SkylightingSettings settings; +}; + +Texture2D srcOcclusionDepth : register(t0); + +RWTexture3D outProbeArray : register(u0); +RWTexture3D outAccumFramesArray : register(u1); + +SamplerState samplerPointClamp : register(s0); + +#define ARRAY_DIM uint3(128, 128, 64) +#define ARRAY_SIZE float3(8192, 8192, 8192 * 0.5) + +[numthreads(8, 8, 1)] void main(uint3 dtid + : SV_DispatchThreadID) { + const static float rcpMaxUint = rcp(4294967295.0); + + uint3 cellID = (int3(dtid) - settings.ArrayOrigin.xyz) % ARRAY_DIM; + bool isValid = all(cellID >= max(0, settings.ValidMargin.xyz)) && all(cellID <= ARRAY_DIM - 1 + min(0, settings.ValidMargin.xyz)); // check if the cell is newly added + + float3 cellCentreMS = cellID + 0.5 - ARRAY_DIM / 2; + cellCentreMS = cellCentreMS / ARRAY_DIM * ARRAY_SIZE + settings.PosOffset.xyz; + + float3 cellCentreOS = mul(settings.OcclusionViewProj, float4(cellCentreMS, 1)).xyz; + cellCentreOS.y = -cellCentreOS.y; + float2 occlusionUV = cellCentreOS.xy * 0.5 + 0.5; + + if (all(occlusionUV > 0) && all(occlusionUV < 1)) { + float occlusionDepth = srcOcclusionDepth.SampleLevel(samplerPointClamp, occlusionUV, 0); + bool visible = cellCentreOS.z < occlusionDepth; + + sh2 occlusionSH = shScale(shEvaluate(settings.OcclusionDir.xyz), float(visible)); + uint accumFrames = 1; + if (isValid) { + accumFrames = min(outAccumFramesArray[dtid] + 1, 10000); + float lerpFactor = rcp(accumFrames); + occlusionSH = shAdd(shScale(outProbeArray[dtid], 1 - lerpFactor), shScale(occlusionSH, lerpFactor)); // exponential accumulation + } + outProbeArray[dtid] = occlusionSH; + outAccumFramesArray[dtid] = accumFrames; + } else if (!isValid) { + outProbeArray[dtid] = float4(1, 0, 1, 0); + outAccumFramesArray[dtid] = 0; + } +} \ No newline at end of file diff --git a/package/Shaders/AmbientCompositeCS.hlsl b/package/Shaders/AmbientCompositeCS.hlsl index 460a1816f..6e3abb19f 100644 --- a/package/Shaders/AmbientCompositeCS.hlsl +++ b/package/Shaders/AmbientCompositeCS.hlsl @@ -8,12 +8,21 @@ Texture2D AlbedoTexture : register(t0); Texture2D NormalRoughnessTexture : register(t1); #if defined(SKYLIGHTING) -# include "Common/Spherical Harmonics/SphericalHarmonics.hlsli" -Texture2D SkylightingTexture : register(t2); +# define SL_INCL_STRUCT +# define SL_INCL_METHODS +# include "Skylighting/Skylighting.hlsli" + +cbuffer SkylightingCB : register(b1) +{ + SkylightingSettings skylightingSettings; +}; + +Texture2D DepthTexture : register(t2); +Texture3D SkylightingProbeArray : register(t3); #endif #if defined(SSGI) -Texture2D SSGITexture : register(t3); +Texture2D SSGITexture : register(t4); #endif RWTexture2D MainRW : register(u0); @@ -43,19 +52,30 @@ RWTexture2D DiffuseAmbientRW : register(u1); ambient = sRGB2Lin(max(0, ambient)); // Fixes black blobs on the world map albedo = sRGB2Lin(albedo); + half visibility = 1.0; #if defined(SKYLIGHTING) - sh2 skylightingSH = SkylightingTexture[dispatchID.xy]; + float rawDepth = DepthTexture[dispatchID.xy]; + float4 positionCS = float4(2 * float2(uv.x, -uv.y + 1) - 1, rawDepth, 1); + float4 positionMS = mul(CameraViewProjInverse[eyeIndex], positionCS); + positionMS.xyz = positionMS.xyz / positionMS.w; +# if defined(VR) + if (eyeIndex == 1) + positionMS.xyz += CameraPosAdjust[1] - CameraPosAdjust[0]; +# endif - half skylighting = saturate(shUnproject(skylightingSH, normalWS)); + sh2 skylighting = sampleSkylighting(skylightingSettings, SkylightingProbeArray, positionMS.xyz, normalWS); + half skylightingDiffuse = shHallucinateZH3Irradiance(skylighting, skylightingSettings.DirectionalDiffuse ? normalWS : float3(0, 0, 1)); + skylightingDiffuse = lerp(skylightingSettings.MixParams.x, 1, saturate(skylightingDiffuse * skylightingSettings.MixParams.y)); + skylightingDiffuse = applySkylightingFadeout(skylightingDiffuse, length(positionMS.xyz)); - ambient *= lerp(0.25, 1.0, skylighting); + visibility = skylightingDiffuse; #endif #if defined(SSGI) half4 ssgiDiffuse = SSGITexture[dispatchID.xy]; ssgiDiffuse.rgb *= albedo; - ambient *= ssgiDiffuse.a; + visibility = min(visibility, ssgiDiffuse.a); DiffuseAmbientRW[dispatchID.xy] = ambient + ssgiDiffuse.rgb; @@ -65,7 +85,7 @@ RWTexture2D DiffuseAmbientRW : register(u1); diffuseColor += ssgiDiffuse.rgb; #endif - ambient = Lin2sRGB(ambient); + ambient = Lin2sRGB(ambient * visibility); diffuseColor = Lin2sRGB(diffuseColor); diffuseColor += ambient; diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 1c91465dd..9b154d71c 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -114,6 +114,10 @@ struct LightLimitFixSettings uint pad0; }; +# define SL_INCL_STRUCT +# include "Skylighting/Skylighting.hlsli" +# undef SL_INCL_STRUCT + cbuffer FeatureData : register(b6) { GrassLightingSettings grassLightingSettings; @@ -122,6 +126,7 @@ cbuffer FeatureData : register(b6) TerraOccSettings terraOccSettings; WetnessEffects wetnessEffects; LightLimitFixSettings lightLimitFixSettings; + SkylightingSettings skylightingSettings; }; Texture2D TexDepthSampler : register(t20); diff --git a/package/Shaders/DeferredCompositeCS.hlsl b/package/Shaders/DeferredCompositeCS.hlsl index 4d2381667..84659421d 100644 --- a/package/Shaders/DeferredCompositeCS.hlsl +++ b/package/Shaders/DeferredCompositeCS.hlsl @@ -25,8 +25,16 @@ SamplerState LinearSampler : register(s0); #endif #if defined(SKYLIGHTING) -# include "Common/Spherical Harmonics/SphericalHarmonics.hlsli" -Texture2D SkylightingTexture : register(t9); +# define SL_INCL_STRUCT +# define SL_INCL_METHODS +# include "Skylighting/Skylighting.hlsli" + +cbuffer SkylightingCB : register(b1) +{ + SkylightingSettings skylightingSettings; +}; + +Texture3D SkylightingProbeArray : register(t9); #endif [numthreads(8, 8, 1)] void main(uint3 dispatchID @@ -79,25 +87,34 @@ Texture2D SkylightingTexture : register(t9); color += reflectance * specularIrradiance; # elif defined(SKYLIGHTING) - sh2 skylightingSH = SkylightingTexture[dispatchID.xy]; +# if defined(VR) + float3 positionMS = positionWS + (eyeIndex == 1 ? CameraPosAdjust[1] - CameraPosAdjust[0] : 0); +# else + float3 positionMS = positionWS; +# endif + + sh2 skylighting = sampleSkylighting(skylightingSettings, SkylightingProbeArray, positionWS.xyz, normalWS); + sh2 specularLobe = fauxSpecularLobeSH(normalWS, -V, roughness); - half skylighting = saturate(shUnproject(skylightingSH, R)); + half skylightingSpecular = saturate(shFuncProductIntegral(skylighting, specularLobe)); + skylightingSpecular = lerp(skylightingSettings.MixParams.z, 1, saturate(skylightingSpecular * skylightingSettings.MixParams.w)); + skylightingSpecular = applySkylightingFadeout(skylightingSpecular, length(positionWS.xyz)); half3 specularIrradiance = 1; - if (skylighting < 1.0) { + if (skylightingSpecular < 1.0) { specularIrradiance = EnvTexture.SampleLevel(LinearSampler, R, level).xyz; specularIrradiance = sRGB2Lin(specularIrradiance); } half3 specularIrradianceReflections = 1.0; - if (skylighting > 0.0) { + if (skylightingSpecular > 0.0) { specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level).xyz; specularIrradianceReflections = sRGB2Lin(specularIrradianceReflections); } - color += reflectance * lerp(specularIrradiance, specularIrradianceReflections, skylighting); + color += reflectance * lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); # else half3 specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level).xyz; specularIrradianceReflections = sRGB2Lin(specularIrradianceReflections); diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 5d2fe8288..6fde510b4 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -929,7 +929,7 @@ float GetSnowParameterY(float texProjTmp, float alpha) # endif # if defined(SKYLIGHTING) -# define LinearSampler SampColorSampler +# define SL_INCL_METHODS # include "Skylighting/Skylighting.hlsli" # endif @@ -1496,7 +1496,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float nearFactor = smoothstep(4096.0 * 2.5, 0.0, viewPosition.z); # if defined(SKYLIGHTING) - float4 skylightingSH = SkylightingTexture.Load(int3(input.Position.xy, 0)); +# if defined(VR) + float3 positionMSSkylight = input.WorldPosition.xyz + (eyeIndex == 1 ? CameraPosAdjust[1] - CameraPosAdjust[0] : 0); +# else + float3 positionMSSkylight = input.WorldPosition.xyz; +# endif + + sh2 skylightingSH = sampleSkylighting(skylightingSettings, SkylightingProbeArray, positionMSSkylight, worldSpaceNormal); # endif # if defined(WETNESS_EFFECTS) @@ -1514,13 +1520,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float maxOcclusion = 1; float minWetnessAngle = 0; minWetnessAngle = saturate(max(minWetnessValue, worldSpaceNormal.z)); - # if defined(SKYLIGHTING) - float wetnessOcclusion = saturate(shUnproject(skylightingSH, float3(0, 0, -1))); - wetnessOcclusion = saturate(wetnessOcclusion * 1.5); -# endif - - bool raindropOccluded = false; + float wetnessOcclusion = saturate(shUnproject(skylightingSH, float3(0, 0, 1)) * 10); +# endif // SKYLIGHTING float4 raindropInfo = float4(0, 0, 1, 0); if (worldSpaceNormal.z > 0 && wetnessEffects.Raining > 0.0f && wetnessEffects.EnableRaindropFx && @@ -1770,10 +1772,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float3 directionalAmbientColor = mul(DirectionalAmbient, modelNormal); -# if defined(SKYLIGHTING) && !defined(SSGI) - // Will look incorrect on objects which have depth which deviates a lot - float skylighting = saturate(shUnproject(skylightingSH, worldSpaceNormal)); - directionalAmbientColor *= lerp(1.0 / 3.0, 1.0, skylighting); + float3 reflectionDiffuseColor = diffuseColor + directionalAmbientColor; + +# if defined(SKYLIGHTING) + float skylightingDiffuse = shHallucinateZH3Irradiance(skylightingSH, worldSpaceNormal); + skylightingDiffuse = lerp(skylightingSettings.MixParams.x, 1, saturate(skylightingDiffuse * skylightingSettings.MixParams.y)); + skylightingDiffuse = applySkylightingFadeout(skylightingDiffuse, viewPosition.z); + directionalAmbientColor *= skylightingDiffuse; # endif # if !(defined(DEFERRED) && defined(SSGI)) @@ -1842,7 +1847,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace // This also applies fresnel float3 wetnessReflectance = GetWetnessAmbientSpecular(screenUV, wetnessNormal, worldSpaceVertexNormal, worldSpaceViewDirection, 1.0 - wetnessGlossinessSpecular); - ; # if !defined(DEFERRED) wetnessSpecular += wetnessReflectance; @@ -1894,9 +1898,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace specularColor = 0; # endif -# if defined(DEFERRED) - diffuseColor += directionalAmbientColor; -# endif + diffuseColor = reflectionDiffuseColor; # if (defined(ENVMAP) || defined(MULTI_LAYER_PARALLAX) || defined(EYE)) # if defined(DYNAMIC_CUBEMAPS) diff --git a/package/Shaders/Water.hlsl b/package/Shaders/Water.hlsl index bddbd9c5e..955245fc8 100644 --- a/package/Shaders/Water.hlsl +++ b/package/Shaders/Water.hlsl @@ -569,7 +569,7 @@ float GetFresnelValue(float3 normal, float3 viewDirection) } # if defined(SKYLIGHTING) -# define LinearSampler Normals01Sampler +# define SL_INCL_METHODS # include "Skylighting/Skylighting.hlsli" # endif @@ -627,7 +627,14 @@ float3 GetWaterDiffuseColor(PS_INPUT input, float3 normal, float3 viewDirection, float3 refractionDiffuseColorSunlight = refractionDiffuseColor * vl * SunColor.xyz * SunDir.w; # if defined(SKYLIGHTING) - float3 refractionDiffuseColorSkylight = refractionDiffuseColor * lerp(GetVLSkylighting(input.WPosition.xyz, refractionWorldPosition.xyz, screenPosition), 1.0, 0.25); +# if defined(VR) + float3 skylightPosOffset = a_eyeIndex == 1 ? CameraPosAdjust[1] - CameraPosAdjust[0] : 0; +# else + float3 skylightPosOffset = 0; +# endif + float3 refractionDiffuseColorSkylight = + getVLSkylighting(skylightingSettings, SkylightingProbeArray, input.WPosition.xyz + skylightPosOffset, refractionWorldPosition.xyz + skylightPosOffset, screenPosition); + refractionDiffuseColorSkylight = refractionDiffuseColor * lerp(refractionDiffuseColorSkylight, 1.0, 0.25); refractionDiffuseColor = refractionDiffuseColorSkylight; # endif refractionDiffuseColor += refractionDiffuseColorSunlight; diff --git a/src/Deferred.cpp b/src/Deferred.cpp index ed832c2ff..be7627d90 100644 --- a/src/Deferred.cpp +++ b/src/Deferred.cpp @@ -421,6 +421,7 @@ void Deferred::DeferredPasses() interior = sky->mode.get() != RE::Sky::Mode::kFull; auto skylighting = Skylighting::GetSingleton(); + auto ssgi = ScreenSpaceGI::GetSingleton(); auto dispatchCount = Util::GetScreenDispatchCount(); @@ -430,10 +431,14 @@ void Deferred::DeferredPasses() // Ambient Composite { - ID3D11ShaderResourceView* srvs[4]{ + ID3D11Buffer* buffer = skylighting->loaded ? skylighting->skylightingCB->CB() : nullptr; + context->CSSetConstantBuffers(1, 1, &buffer); + + ID3D11ShaderResourceView* srvs[5]{ albedo.SRV, normalRoughness.SRV, - skylighting->loaded ? skylighting->skylightingTexture->srv.get() : nullptr, + skylighting->loaded ? depth.depthSRV : nullptr, + skylighting->loaded ? skylighting->texProbeArray->srv.get() : nullptr, ssgi->loaded ? ssgi->texGI[ssgi->outputGIIdx]->srv.get() : nullptr, }; @@ -446,6 +451,9 @@ void Deferred::DeferredPasses() context->CSSetShader(shader, nullptr, 0); context->Dispatch(dispatchCount.x, dispatchCount.y, 1); + + buffer = nullptr; + context->CSSetConstantBuffers(0, 1, &buffer); } } @@ -461,6 +469,9 @@ void Deferred::DeferredPasses() // Deferred Composite { + ID3D11Buffer* buffer = skylighting->loaded ? skylighting->skylightingCB->CB() : nullptr; + context->CSSetConstantBuffers(1, 1, &buffer); + ID3D11ShaderResourceView* srvs[10]{ specular.SRV, albedo.SRV, @@ -471,7 +482,7 @@ void Deferred::DeferredPasses() dynamicCubemaps->loaded ? reflectance.SRV : nullptr, dynamicCubemaps->loaded ? dynamicCubemaps->envTexture->srv.get() : nullptr, dynamicCubemaps->loaded ? dynamicCubemaps->envReflectionsTexture->srv.get() : nullptr, - dynamicCubemaps->loaded && skylighting->loaded ? skylighting->skylightingTexture->srv.get() : nullptr + dynamicCubemaps->loaded && skylighting->loaded ? skylighting->texProbeArray->srv.get() : nullptr }; if (dynamicCubemaps->loaded) @@ -486,6 +497,9 @@ void Deferred::DeferredPasses() context->CSSetShader(shader, nullptr, 0); context->Dispatch(dispatchCount.x, dispatchCount.y, 1); + + buffer = nullptr; + context->CSSetConstantBuffers(0, 1, &buffer); } // Clear diff --git a/src/FeatureBuffer.cpp b/src/FeatureBuffer.cpp index d2dcd6987..e56dadfb8 100644 --- a/src/FeatureBuffer.cpp +++ b/src/FeatureBuffer.cpp @@ -4,6 +4,7 @@ #include "Features/ExtendedMaterials.h" #include "Features/GrassLighting.h" #include "Features/LightLimitFix.h" +#include "Features/Skylighting.h" #include "Features/TerrainOcclusion.h" #include "Features/WetnessEffects.h" @@ -31,5 +32,6 @@ std::pair GetFeatureBufferData() DynamicCubemaps::GetSingleton()->settings, TerrainOcclusion::GetSingleton()->GetCommonBufferData(), WetnessEffects::GetSingleton()->GetCommonBufferData(), - LightLimitFix::GetSingleton()->GetCommonBufferData()); + LightLimitFix::GetSingleton()->GetCommonBufferData(), + Skylighting::GetSingleton()->cbData); } \ No newline at end of file diff --git a/src/Features/Skylighting.cpp b/src/Features/Skylighting.cpp index ab9e12eb9..2d9df7369 100644 --- a/src/Features/Skylighting.cpp +++ b/src/Features/Skylighting.cpp @@ -1,53 +1,54 @@ #include "Skylighting.h" -#include -#include -#include NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( Skylighting::Settings, - EnableSkylighting, - HeightSkylighting, - MinimumBound, - RenderTrees) + DirectionalDiffuse, + MaxZenith, + MinDiffuseVisibility, + DiffuseBrightness, + MinSpecularVisibility, + SpecularBrightness) + +void Skylighting::LoadSettings(json& o_json) +{ + settings = o_json; +} + +void Skylighting::SaveSettings(json& o_json) +{ + o_json = settings; +} + +void Skylighting::RestoreDefaultSettings() +{ + settings = {}; +} void Skylighting::DrawSettings() { - ImGui::Checkbox("Enable Skylighting", &settings.EnableSkylighting); - ImGui::Checkbox("Enable Height Map Rendering", &settings.HeightSkylighting); - ImGui::SliderFloat("Minimum Bound", &settings.MinimumBound, 1, 256, "%.0f"); - ImGui::Checkbox("Render Trees", &settings.RenderTrees); + ImGui::Checkbox("Directional Diffuse", &settings.DirectionalDiffuse); + if (auto _tt = Util::HoverTooltipWrapper()) + ImGui::Text( + "Extra darkening depending on surface orientation.\n" + "More physically correct, but may impact the intended visual of certain weathers."); + + ImGui::SliderAngle("Max Zenith Angle", &settings.MaxZenith, 0, 90); + if (auto _tt = Util::HoverTooltipWrapper()) + ImGui::Text("Smaller angles creates more focused top-down shadow."); + + ImGui::SliderFloat("Diffuse Min Visibility", &settings.MinDiffuseVisibility, 0, 1, "%.2f"); + ImGui::SliderFloat("Diffuse Brightness", &settings.DiffuseBrightness, 0, 10, "%.1f"); + ImGui::SliderFloat("Specular Min Visibility", &settings.MinSpecularVisibility, 0, 1, "%.2f"); + ImGui::SliderFloat("Specular Brightness", &settings.SpecularBrightness, 0, 10, "%.1f"); } void Skylighting::SetupResources() { - GetSkylightingCS(); - GetSkylightingShadowMapCS(); - auto renderer = RE::BSGraphics::Renderer::GetSingleton(); + auto& device = State::GetSingleton()->device; { - auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; - - D3D11_TEXTURE2D_DESC texDesc{}; - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - - main.texture->GetDesc(&texDesc); - main.SRV->GetDesc(&srvDesc); - main.UAV->GetDesc(&uavDesc); - - texDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; - - srvDesc.Format = texDesc.Format; - uavDesc.Format = texDesc.Format; - - skylightingTexture = new Texture2D(texDesc); - skylightingTexture->CreateSRV(srvDesc); - skylightingTexture->CreateUAV(uavDesc); - - skylightingTempTexture = new Texture2D(texDesc); - skylightingTempTexture->CreateSRV(srvDesc); - skylightingTempTexture->CreateUAV(uavDesc); + skylightingCB = eastl::make_unique(ConstantBufferDesc()); } { @@ -61,331 +62,186 @@ void Skylighting::SetupResources() precipitationOcclusion.depthSRV->GetDesc(&srvDesc); precipitationOcclusion.views[0]->GetDesc(&dsvDesc); - occlusionTexture = new Texture2D(texDesc); - occlusionTexture->CreateSRV(srvDesc); - occlusionTexture->CreateDSV(dsvDesc); + texOcclusion = new Texture2D(texDesc); + texOcclusion->CreateSRV(srvDesc); + texOcclusion->CreateDSV(dsvDesc); } { - perFrameCB = new ConstantBuffer(ConstantBufferDesc()); + D3D11_TEXTURE3D_DESC texDesc{ + .Width = probeArrayDims[0], + .Height = probeArrayDims[1], + .Depth = probeArrayDims[2], + .MipLevels = 1, + .Format = DXGI_FORMAT_R16G16B16A16_FLOAT, + .Usage = D3D11_USAGE_DEFAULT, + .BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS, + .CPUAccessFlags = 0, + .MiscFlags = 0 + }; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = { + .Format = texDesc.Format, + .ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D, + .Texture3D = { + .MostDetailedMip = 0, + .MipLevels = texDesc.MipLevels } + }; + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = { + .Format = texDesc.Format, + .ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D, + .Texture3D = { + .MipSlice = 0, + .FirstWSlice = 0, + .WSize = texDesc.Depth } + }; + + texProbeArray = new Texture3D(texDesc); + texProbeArray->CreateSRV(srvDesc); + texProbeArray->CreateUAV(uavDesc); + + texDesc.Format = srvDesc.Format = uavDesc.Format = DXGI_FORMAT_R16_UINT; + + texAccumFramesArray = new Texture3D(texDesc); + texAccumFramesArray->CreateSRV(srvDesc); + texAccumFramesArray->CreateUAV(uavDesc); } { - auto& device = State::GetSingleton()->device; - auto& context = State::GetSingleton()->context; - - DirectX::CreateDDSTextureFromFile(device, context, L"Data\\Shaders\\Skylighting\\bluenoise.dds", nullptr, &noiseView); - } - - { - auto& device = State::GetSingleton()->device; - - D3D11_SAMPLER_DESC sampDesc; - - sampDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; - sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; - sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; - sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; - sampDesc.MipLODBias = 0.0f; - sampDesc.MaxAnisotropy = 1; - sampDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; - sampDesc.BorderColor[0] = sampDesc.BorderColor[1] = sampDesc.BorderColor[2] = sampDesc.BorderColor[3] = 0; - sampDesc.MinLOD = 0; - sampDesc.MaxLOD = D3D11_FLOAT32_MAX; - - DX::ThrowIfFailed(device->CreateSamplerState(&sampDesc, &comparisonSampler)); - } -} - -void Skylighting::LoadSettings(json& o_json) -{ - settings = o_json; -} - -void Skylighting::SaveSettings(json& o_json) -{ - o_json = settings; -} - -void Skylighting::PostPostLoad() -{ - logger::info("[SKYLIGHTING] Hooking BSLightingShaderProperty::GetPrecipitationOcclusionMapRenderPassesImp"); - stl::write_vfunc<0x2D, BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPassesImpl>(RE::VTABLE_BSLightingShaderProperty[0]); - stl::write_thunk_call(REL::RelocationID(35560, 36559).address() + REL::Relocate(0x3A1, 0x3A1, 0x2FA)); - stl::write_vfunc<0x6, BSUtilityShader_SetupGeometry>(RE::VTABLE_BSUtilityShader[0]); -} - -void Skylighting::RestoreDefaultSettings() -{ - settings = {}; -} - -ID3D11ComputeShader* Skylighting::GetSkylightingCS() -{ - if (!skylightingCS) { - logger::debug("Compiling SkylightingCS"); - skylightingCS = (ID3D11ComputeShader*)Util::CompileShader(L"Data\\Shaders\\Skylighting\\SkylightingCS.hlsl", {}, "cs_5_0"); + D3D11_SAMPLER_DESC samplerDesc = { + .Filter = D3D11_FILTER_MIN_MAG_MIP_POINT, + .AddressU = D3D11_TEXTURE_ADDRESS_CLAMP, + .AddressV = D3D11_TEXTURE_ADDRESS_CLAMP, + .AddressW = D3D11_TEXTURE_ADDRESS_CLAMP, + .MaxAnisotropy = 1, + .MinLOD = 0, + .MaxLOD = D3D11_FLOAT32_MAX + }; + DX::ThrowIfFailed(device->CreateSamplerState(&samplerDesc, pointClampSampler.put())); } - return skylightingCS; -} -ID3D11ComputeShader* Skylighting::GetSkylightingShadowMapCS() -{ - if (!skylightingShadowMapCS) { - logger::debug("Compiling SkylightingCS SHADOWMAP"); - skylightingShadowMapCS = (ID3D11ComputeShader*)Util::CompileShader(L"Data\\Shaders\\Skylighting\\SkylightingCS.hlsl", { { "SHADOWMAP", nullptr } }, "cs_5_0"); - } - return skylightingShadowMapCS; + CompileComputeShaders(); } -ID3D11ComputeShader* Skylighting::GetSkylightingBlurHorizontalCS() +void Skylighting::ClearShaderCache() { - if (!skylightingBlurHorizontalCS) { - logger::debug("Compiling SkylightingBlurCS HORIZONTAL"); - skylightingBlurHorizontalCS = (ID3D11ComputeShader*)Util::CompileShader(L"Data\\Shaders\\Skylighting\\SkylightingBlurCS.hlsl", { { "HORIZONTAL", nullptr } }, "cs_5_0"); - } - return skylightingBlurHorizontalCS; -} + static const std::vector*> shaderPtrs = { + &probeUpdateCompute + }; -ID3D11ComputeShader* Skylighting::GetSkylightingBlurVerticalCS() -{ - if (!skylightingBlurVerticalCS) { - logger::debug("Compiling SkylightingVerticalCS"); - skylightingBlurVerticalCS = (ID3D11ComputeShader*)Util::CompileShader(L"Data\\Shaders\\Skylighting\\SkylightingBlurCS.hlsl", {}, "cs_5_0"); - } - return skylightingBlurVerticalCS; -} + for (auto shader : shaderPtrs) + if (*shader) { + (*shader)->Release(); + shader->detach(); + } -ID3D11PixelShader* Skylighting::GetFoliagePS() -{ - if (!foliagePixelShader) { - logger::debug("Compiling Utility.hlsl"); - foliagePixelShader = (ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\Utility.hlsl", { { "RENDER_DEPTH", "" }, { "FOLIAGE", "" } }, "ps_5_0"); - } - return foliagePixelShader; + CompileComputeShaders(); } -void Skylighting::SkylightingShaderHacks() +void Skylighting::CompileComputeShaders() { - if (inOcclusion) { - auto& context = State::GetSingleton()->context; + struct ShaderCompileInfo + { + winrt::com_ptr* programPtr; + std::string_view filename; + std::vector> defines; + }; - if (foliage) { - context->PSSetShader(GetFoliagePS(), NULL, NULL); - } else { - context->PSSetShader(nullptr, NULL, NULL); - } - } -} + std::vector + shaderInfos = { + { &probeUpdateCompute, "updateProbes.cs.hlsl", {} }, + }; -void Skylighting::ClearShaderCache() -{ - if (skylightingCS) { - skylightingCS->Release(); - skylightingCS = nullptr; - } - if (skylightingShadowMapCS) { - skylightingShadowMapCS->Release(); - skylightingShadowMapCS = nullptr; - } - if (skylightingBlurHorizontalCS) { - skylightingBlurHorizontalCS->Release(); - skylightingBlurHorizontalCS = nullptr; - } - if (skylightingBlurVerticalCS) { - skylightingBlurVerticalCS->Release(); - skylightingBlurVerticalCS = nullptr; - } - if (foliagePixelShader) { - foliagePixelShader->Release(); - foliagePixelShader = nullptr; + for (auto& info : shaderInfos) { + auto path = std::filesystem::path("Data\\Shaders\\Skylighting") / info.filename; + if (auto rawPtr = reinterpret_cast(Util::CompileShader(path.c_str(), info.defines, "cs_5_0"))) + info.programPtr->attach(rawPtr); } } -void Skylighting::Compute() +void Skylighting::Prepass() { - auto state = State::GetSingleton(); - auto& context = state->context; - - if (!settings.EnableSkylighting) { - float clear[4] = { 1, 1, 1, 1 }; - context->ClearUnorderedAccessViewFloat(GetSingleton()->skylightingTexture->uav.get(), clear); - return; - } - - auto renderer = RE::BSGraphics::Renderer::GetSingleton(); - - auto dispatchCount = Util::GetScreenDispatchCount(); - - bool temporal = Util::GetTemporal(); + auto& context = State::GetSingleton()->context; + auto state = RE::BSGraphics::RendererShadowState::GetSingleton(); { - PerFrameCB data{}; - data.OcclusionViewProj = viewProjMat; - data.EyePosition = { eyePosition.x, eyePosition.y, eyePosition.z, 0 }; - - auto shadowSceneNode = RE::BSShaderManager::State::GetSingleton().shadowSceneNode[0]; - auto shadowDirLight = (RE::BSShadowDirectionalLight*)shadowSceneNode->GetRuntimeData().shadowDirLight; - bool dirShadow = shadowDirLight && shadowDirLight->shadowLightIndex == 0; - - if (dirShadow) { - data.ShadowDirection = float4(shadowDirLight->lightDirection.x, shadowDirLight->lightDirection.y, shadowDirLight->lightDirection.z, 0); - } - - data.BufferDim.x = state->screenSize.x; - data.BufferDim.y = state->screenSize.y; - data.BufferDim.z = 1.0f / data.BufferDim.x; - data.BufferDim.w = 1.0f / data.BufferDim.y; - - data.CameraData = Util::GetCameraData(); - - data.FrameCount = temporal ? RE::BSGraphics::State::GetSingleton()->frameCount : 0; - - perFrameCB->Update(data); + static float3 prevCellID = { 0, 0, 0 }; + + auto eyePosNI = !REL::Module::IsVR() ? state->GetRuntimeData().posAdjust.getEye() : state->GetVRRuntimeData().posAdjust.getEye(); + auto eyePos = float3{ eyePosNI.x, eyePosNI.y, eyePosNI.z }; + + float3 cellSize = { + occlusionDistance / probeArrayDims[0], + occlusionDistance / probeArrayDims[1], + occlusionDistance * .5f / probeArrayDims[2] + }; + auto cellID = eyePos / cellSize; + cellID = { round(cellID.x), round(cellID.y), round(cellID.z) }; + auto cellOrigin = cellID * cellSize; + float3 cellIDDiff = prevCellID - cellID; + + cbData = { + .OcclusionViewProj = OcclusionTransform, + .OcclusionDir = OcclusionDir, + .PosOffset = cellOrigin - eyePos, + .ArrayOrigin = { + ((int)cellID.x - probeArrayDims[0] / 2) % probeArrayDims[0], + ((int)cellID.y - probeArrayDims[1] / 2) % probeArrayDims[1], + ((int)cellID.z - probeArrayDims[2] / 2) % probeArrayDims[2], 0 }, + .ValidMargin = { (int)cellIDDiff.x, (int)cellIDDiff.y, (int)cellIDDiff.z }, + .MixParams = { settings.MinDiffuseVisibility, settings.DiffuseBrightness, settings.MinSpecularVisibility, settings.SpecularBrightness }, + .DirectionalDiffuse = settings.DirectionalDiffuse, + }; + + skylightingCB->Update(cbData); + + prevCellID = cellID; } - auto depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kPOST_ZPREPASS_COPY]; - auto terrainBlending = TerrainBlending::GetSingleton(); - - ID3D11ShaderResourceView* srvs[5]{ - terrainBlending->loaded ? terrainBlending->blendedDepthTexture16->srv.get() : depth.depthSRV, - Deferred::GetSingleton()->shadowView, - Deferred::GetSingleton()->perShadow->srv.get(), - noiseView, - occlusionTexture->srv.get() - }; - - context->CSSetShaderResources(0, ARRAYSIZE(srvs), srvs); - - ID3D11UnorderedAccessView* uavs[1]{ skylightingTexture->uav.get() }; - context->CSSetUnorderedAccessViews(0, ARRAYSIZE(uavs), uavs, nullptr); - - auto buffer = perFrameCB->CB(); - context->CSSetConstantBuffers(0, 1, &buffer); - - ID3D11SamplerState* samplers[2] = { Deferred::GetSingleton()->linearSampler, comparisonSampler }; - context->CSSetSamplers(0, ARRAYSIZE(samplers), samplers); + std::array srvs = { texOcclusion->srv.get() }; + std::array uavs = { texProbeArray->uav.get(), texAccumFramesArray->uav.get() }; + std::array samplers = { pointClampSampler.get() }; + auto cb = skylightingCB->CB(); + // update probe array { - REL::Relocation perFrame{ REL::RelocationID(524768, 411384) }; - ID3D11Buffer* buffers[1] = { *perFrame.get() }; - - context->CSSetConstantBuffers(12, ARRAYSIZE(buffers), buffers); + context->CSSetConstantBuffers(1, 1, &cb); + context->CSSetSamplers(0, (uint)samplers.size(), samplers.data()); + context->CSSetShaderResources(0, (uint)srvs.size(), srvs.data()); + context->CSSetUnorderedAccessViews(0, (uint)uavs.size(), uavs.data(), nullptr); + context->CSSetShader(probeUpdateCompute.get(), nullptr, 0); + context->Dispatch((probeArrayDims[0] + 7u) >> 3, (probeArrayDims[1] + 7u) >> 3, probeArrayDims[2]); } - context->CSSetShader(settings.HeightSkylighting ? GetSkylightingCS() : GetSkylightingShadowMapCS(), nullptr, 0); - - context->Dispatch(dispatchCount.x, dispatchCount.y, 1); - - srvs[0] = nullptr; - srvs[1] = nullptr; - srvs[2] = nullptr; - srvs[3] = nullptr; - srvs[4] = nullptr; - context->CSSetShaderResources(0, ARRAYSIZE(srvs), srvs); - - uavs[0] = nullptr; - context->CSSetUnorderedAccessViews(0, ARRAYSIZE(uavs), uavs, nullptr); - - buffer = nullptr; - context->CSSetConstantBuffers(0, 1, &buffer); - - samplers[0] = nullptr; - samplers[1] = nullptr; - context->CSSetSamplers(0, ARRAYSIZE(samplers), samplers); - - context->CSSetShader(nullptr, nullptr, 0); -} - -void Skylighting::ComputeBlur(bool a_horizontal) -{ - auto state = State::GetSingleton(); - auto& context = state->context; - - if (!settings.EnableSkylighting) { - return; + // reset + { + srvs.fill(nullptr); + uavs.fill(nullptr); + samplers.fill(nullptr); + cb = nullptr; + + context->CSSetConstantBuffers(1, 1, &cb); + context->CSSetSamplers(0, (uint)samplers.size(), samplers.data()); + context->CSSetShaderResources(0, (uint)srvs.size(), srvs.data()); + context->CSSetUnorderedAccessViews(0, (uint)uavs.size(), uavs.data(), nullptr); + context->CSSetShader(nullptr, nullptr, 0); } - auto renderer = RE::BSGraphics::Renderer::GetSingleton(); - - auto dispatchCount = Util::GetScreenDispatchCount(); - - auto depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kPOST_ZPREPASS_COPY]; - - auto terrainBlending = TerrainBlending::GetSingleton(); - - ID3D11ShaderResourceView* srvs[2]{ - terrainBlending->loaded ? terrainBlending->blendedDepthTexture16->srv.get() : depth.depthSRV, - (a_horizontal ? skylightingTexture : skylightingTempTexture)->srv.get() - }; - - context->CSSetShaderResources(0, ARRAYSIZE(srvs), srvs); - - ID3D11UnorderedAccessView* uavs[1]{ - (!a_horizontal ? skylightingTexture : skylightingTempTexture)->uav.get() - }; - - context->CSSetUnorderedAccessViews(0, ARRAYSIZE(uavs), uavs, nullptr); - - auto buffer = perFrameCB->CB(); - context->CSSetConstantBuffers(0, 1, &buffer); - - ID3D11SamplerState* samplers[1] = { Deferred::GetSingleton()->linearSampler }; - context->CSSetSamplers(0, ARRAYSIZE(samplers), samplers); - + // set PS shader resource { - REL::Relocation perFrame{ REL::RelocationID(524768, 411384) }; - ID3D11Buffer* buffers[1] = { *perFrame.get() }; - - context->CSSetConstantBuffers(12, ARRAYSIZE(buffers), buffers); + ID3D11ShaderResourceView* srv = texProbeArray->srv.get(); + context->PSSetShaderResources(29, 1, &srv); } - - context->CSSetShader(a_horizontal ? GetSkylightingBlurHorizontalCS() : GetSkylightingBlurVerticalCS(), nullptr, 0); - - context->Dispatch(dispatchCount.x, dispatchCount.y, 1); - - srvs[0] = nullptr; - srvs[1] = nullptr; - context->CSSetShaderResources(0, ARRAYSIZE(srvs), srvs); - - uavs[0] = nullptr; - context->CSSetUnorderedAccessViews(0, ARRAYSIZE(uavs), uavs, nullptr); - - buffer = nullptr; - context->CSSetConstantBuffers(0, 1, &buffer); - - samplers[0] = nullptr; - context->CSSetSamplers(0, ARRAYSIZE(samplers), samplers); - - context->CSSetShader(nullptr, nullptr, 0); } -void Skylighting::Prepass() -{ - Compute(); - ComputeBlur(true); - ComputeBlur(false); - Bind(); -} - -void Skylighting::Bind() +void Skylighting::PostPostLoad() { - auto state = State::GetSingleton(); - auto& context = state->context; - - auto buffer = perFrameCB->CB(); - context->PSSetConstantBuffers(8, 1, &buffer); - - ID3D11ShaderResourceView* srvs[2]{ - occlusionTexture->srv.get(), - skylightingTexture->srv.get(), - }; - - context->PSSetShaderResources(29, ARRAYSIZE(srvs), srvs); + logger::info("[SKYLIGHTING] Hooking BSLightingShaderProperty::GetPrecipitationOcclusionMapRenderPassesImp"); + stl::write_vfunc<0x2D, BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPassesImpl>(RE::VTABLE_BSLightingShaderProperty[0]); + stl::write_thunk_call(REL::RelocationID(35560, 36559).address() + REL::Relocate(0x3A1, 0x3A1, 0x2FA)); + stl::write_thunk_call(REL::RelocationID(25643, 26185).address() + REL::Relocate(0x5D9, 0x59D, 0x5DC)); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// RE Code +////////////////////////////////////////////////////////////// struct BSParticleShaderRainEmitter { @@ -393,20 +249,6 @@ struct BSParticleShaderRainEmitter char _pad_8[4056]; }; -static void Precipitation_SetupMask(RE::Precipitation* a_This) -{ - using func_t = decltype(&Precipitation_SetupMask); - REL::Relocation func{ REL::RelocationID(25641, 26183) }; - func(a_This); -} - -static void Precipitation_RenderMask(RE::Precipitation* a_This, BSParticleShaderRainEmitter* a_emitter) -{ - using func_t = decltype(&Precipitation_RenderMask); - REL::Relocation func{ REL::RelocationID(25642, 26184) }; - func(a_This, a_emitter); -} - enum class ShaderTechnique { // Sky @@ -616,11 +458,27 @@ class BSBatchRenderer void* unk2; }; -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Hooks +////////////////////////////////////////////////////////////// + +static void Precipitation_SetupMask(RE::Precipitation* a_This) +{ + using func_t = decltype(&Precipitation_SetupMask); + REL::Relocation func{ REL::RelocationID(25641, 26183) }; + func(a_This); +} + +static void Precipitation_RenderMask(RE::Precipitation* a_This, BSParticleShaderRainEmitter* a_emitter) +{ + using func_t = decltype(&Precipitation_RenderMask); + REL::Relocation func{ REL::RelocationID(25642, 26184) }; + func(a_This, a_emitter); +} void* Skylighting::BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPassesImpl::thunk( - RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, [[maybe_unused]] uint32_t renderMode, [[maybe_unused]] RE::BSGraphics::BSShaderAccumulator* accumulator) + RE::BSLightingShaderProperty* property, + RE::BSGeometry* geometry, + [[maybe_unused]] uint32_t renderMode, + [[maybe_unused]] RE::BSGraphics::BSShaderAccumulator* accumulator) { auto batch = (BSBatchRenderer*)accumulator->GetRuntimeData().batchRenderer; batch->m_GeometryGroups[14]->m_Flags &= ~1; @@ -631,18 +489,16 @@ void* Skylighting::BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPa auto* precipitationOcclusionMapRenderPassList = reinterpret_cast(&property->unk0C8); precipitationOcclusionMapRenderPassList->Clear(); + if (GetSingleton()->inOcclusion && !GetSingleton()->renderTrees) { + if (property->flags.any(kSkinned) && property->flags.none(kTreeAnim)) + return precipitationOcclusionMapRenderPassList; + } else { + if (property->flags.any(kSkinned)) + return precipitationOcclusionMapRenderPassList; + } - if (!GetSingleton()->inOcclusion && property->flags.any(kSkinned, kTreeAnim) && !GetSingleton()->settings.RenderTrees) - return precipitationOcclusionMapRenderPassList; - - if (property->flags.any(kSkinned) && !property->flags.any(kTreeAnim)) - return precipitationOcclusionMapRenderPassList; - - if (!GetSingleton()->settings.RenderTrees && property->flags.any(kTreeAnim)) - return precipitationOcclusionMapRenderPassList; - - if (property->flags.any(kZBufferWrite) && property->flags.none(kRefraction, kTempRefraction, kMultiTextureLandscape, kNoLODLandBlend, kLODLandscape, kEyeReflect, kDecal, kDynamicDecal, kAnisotropicLighting)) { - if (geometry->worldBound.radius > GetSingleton()->settings.MinimumBound) { + if (property->flags.any(kZBufferWrite) && property->flags.none(kRefraction, kTempRefraction, kMultiTextureLandscape, kNoLODLandBlend, kLODLandscape, kEyeReflect, kDecal, kDynamicDecal, kAnisotropicLighting) && !(property->flags.any(kSkinned) && property->flags.none(kTreeAnim))) { + if (geometry->worldBound.radius > GetSingleton()->boundSize) { stl::enumeration technique; technique.set(RenderDepth); @@ -656,7 +512,7 @@ void* Skylighting::BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPa } } return precipitationOcclusionMapRenderPassList; -}; +} void Skylighting::Main_Precipitation_RenderOcclusion::thunk() { @@ -668,8 +524,11 @@ void Skylighting::Main_Precipitation_RenderOcclusion::thunk() auto sky = RE::Sky::GetSingleton(); auto precip = sky->precip; - if (GetSingleton()->settings.EnableSkylighting && GetSingleton()->settings.HeightSkylighting) { - if (doPrecip) { + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + GetSingleton()->eyePosition = shadowState->GetRuntimeData().posAdjust.getEye(); + + if (GetSingleton()->doOcclusion) { + { doPrecip = false; auto precipObject = precip->currentPrecip; @@ -686,75 +545,120 @@ void Skylighting::Main_Precipitation_RenderOcclusion::thunk() Precipitation_RenderMask(precip, rain); } + } - } else { + { doPrecip = true; - auto singleton = GetSingleton(); + auto renderer = RE::BSGraphics::Renderer::GetSingleton(); + auto& precipitation = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kPRECIPITATION_OCCLUSION_MAP]; + RE::BSGraphics::DepthStencilData precipitationCopy = precipitation; - std::chrono::time_point currentTimer = std::chrono::system_clock::now(); - auto timePassed = std::chrono::duration_cast(currentTimer - singleton->lastUpdateTimer).count(); + precipitation.depthSRV = GetSingleton()->texOcclusion->srv.get(); + precipitation.texture = GetSingleton()->texOcclusion->resource.get(); + precipitation.views[0] = GetSingleton()->texOcclusion->dsv.get(); - if (timePassed >= (1000.0f / 30.0f)) { - singleton->lastUpdateTimer = currentTimer; + static float& PrecipitationShaderCubeSize = (*(float*)REL::RelocationID(515451, 401590).address()); + float originalPrecipitationShaderCubeSize = PrecipitationShaderCubeSize; - singleton->eyePosition = Util::GetEyePosition(0); + static RE::NiPoint3& PrecipitationShaderDirection = (*(RE::NiPoint3*)REL::RelocationID(515509, 401648).address()); + RE::NiPoint3 originalParticleShaderDirection = PrecipitationShaderDirection; - auto renderer = RE::BSGraphics::Renderer::GetSingleton(); - auto& precipitation = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kPRECIPITATION_OCCLUSION_MAP]; - RE::BSGraphics::DepthStencilData precipitationCopy = precipitation; + GetSingleton()->inOcclusion = true; + PrecipitationShaderCubeSize = GetSingleton()->occlusionDistance; - precipitation.depthSRV = singleton->occlusionTexture->srv.get(); - precipitation.texture = singleton->occlusionTexture->resource.get(); - precipitation.views[0] = singleton->occlusionTexture->dsv.get(); + float originaLastCubeSize = precip->lastCubeSize; + precip->lastCubeSize = PrecipitationShaderCubeSize; - static float& PrecipitationShaderCubeSize = (*(float*)REL::RelocationID(515451, 401590).address()); - float originalPrecipitationShaderCubeSize = PrecipitationShaderCubeSize; + float2 vPoint; + { + constexpr float rcpRandMax = 1.f / RAND_MAX; + static int randSeed = std::rand(); + static uint randFrameCount = 0; - static RE::NiPoint3& PrecipitationShaderDirection = (*(RE::NiPoint3*)REL::RelocationID(515509, 401648).address()); - RE::NiPoint3 originalParticleShaderDirection = PrecipitationShaderDirection; + // r2 sequence + vPoint = float2(randSeed * rcpRandMax) + (float)randFrameCount * float2(0.245122333753, 0.430159709002); + vPoint.x -= static_cast(vPoint.x); + vPoint.y -= static_cast(vPoint.y); - singleton->inOcclusion = true; - PrecipitationShaderCubeSize = 10000; + randFrameCount++; + if (randFrameCount == 9999) { + randFrameCount = 0; + randSeed = std::rand(); + } - float originaLastCubeSize = precip->lastCubeSize; - precip->lastCubeSize = PrecipitationShaderCubeSize; + // disc transformation + vPoint.x = sqrt(vPoint.x * sin(GetSingleton()->settings.MaxZenith)); + vPoint.y *= 6.28318530718; - PrecipitationShaderDirection = { 0, 0, -1 }; + vPoint = { vPoint.x * cos(vPoint.y), vPoint.x * sin(vPoint.y) }; + } - Precipitation_SetupMask(precip); - Precipitation_SetupMask(precip); // Calling setup twice fixes an issue when it is raining + float3 PrecipitationShaderDirectionF = -float3{ vPoint.x, vPoint.y, sqrt(1 - vPoint.LengthSquared()) }; + PrecipitationShaderDirectionF.Normalize(); - BSParticleShaderRainEmitter* rain = new BSParticleShaderRainEmitter; - Precipitation_RenderMask(precip, rain); - singleton->inOcclusion = false; - RE::BSParticleShaderCubeEmitter* cube = (RE::BSParticleShaderCubeEmitter*)rain; - singleton->viewProjMat = cube->occlusionProjection; + PrecipitationShaderDirection = { PrecipitationShaderDirectionF.x, PrecipitationShaderDirectionF.y, PrecipitationShaderDirectionF.z }; - cube = nullptr; - delete rain; + Precipitation_SetupMask(precip); + Precipitation_SetupMask(precip); // Calling setup twice fixes an issue when it is raining - PrecipitationShaderCubeSize = originalPrecipitationShaderCubeSize; - precip->lastCubeSize = originaLastCubeSize; + BSParticleShaderRainEmitter* rain = new BSParticleShaderRainEmitter; + Precipitation_RenderMask(precip, rain); + GetSingleton()->inOcclusion = false; + RE::BSParticleShaderCubeEmitter* cube = (RE::BSParticleShaderCubeEmitter*)rain; - PrecipitationShaderDirection = originalParticleShaderDirection; + GetSingleton()->OcclusionDir = -float4{ PrecipitationShaderDirectionF.x, PrecipitationShaderDirectionF.y, PrecipitationShaderDirectionF.z, 0 }; + GetSingleton()->OcclusionTransform = cube->occlusionProjection; - precipitation = precipitationCopy; + cube = nullptr; + delete rain; - singleton->foliage = false; - } + PrecipitationShaderCubeSize = originalPrecipitationShaderCubeSize; + precip->lastCubeSize = originaLastCubeSize; + + PrecipitationShaderDirection = originalParticleShaderDirection; + + precipitation = precipitationCopy; } - } else { - func(); } State::GetSingleton()->EndPerfEvent(); } void Skylighting::BSUtilityShader_SetupGeometry::thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags) { - // update foliage - if (GetSingleton()->inOcclusion) { - GetSingleton()->foliage = Pass->shaderProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kTreeAnim); + auto& feat = *GetSingleton(); + if (feat.inOcclusion) { + auto renderer = RE::BSGraphics::Renderer::GetSingleton(); + auto& precipitation = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kPRECIPITATION_OCCLUSION_MAP]; + + precipitation.depthSRV = feat.texOcclusion->srv.get(); + precipitation.texture = feat.texOcclusion->resource.get(); + precipitation.views[0] = feat.texOcclusion->dsv.get(); + + auto state = RE::BSGraphics::RendererShadowState::GetSingleton(); + GET_INSTANCE_MEMBER(stateUpdateFlags, state) + stateUpdateFlags.set(RE::BSGraphics::ShaderFlags::DIRTY_RENDERTARGET); } + func(This, Pass, RenderFlags); -} \ No newline at end of file +} + +void Skylighting::SetViewFrustum::thunk(RE::NiCamera* a_camera, RE::NiFrustum* a_frustum) +{ + if (GetSingleton()->inOcclusion) { + static float frameCount = 0; + + uint corner = (uint)frameCount % 4; + + a_frustum->fBottom = (corner == 0 || corner == 1) ? -5000.0f : 0.0f; + + a_frustum->fLeft = (corner == 0 || corner == 2) ? -5000.0f : 0.0f; + a_frustum->fRight = (corner == 1 || corner == 3) ? 5000.0f : 0.0f; + + a_frustum->fTop = (corner == 2 || corner == 3) ? 5000.0f : 0.0f; + + frameCount += 0.5f; + } + + func(a_camera, a_frustum); +} diff --git a/src/Features/Skylighting.h b/src/Features/Skylighting.h index cbb9c6967..54ecf0226 100644 --- a/src/Features/Skylighting.h +++ b/src/Features/Skylighting.h @@ -7,7 +7,6 @@ struct Skylighting : Feature { -public: static Skylighting* GetSingleton() { static Skylighting singleton; @@ -21,82 +20,75 @@ struct Skylighting : Feature virtual inline std::string_view GetShaderDefineName() override { return "SKYLIGHTING"; } virtual bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - virtual void SetupResources() override; - + virtual void RestoreDefaultSettings() override; virtual void DrawSettings() override; virtual void LoadSettings(json& o_json) override; virtual void SaveSettings(json& o_json) override; - virtual void PostPostLoad() override; - - virtual void Prepass() override; - - virtual void RestoreDefaultSettings() override; - - ID3D11ComputeShader* GetSkylightingCS(); - ID3D11ComputeShader* GetSkylightingShadowMapCS(); - ID3D11ComputeShader* GetSkylightingBlurHorizontalCS(); - ID3D11ComputeShader* GetSkylightingBlurVerticalCS(); + virtual inline void Reset() override{}; + virtual void SetupResources() override; virtual void ClearShaderCache() override; + void CompileComputeShaders(); - void Bind(); - - void Compute(); - void ComputeBlur(bool a_horizontal); - - ID3D11PixelShader* GetFoliagePS(); + virtual void Prepass() override; - void SkylightingShaderHacks(); // referenced in State.cpp + virtual void PostPostLoad() override; ////////////////////////////////////////////////////////////////////////////////// - struct alignas(16) PerFrameCB + struct Settings + { + bool DirectionalDiffuse = true; + float MaxZenith = 3.1415926f / 3.f; // 60 deg + float MinDiffuseVisibility = 0.1; + float DiffuseBrightness = 4; + float MinSpecularVisibility = 0; + float SpecularBrightness = 4; + } settings; + + struct SkylightingCB { REX::W32::XMFLOAT4X4 OcclusionViewProj; - float4 EyePosition; - float4 ShadowDirection; - float4 BufferDim; - float4 CameraData; - uint FrameCount; - uint pad0[3]; - }; + float4 OcclusionDir; - ConstantBuffer* perFrameCB = nullptr; + float3 PosOffset; // cell origin in camera model space + float _pad0; + uint ArrayOrigin[4]; + int ValidMargin[4]; - struct Settings - { - bool EnableSkylighting = true; - bool HeightSkylighting = true; - float MinimumBound = 128; - bool RenderTrees = false; - }; + float4 MixParams; // x: min diffuse visibility, y: diffuse mult, z: min specular visibility, w: specular mult - Settings settings; + uint DirectionalDiffuse; + float3 _pad1; + } cbData; + static_assert(sizeof(SkylightingCB) % 16 == 0); + eastl::unique_ptr skylightingCB = nullptr; - Texture2D* skylightingTexture = nullptr; - Texture2D* skylightingTempTexture = nullptr; - ID3D11ShaderResourceView* noiseView = nullptr; - Texture2D* occlusionTexture = nullptr; + winrt::com_ptr pointClampSampler = nullptr; - ID3D11ComputeShader* skylightingCS = nullptr; - ID3D11ComputeShader* skylightingShadowMapCS = nullptr; - ID3D11ComputeShader* skylightingBlurHorizontalCS = nullptr; - ID3D11ComputeShader* skylightingBlurVerticalCS = nullptr; + Texture2D* texOcclusion = nullptr; + Texture3D* texProbeArray = nullptr; + Texture3D* texAccumFramesArray = nullptr; - ID3D11SamplerState* comparisonSampler; + winrt::com_ptr probeUpdateCompute = nullptr; - ID3D11PixelShader* foliagePixelShader = nullptr; + // misc parameters + bool doOcclusion = true; + uint probeArrayDims[3] = { 128, 128, 64 }; + float occlusionDistance = 8192.f; + bool renderTrees = false; + float boundSize = 128; // cached variables bool inOcclusion = false; - bool foliage = false; - REX::W32::XMFLOAT4X4 viewProjMat; - RE::NiPoint3 eyePosition; - std::chrono::time_point lastUpdateTimer = std::chrono::system_clock::now(); + RE::NiPoint3 eyePosition{}; + REX::W32::XMFLOAT4X4 OcclusionTransform; + float4 OcclusionDir; ////////////////////////////////////////////////////////////////////////////////// + // Hooks struct BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPassesImpl { static void* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, uint32_t renderMode, RE::BSGraphics::BSShaderAccumulator* accumulator); @@ -114,4 +106,10 @@ struct Skylighting : Feature static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); static inline REL::Relocation func; }; -}; + + struct SetViewFrustum + { + static void thunk(RE::NiCamera* a_camera, RE::NiFrustum* a_frustum); + static inline REL::Relocation func; + }; +}; \ No newline at end of file diff --git a/src/State.cpp b/src/State.cpp index 21db12a3b..2039169c2 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -23,9 +23,9 @@ void State::Draw() if (terrainBlending->loaded) terrainBlending->TerrainShaderHacks(); - auto skylighting = Skylighting::GetSingleton(); - if (skylighting->loaded) - skylighting->SkylightingShaderHacks(); + // auto skylighting = Skylighting::GetSingleton(); + // if (skylighting->loaded) + // skylighting->SkylightingShaderHacks(); if (currentShader && updateShader) { auto type = currentShader->shaderType.get();