Files
2026-07-13 13:13:17 +08:00

161 lines
4.9 KiB
HLSL

#include "shared/hash-functions.hlsl"
#include "shared/noise-functions.hlsl"
#include "shared/point.hlsl"
#include "shared/quat-functions.hlsl"
cbuffer Params : register(b0)
{
float4x4 TransformVolume;
float4x4 InverseTransformVolume;
float Amount;
float Bounciness;
float RandomizeBounce;
float RandomizeReflection;
float Attraction;
float Repulsion;
float SpeedFactor;
float InvertVolumeFactor;
float AttractionDecay;
}
cbuffer Params : register(b1)
{
int VolumeShape;
}
RWStructuredBuffer<Particle> Particles : u0;
static const int VolumeSphere = 0;
static const int VolumeBox = 1;
static const int VolumePlane = 2;
static const int VolumeCylinder = 3;
static const int VolumeNoise = 4;
[numthreads(64, 1, 1)] void main(uint3 i : SV_DispatchThreadID)
{
uint maxParticleCount, _;
Particles.GetDimensions(maxParticleCount, _);
int gi = i.x;
if (gi >= maxParticleCount)
return;
if (isnan(Particles[gi].BirthTime))
return;
// return;
if (isnan(TransformVolume._11) || TransformVolume._11 == 0)
{
return;
}
float3 pos = Particles[gi].Position;
float4 rot = Particles[gi].Rotation;
float3 velocity = Particles[gi].Velocity;
float r = Particles[gi].Radius;
float3 posInVolume = mul(float4(pos, 1), TransformVolume).xyz;
float3 posInVolumeNext = mul(float4(pos + velocity * SpeedFactor * 0.01 * 2, 1), TransformVolume).xyz;
float unitLength = 1 * r / 2;
float3 rInVolume = length(mul(float4(unitLength.xxx, 0), TransformVolume));
// float s = 1;
float distance = 0;
float distanceNext = 0;
float3 surfaceN = 0;
if (VolumeShape == VolumeSphere)
{
float rUnitSphere = 0.5;
distance = length(posInVolume) - rUnitSphere;
distanceNext = length(posInVolumeNext) - rUnitSphere;
surfaceN = normalize(posInVolume);
// s = smoothstep(1 + FallOff, 1, distance);
}
else if (VolumeShape == VolumeBox)
{
float3 t1 = abs(posInVolume);
surfaceN = t1.x > t1.y ? (t1.x > t1.z ? float3(sign(posInVolume.x), 0, 0) : float3(0, 0, sign(posInVolume.z)))
: (t1.y > t1.z ? float3(0, sign(posInVolume.y), 0) : float3(0, 0, sign(posInVolume.z)));
float r1 = length(abs(rInVolume * surfaceN)) * InvertVolumeFactor;
float rUnitSphere = 0.5;
distance = max(max(t1.x, t1.y), t1.z) - rUnitSphere - r1;
float3 t2 = abs(posInVolumeNext);
distanceNext = max(max(t2.x, t2.y), t2.z) - rUnitSphere - r1;
}
else if (VolumeShape == VolumePlane)
{
distance = posInVolume.y - r * InvertVolumeFactor;
distanceNext = posInVolumeNext.y - r * InvertVolumeFactor;
surfaceN = float3(0, 1, 0);
// s = smoothstep(FallOff, 0, distance);
}
else if (VolumeShape == VolumeCylinder)
{
// Assuming the cylinder is aligned along the y-axis
float rCylinder = 0.5;
float heightCylinder = 1.0;
float2 xyPos = posInVolume.xz;
float2 xyPosNext = posInVolumeNext.xz;
float distanceToCenter = length(xyPos);
float distanceToCenterNext = length(xyPosNext);
// Check if the particle is within the radius of the cylinder
if (distanceToCenter <= rCylinder)
{
distance = abs(posInVolume.y) - heightCylinder * 0.5;
distanceNext = abs(posInVolumeNext.y) - heightCylinder * 0.5;
// Set the surface normal based on the cylinder's orientation
surfaceN = float3(0, sign(posInVolume.y), 0);
}
else
{
// Particle is outside the cylinder, use the distance to the cylinder surface
distance = distanceToCenter - rCylinder;
distanceNext = distanceToCenterNext - rCylinder;
// Set the surface normal based on the cylinder's orientation
surfaceN = float3(xyPos.x, 0, xyPos.y);
surfaceN.y = 0; // Ignore the y-component, as it's already handled above
surfaceN = normalize(surfaceN);
}
}
float3 force = 0;
surfaceN *= InvertVolumeFactor;
float3 surfaceInWorld = normalize(mul(float4(surfaceN, 0), InverseTransformVolume).xyz);
// float3 surfaceInWorld = surfaceN;
if (sign(distance * distanceNext) < 0 && distance * InvertVolumeFactor > 0)
{
float4 rand = hash41u(gi);
velocity = reflect(velocity, surfaceInWorld + (RandomizeReflection * (rand.xyz - 0.5))) * Bounciness * (RandomizeBounce * (rand.z - 0.5) + 1);
}
else
{
if (distance * InvertVolumeFactor < 0)
{
force = surfaceInWorld * Repulsion;
}
else
{
force = -surfaceInWorld * Attraction / (1 + distance * AttractionDecay);
}
velocity += force * SpeedFactor;
}
if (!isnan(velocity.x) && !isnan(velocity.y) && !isnan(velocity.z))
{
Particles[gi].Velocity = lerp(Particles[gi].Velocity, velocity, Amount);
}
}