From 787e7b423ce3d2832a4fdfaeed22bb4e4ab886e5 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Tue, 29 Mar 2022 15:02:27 +0200 Subject: [PATCH] Add additional HitNormal feature to Global SDF trace output --- Source/Shaders/GlobalSignDistanceField.hlsl | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index bb33316bc..4f02acff2 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -24,6 +24,7 @@ struct GlobalSDFTrace float3 WorldDirection; float MaxDistance; float StepScale; + bool NeedsHitNormal; void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f) { @@ -32,12 +33,14 @@ struct GlobalSDFTrace MinDistance = minDistance; MaxDistance = maxDistance; StepScale = stepScale; + NeedsHitNormal = false; } }; // Global SDF ray trace hit information. struct GlobalSDFHit { + float3 HitNormal; float HitTime; uint HitCascade; uint StepsCount; @@ -54,13 +57,13 @@ struct GlobalSDFHit }; // Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location. -float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition, uint minCascade = 0) +float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition) { float distance = data.CascadePosDistance[3].w * 2.0f; if (distance <= 0.0f) return 60000; UNROLL - for (uint cascade = minCascade; cascade < 4; cascade++) + for (uint cascade = 0; cascade < 4; cascade++) { float4 cascadePosDistance = data.CascadePosDistance[cascade]; float cascadeMaxDistance = cascadePosDistance.w * 2; @@ -77,14 +80,14 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 } // Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector. -float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition, out float distance, uint minCascade = 0) +float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition, out float distance) { float3 gradient = float3(0, 0.00001f, 0); distance = 60000; if (data.CascadePosDistance[3].w <= 0.0f) return gradient; UNROLL - for (uint cascade = minCascade; cascade < 4; cascade++) + for (uint cascade = 0; cascade < 4; cascade++) { float4 cascadePosDistance = data.CascadePosDistance[cascade]; float cascadeMaxDistance = cascadePosDistance.w * 2; @@ -109,19 +112,17 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4] } // Ray traces the Global SDF. -GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4], Texture3D mips[4], const GlobalSDFTrace trace, uint minCascade = 0) +GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4], Texture3D mips[4], const GlobalSDFTrace trace) { - GlobalSDFHit hit; + GlobalSDFHit hit = (GlobalSDFHit)0; hit.HitTime = -1.0f; - hit.HitCascade = 0; - hit.StepsCount = 0; float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1) float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1) float nextIntersectionStart = 0.0f; float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2); float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance; UNROLL - for (uint cascade = minCascade; cascade < 4 && hit.HitTime < 0.0f; cascade++) + for (uint cascade = 0; cascade < 4 && hit.HitTime < 0.0f; cascade++) { float4 cascadePosDistance = data.CascadePosDistance[cascade]; float cascadeMaxDistance = cascadePosDistance.w * 2; @@ -173,6 +174,18 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4] // Surface hit hit.HitTime = max(stepTime + stepDistance - minSurfaceThickness, 0.0f); hit.HitCascade = cascade; + if (trace.NeedsHitNormal) + { + // Calculate hit normal from SDF gradient + float texelOffset = 1.0f / data.Resolution; + float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x; + float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x; + float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x; + float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x; + float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x; + float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x; + hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); + } break; }