Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: complex skylighting #348

Merged
merged 26 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
47b7896
feat: basic diffuse skylighting with probe clipmap
Pentalimbed Jul 19, 2024
d9e636f
style: 🎨 apply clang-format changes
Pentalimbed Jul 20, 2024
c5706cc
feat: proper clipmap update and fadeout
Pentalimbed Jul 20, 2024
07a7396
fix: fix out-of-camera fadeout
Pentalimbed Jul 20, 2024
44bf520
feat: faux specular skylighting
Pentalimbed Jul 21, 2024
67ec1c7
feat: added skylighting visual settings
Pentalimbed Jul 21, 2024
67775f7
feat: add rain occlusion
Pentalimbed Jul 21, 2024
1206861
fix: fix VR address
Pentalimbed Jul 21, 2024
d8eae8b
refactor: `updateProbes` uses the header
Pentalimbed Jul 21, 2024
a20fb73
feat: skylight vl ig
Pentalimbed Jul 21, 2024
e852a8e
feat: true average in place of exponential average
Pentalimbed Jul 21, 2024
ff278dc
fix: cell invalidation fix, enabled optimisation hook
Pentalimbed Jul 22, 2024
a547f5e
fix: fixed cell invalidation...again
Pentalimbed Jul 22, 2024
4c516d7
chore: update with `dev` changes
Pentalimbed Jul 22, 2024
240117f
feat: add DirectionalDiffuse option
Pentalimbed Jul 22, 2024
f418b9f
feat: potential VR support for skylighting
Pentalimbed Jul 22, 2024
2c0000c
fix: toggle skylighting VR
Pentalimbed Jul 22, 2024
96fb887
fix: skylighting forward rendering
doodlum Jul 23, 2024
a4c1541
Merge branch 'fancy-skylighting' of https://github.com/Pentalimbed/sk…
doodlum Jul 23, 2024
be0b8f6
Merge branch 'dev' into fancy-skylighting
Pentalimbed Jul 23, 2024
eaa7b3b
chore: update to `dev` standards
Pentalimbed Jul 23, 2024
2af0e3b
chore: remove `SkylightingBlurCS.hlsl`
Pentalimbed Jul 23, 2024
2a7985a
chore: replace sobol with r2 due to licensing concerns
Pentalimbed Jul 24, 2024
bddabf7
chore: add `RestoreDefaultSettings`
Pentalimbed Jul 24, 2024
e7d62f3
chore: remove test lines
Pentalimbed Jul 24, 2024
042ccb6
chore: changed AO blending to prevent over darkening
Pentalimbed Jul 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions features/Grass Lighting/Shaders/RunGrass.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down
197 changes: 110 additions & 87 deletions features/Skylighting/Shaders/Skylighting/Skylighting.hlsli
Original file line number Diff line number Diff line change
@@ -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<unorm float> OcclusionMapSampler : register(t29);
Texture2D<float4> 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<sh2> 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<sh2> 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<sh2> 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
Loading
Loading