From ffb760d8f3766d7303240c3b4645fad5bacb63d8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Aug 2024 22:43:00 +0200 Subject: [PATCH] Fix GPU particles issues with Global SDF far away from scene geometry --- ...rticleEmitterGraph.GPU.ParticleModules.cpp | 16 ++++----- Source/Shaders/GlobalSignDistanceField.hlsl | 36 +++++++++++++------ 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp index 04dbbbc32..bbf4ccdf7 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp @@ -626,8 +626,8 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " // Position (Global SDF)\n" " float3 wsPos = {2};\n" " float dist;\n" - " float3 dir = -normalize(SampleGlobalSDFGradient({1}, {1}_Tex, wsPos, dist));\n" - " {0} += dir * dist;\n" + " float3 dir = -normalize(SampleGlobalSDFGradient({1}, {1}_Tex, {1}_Mip, wsPos, dist));\n" + " {0} += dist < GLOBAL_SDF_WORLD_SIZE ? dir * dist : float3(0, 0, 0);\n" " }}\n" ), position.Value, param.ShaderName, wsPos); break; @@ -892,7 +892,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " {{\n" " // Conform to Global SDF\n" " float dist;\n" - " float3 dir = normalize(SampleGlobalSDFGradient({3}, {3}_Tex, {0}, dist));\n" + " float3 dir = normalize(SampleGlobalSDFGradient({3}, {3}_Tex, {3}_Mip, {0}, dist));\n" " if (dist > 0) dir *= -1;\n" " float distToSurface = abs(dist);\n" " float spdNormal = dot(dir, {1});\n" @@ -900,7 +900,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " float tgtSpeed = {4} * ratio;\n" " float deltaSpeed = tgtSpeed - spdNormal;\n" " float3 deltaVelocity = dir * (sign(deltaSpeed) * min(abs(deltaSpeed), DeltaTime * lerp({7}, {5}, ratio)) / max({2}, PARTICLE_THRESHOLD));\n" - " {1} += deltaVelocity;\n" + " {1} += dist < GLOBAL_SDF_WORLD_SIZE ? deltaVelocity : 0.0f;\n" " }}\n" ), position.Value, velocity.Value, mass.Value, param.ShaderName, attractionSpeed.Value, attractionForce.Value, stickDistance.Value, stickForce.Value); break; @@ -917,11 +917,11 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " // Collision (Global SDF)\n" " float3 nextPos = {0} + {1} * DeltaTime;\n" " nextPos = mul(float4(nextPos, 1), WorldMatrix).xyz;\n" - " float dist = SampleGlobalSDF({10}, {10}_Tex, nextPos);\n" + " float dist = SampleGlobalSDF({10}, {10}_Tex, {10}_Mip, nextPos);\n" " if (dist < {5})\n" " {{\n" " {0} = mul(float4({0}, 1), WorldMatrix).xyz;\n" - " float3 n = normalize(SampleGlobalSDFGradient({10}, {10}_Tex, {0}, dist));\n" + " float3 n = normalize(SampleGlobalSDFGradient({10}, {10}_Tex, {10}_Mip, {0}, dist));\n" " {0} += n * -dist;\n" " {0} = mul(float4({0}, 1), InvWorldMatrix).xyz;\n" COLLISION_LOGIC() @@ -931,10 +931,10 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " {{\n" " // Collision (Global SDF)\n" " float3 nextPos = {0} + {1} * DeltaTime;\n" - " float dist = SampleGlobalSDF({10}, {10}_Tex, nextPos);\n" + " float dist = SampleGlobalSDF({10}, {10}_Tex, {10}_Mip, nextPos);\n" " if (dist < {5})\n" " {{\n" - " float3 n = normalize(SampleGlobalSDFGradient({10}, {10}_Tex, {0}, dist));\n" + " float3 n = normalize(SampleGlobalSDFGradient({10}, {10}_Tex, {10}_Mip, {0}, dist));\n" " {0} += n * -dist;\n" COLLISION_LOGIC() " }}\n" diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 577b9ebca..b850109c1 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -155,13 +155,14 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, Text } // 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, float3 worldPosition, out float distance) +float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex, float3 worldPosition, out float distance, uint startCascade = 0) { float3 gradient = float3(0, 0.00001f, 0); distance = GLOBAL_SDF_WORLD_SIZE; if (data.CascadePosDistance[3].w <= 0.0f) return gradient; - for (uint cascade = 0; cascade < data.CascadesCount; cascade++) + startCascade = min(startCascade, data.CascadesCount - 1); + for (uint cascade = startCascade; cascade < data.CascadesCount; cascade++) { float3 cascadeUV, textureUV; GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV); @@ -205,19 +206,32 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip; if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) { - distance = distanceMip; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin) + { distance = distanceTex; - float texelOffset = 1.0f / data.Resolution; - float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; - gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; + float texelOffset = 1.0f / data.Resolution; + float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; + } + else + { + distance = distanceMip; + float texelOffset = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR / data.Resolution; + float xp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceMip; + } break; } }