diff --git a/Content/Shaders/GlobalSurfaceAtlas.flax b/Content/Shaders/GlobalSurfaceAtlas.flax index f08b3e829..038465b36 100644 --- a/Content/Shaders/GlobalSurfaceAtlas.flax +++ b/Content/Shaders/GlobalSurfaceAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f6d1ff40e046e496fe4a550cabf61e180cc46d5045798463d11aa1863578539 -size 5969 +oid sha256:da4ac86323889084590fe12325b00acae1294dbdf2e28077f3af5a93ea7a7162 +size 7125 diff --git a/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp index 5f6d72036..b7a8148de 100644 --- a/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp @@ -32,7 +32,8 @@ PACK_STRUCT(struct Data0 { Vector3 ViewWorldPos; float ViewNearPlane; - Vector3 Padding00; + Vector2 Padding00; + float LightShadowsStrength; float ViewFarPlane; Vector4 ViewFrustumWorldRays[4]; GlobalSignDistanceFieldPass::GlobalSDFData GlobalSDF; @@ -51,7 +52,7 @@ PACK_STRUCT(struct AtlasTileVertex struct GlobalSurfaceAtlasTile : RectPack { Vector3 ViewDirection; - Vector3 ViewPosition; // TODO: use from ViewMatrix + Vector3 ViewPosition; Vector3 ViewBoundsSize; Matrix ViewMatrix; @@ -428,7 +429,6 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // Insert tile into atlas auto* tile = surfaceAtlasData.AtlasTiles->Insert(tileResolution, tileResolution, 0, &surfaceAtlasData, e.Actor, tileIndex); - // TODO: try to perform atlas defragmentation if it's full (eg. max once per ~10s) if (tile) { if (!object) @@ -680,7 +680,10 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // Copy emissive light into the final direct lighting atlas // TODO: test perf diff when manually copying only dirty object tiles and dirty light tiles - context->CopyTexture(surfaceAtlasData.AtlasDirectLight, 0, 0, 0, 0, surfaceAtlasData.AtlasEmissive, 0); + { + PROFILE_GPU_CPU("Copy Emissive"); + context->CopyTexture(surfaceAtlasData.AtlasDirectLight, 0, 0, 0, 0, surfaceAtlasData.AtlasEmissive, 0); + } context->SetViewportAndScissors(Viewport(0, 0, resolution, resolution)); context->SetRenderTarget(surfaceAtlasData.AtlasDirectLight->View()); @@ -689,6 +692,11 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co context->BindSR(2, surfaceAtlasData.AtlasGBuffer2->View()); context->BindSR(3, surfaceAtlasData.AtlasDepth->View()); context->BindSR(4, surfaceAtlasData.ObjectsBuffer.GetBuffer()->View()); + for (int32 i = 0; i < 4; i++) + { + context->BindSR(i + 5, bindingDataSDF.Cascades[i]->ViewVolume()); + context->BindSR(i + 9, bindingDataSDF.CascadeMips[i]->ViewVolume()); + } context->BindCB(0, _cb0); Data0 data; data.ViewWorldPos = renderContext.View.Position; @@ -717,6 +725,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co const bool useShadow = CanRenderShadow(renderContext.View, light); // TODO: test perf/quality when using Shadow Map for directional light (ShadowsPass::Instance()->LastDirLightShadowMap) instead of Global SDF trace light.SetupLightData(&data.Light, useShadow); + data.LightShadowsStrength = 1.0f - light.ShadowsStrength; context->UpdateCB(_cb0, &data); context->SetState(_psDirectLighting0); VB_DRAW(); @@ -743,6 +752,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // Draw draw light const bool useShadow = CanRenderShadow(renderContext.View, light); light.SetupLightData(&data.Light, useShadow); + data.LightShadowsStrength = 1.0f - light.ShadowsStrength; context->UpdateCB(_cb0, &data); context->SetState(_psDirectLighting1); VB_DRAW(); @@ -769,6 +779,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // Draw draw light const bool useShadow = CanRenderShadow(renderContext.View, light); light.SetupLightData(&data.Light, useShadow); + data.LightShadowsStrength = 1.0f - light.ShadowsStrength; context->UpdateCB(_cb0, &data); context->SetState(_psDirectLighting1); VB_DRAW(); @@ -779,6 +790,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // TODO: indirect lighting apply to get infinite bounces for GI + // TODO: explore atlas tiles optimization with feedback from renderer (eg. when tile is sampled by GI/Reflections mark it as used, then sort tiles by importance and prioritize updates for ones frequently used) + #undef WRITE_TILE return false; } diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 4f02acff2..b27938951 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -6,6 +6,7 @@ #define GLOBAL_SDF_RASTERIZE_CHUNK_SIZE 32 #define GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN 4 #define GLOBAL_SDF_MIP_FLOODS 5 +#define GLOBAL_SDF_WORLD_SIZE 60000.0f // Global SDF data for a constant buffer struct GlobalSDFData @@ -61,7 +62,7 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 { float distance = data.CascadePosDistance[3].w * 2.0f; if (distance <= 0.0f) - return 60000; + return GLOBAL_SDF_WORLD_SIZE; UNROLL for (uint cascade = 0; cascade < 4; cascade++) { @@ -83,7 +84,7 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition, out float distance) { float3 gradient = float3(0, 0.00001f, 0); - distance = 60000; + distance = GLOBAL_SDF_WORLD_SIZE; if (data.CascadePosDistance[3].w <= 0.0f) return gradient; UNROLL diff --git a/Source/Shaders/GlobalSurfaceAtlas.shader b/Source/Shaders/GlobalSurfaceAtlas.shader index 8d1286b18..e866e1a3a 100644 --- a/Source/Shaders/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GlobalSurfaceAtlas.shader @@ -12,7 +12,8 @@ META_CB_BEGIN(0, Data) float3 ViewWorldPos; float ViewNearPlane; -float3 Padding00; +float2 Padding00; +float LightShadowsStrengthOneMinus; float ViewFarPlane; float4 ViewFrustumWorldRays[4]; GlobalSDFData GlobalSDF; @@ -66,6 +67,8 @@ void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out fl // GBuffer+Depth at 0-3 slots Buffer GlobalSurfaceAtlasObjects : register(t4); +Texture3D GlobalSDFTex[4] : register(t5); +Texture3D GlobalSDFMip[4] : register(t9); // Pixel shader for Global Surface Atlas shading with direct light contribution META_PS(true, FEATURE_LEVEL_SM5) @@ -81,11 +84,10 @@ float4 PS_DirectLighting(AtlasVertexOutput input) : SV_Target // Load GBuffer sample from atlas GBufferData gBufferData = (GBufferData)0; GBufferSample gBuffer = SampleGBuffer(gBufferData, atlasUV); - - // Skip unlit pixels BRANCH if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT) { + // Skip unlit pixels discard; return 0; } @@ -102,11 +104,43 @@ float4 PS_DirectLighting(AtlasVertexOutput input) : SV_Target float4x4 tileLocalToWorld = Inverse(tile.WorldToLocal); gBuffer.WorldPos = mul(float4(gBufferTilePos, 1), tileLocalToWorld).xyz; + // Calculate shadowing + float3 L = Light.Direction; +#if RADIAL_LIGHT + float3 toLight = Light.Position - gBuffer.WorldPos; + float toLightDst = length(toLight); + if (toLightDst >= Light.Radius) + { + // Skip texels outside the light influence range + discard; + return 0; + } + L = toLight / toLightDst; +#else + float toLightDst = GLOBAL_SDF_WORLD_SIZE; +#endif float4 shadowMask = 1; - BRANCH if (Light.CastShadows > 0) { - // TODO: calculate shadow for the light (use Global SDF) + float NoL = dot(gBuffer.Normal, L); + float shadowBias = 10.0f; + float bias = 2 * shadowBias * saturate(1 - NoL) + shadowBias; + BRANCH + if (NoL > 0) + { + // TODO: try using shadow map for on-screen pixels + // TODO: try using cone trace with Global SDF for smoother shadow (eg. for sun shadows or for area lights) + + // Shot a ray from light into texel to see if there is any occluder + GlobalSDFTrace trace; + trace.Init(gBuffer.WorldPos + gBuffer.Normal * shadowBias, L, bias, toLightDst - bias); + GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); + shadowMask = hit.IsHit() ? LightShadowsStrengthOneMinus : 1; + } + else + { + shadowMask = 0; + } } // Calculate lighting