Fix GPU particles issues with Global SDF far away from scene geometry

This commit is contained in:
Wojtek Figat
2024-08-05 22:43:00 +02:00
parent 52b00644c6
commit ffb760d8f3
2 changed files with 33 additions and 19 deletions

View File

@@ -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"

View File

@@ -155,13 +155,14 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D<snorm float> 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<snorm float> tex, float3 worldPosition, out float distance)
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<snorm float> 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<snorm float>
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;
}
}