From a917397090262ca843a8c1601b20fe1f9edcbbf3 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Fri, 25 Mar 2022 15:36:00 +0100 Subject: [PATCH] Add `Conform to Global SDF` to GPU particles --- Content/Shaders/GlobalSignDistanceField.flax | 4 +-- .../Surface/Archetypes/ParticleModules.cs | 25 +++++++++++++++ ...rticleEmitterGraph.CPU.ParticleModules.cpp | 6 ++++ ...rticleEmitterGraph.GPU.ParticleModules.cpp | 32 +++++++++++++++++++ .../Particles/Graph/ParticleEmitterGraph.h | 1 + Source/Shaders/GlobalSignDistanceField.hlsl | 9 +++--- Source/Shaders/GlobalSignDistanceField.shader | 9 +++++- 7 files changed, 79 insertions(+), 7 deletions(-) diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index 671925ceb..54dd366ac 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddf6d7c9411162a9e6ba9a762d8c95374a3c78b2ca9ee34c5695869eb56cbf6b -size 8375 +oid sha256:c283024b5b907b9ee8eeb43ee95f8f825fe3cca959461b920d93f2978fdfd3bf +size 8460 diff --git a/Source/Editor/Surface/Archetypes/ParticleModules.cs b/Source/Editor/Surface/Archetypes/ParticleModules.cs index 916ed76ea..498d83c68 100644 --- a/Source/Editor/Surface/Archetypes/ParticleModules.cs +++ b/Source/Editor/Surface/Archetypes/ParticleModules.cs @@ -1362,6 +1362,31 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(-0.5f + 0, "Surface Thickness", true, typeof(float), 5, 8), }, }, + new NodeArchetype + { + TypeID = 335, + Create = CreateParticleModuleNode, + Title = "Conform to Global SDF", + Description = "Applies the force vector to particles to conform around Global SDF", + Flags = DefaultModuleFlags, + Size = new Vector2(200, 4 * Surface.Constants.LayoutOffsetY), + DefaultValues = new object[] + { + true, + (int)ModuleType.Update, + 5.0f, + 2000.0f, + 1.0f, + 5000.0f, + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(-0.5f, "Attraction Speed", true, typeof(float), 0, 2), + NodeElementArchetype.Factory.Input(-0.5f + 1.0f, "Attraction Force", true, typeof(float), 1, 3), + NodeElementArchetype.Factory.Input(-0.5f + 2.0f, "Stick Distance", true, typeof(float), 2, 4), + NodeElementArchetype.Factory.Input(-0.5f + 3.0f, "Stick Force", true, typeof(float), 3, 5), + }, + }, GetParticleAttribute(ModuleType.Update, 350, "Set Position", "Sets the particle position", typeof(Vector3), Vector3.Zero), GetParticleAttribute(ModuleType.Update, 351, "Set Lifetime", "Sets the particle lifetime (in seconds)", typeof(float), 10.0f), GetParticleAttribute(ModuleType.Update, 352, "Set Age", "Sets the particle age (in seconds)", typeof(float), 0.0f), diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index 39b1f8b9b..dd5fb6ea5 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -1463,6 +1463,12 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Not supported break; } + // Conform to Global SDF + case 335: + { + // Not supported + break; + } #undef COLLISION_BEGIN #undef COLLISION_INPUTS_FETCH diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp index addce771b..fd1c000fe 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp @@ -877,6 +877,38 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) ); break; } + // Conform to Global SDF + case 335: + { + auto position = AccessParticleAttribute(node, nodeGpu->Attributes[0], AccessMode::Read); + auto velocity = AccessParticleAttribute(node, nodeGpu->Attributes[1], AccessMode::ReadWrite); + auto mass = AccessParticleAttribute(node, nodeGpu->Attributes[2], AccessMode::Read); + + const Value attractionSpeed = GetValue(node->GetBox(0), 2).AsFloat(); + const Value attractionForce = GetValue(node->GetBox(1), 3).AsFloat(); + const Value stickDistance = GetValue(node->GetBox(2), 4).AsFloat(); + const Value stickForce = GetValue(node->GetBox(3), 5).AsFloat(); + + auto param = findOrAddGlobalSDF(); + _includes.Add(TEXT("./Flax/GlobalSignDistanceField.hlsl")); + _writer.Write( + TEXT( + " {{\n" + " // Conform to Global SDF\n" + " float dist;\n" + " float3 dir = normalize(SampleGlobalSDFGradient({3}, {3}_Tex, {0}, dist));\n" + " if (dist > 0) dir *= -1;\n" + " float distToSurface = abs(dist);\n" + " float spdNormal = dot(dir, {1});\n" + " float ratio = smoothstep(0.0f, {6} * 2.0f, distToSurface);\n" + " 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" + " if (dist < 500) {1} += deltaVelocity;\n" + " }}\n" + ), position.Value, velocity.Value, mass.Value, param.ShaderName, attractionSpeed.Value, attractionForce.Value, stickDistance.Value, stickForce.Value); + break; + } #undef COLLISION_BEGIN #undef COLLISION_LOGIC diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index 8c803726e..3ab77c0b9 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -415,6 +415,7 @@ public: #undef CASE_SET_PARTICLE_ATTRIBUTE // Conform to Sphere case GRAPH_NODE_MAKE_TYPE(15, 305): + case GRAPH_NODE_MAKE_TYPE(15, 335): // Conform to Global SDF { USE_ATTRIBUTE(Position, Vector3, 0); USE_ATTRIBUTE(Velocity, Vector3, 1); diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 26bbc7957..bb33316bc 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -77,11 +77,11 @@ 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, uint minCascade = 0) +float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition, out float distance, uint minCascade = 0) { float3 gradient = float3(0, 0.00001f, 0); - float distance = data.CascadePosDistance[3].w * 2.0f; - if (distance <= 0.0f) + distance = 60000; + if (data.CascadePosDistance[3].w <= 0.0f) return gradient; UNROLL for (uint cascade = minCascade; cascade < 4; cascade++) @@ -93,7 +93,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4] float cascadeDistance = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); if (cascadeDistance < 0.9f && !any(cascadeUV < 0) && !any(cascadeUV > 1)) { - float texelOffset = 0.5f / data.Resolution; + 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; @@ -101,6 +101,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4] 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; gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; + distance = cascadeDistance * cascadeMaxDistance; break; } } diff --git a/Source/Shaders/GlobalSignDistanceField.shader b/Source/Shaders/GlobalSignDistanceField.shader index dac95b00c..82cccbd65 100644 --- a/Source/Shaders/GlobalSignDistanceField.shader +++ b/Source/Shaders/GlobalSignDistanceField.shader @@ -210,7 +210,14 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target float3 color = saturate(hit.StepsCount / 80.0f).xxx; if (!hit.IsHit()) color.rg *= 0.4f; - //else color.rgb = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, hit.GetHitPosition(trace))); +#if 0 + else + { + // Debug draw SDF normals + float dst; + color.rgb = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, hit.GetHitPosition(trace), dst)) * 0.5f + 0.5f; + } +#endif return float4(color, 1); }