Particle turbulence: motion and birth displacement
Working preset, static field, lifetime influence and troubleshooting.
Turbulence bends GPU particle trajectories using a procedural noise field. Use it for drifting smoke, sparks or swirling dust. It does not require an imported noise texture and does not create a gameplay physics force.
Start with a visible, moving emitter
Create a working GpuParticles3D first: assign a draw mesh and visible material, enable emission, and frame it with a Camera3D. If nothing is visible before adding turbulence, follow the emitter setup.
Turbulence needs motion to redirect. Give the process material initial velocity, gravity or another movement contributor. For a controlled comparison, use initial velocity and disable gravity, attractors and collision.
Save this helper as ParticleTurbulence.cs:
using Reckfell;
public static class ParticleTurbulence
{
// Static noise-field preset for a controlled motion comparison.
public static ParticleProcessMaterial Create()
{
return new ParticleProcessMaterial
{
Direction = Vector3.Up, Spread = 0,
InitialVelocityMin = 1f, InitialVelocityMax = 1f,
Gravity = Vector3.Zero,
EmissionShapeOffset = new Vector3(0.13f, 0.17f, 0),
TurbulenceEnabled = true,
TurbulenceNoiseStrength = 1f,
TurbulenceNoiseScale = 3f,
TurbulenceNoiseSpeed = Vector3.Zero,
TurbulenceNoiseSpeedRandom = 0f,
TurbulenceInfluenceMin = 1f,
TurbulenceInfluenceMax = 1f,
TurbulenceInitialDisplacementMin = 0f,
TurbulenceInitialDisplacementMax = 0f,
};
}
}Assign particles.ProcessMaterial = ParticleTurbulence.Create(); to your emitter. Use Lifetime 4, FixedFps 60 and a VisibilityAabb covering the intended motion. For the same comparison as the executed example, place the emitter at (0.37, 0.21, 0), use world coordinates (LocalCoords = false), OneShot, Explosiveness 1 and a fixed seed. The emission offset places particles away from the noise origin; it is not a required magic value for every effect.
Restart the emitter after each change. With TurbulenceEnabled off, particles travel upward. With the preset enabled, the same initial motion bends through the static field. The exact path depends on position and the field settings; it is not a target curve.
Tune one control at a time
| Control | What it changes |
|---|---|
| TurbulenceNoiseStrength | Noise contrast and direction detail. It is not a direct particle speed or force value. |
| TurbulenceNoiseScale | Spatial field detail through a nonlinear control. Do not treat it as meters or copy a frequency value from another particle system. |
| TurbulenceNoiseSpeed | Translation of the procedural field over time. It does not directly set particle velocity. |
| TurbulenceNoiseSpeedRandom | Time motion in the noise's additional coordinate. Zero NoiseSpeed alone does not freeze the field while this value is nonzero. |
| TurbulenceInfluenceMin / Max | Per-particle range controlling how strongly the field redirects motion. Use equal endpoints to remove this source of variation. |
| TurbulenceInfluenceOverLife | Texture multiplier sampled over normalized particle age: 0 at birth, 1 at death. A CurveTexture is convenient for authoring it. |
| TurbulenceInitialDisplacementMin / Max | Offset applied once at birth along the noise-derived direction. It is separate from ongoing steering. |
To keep the field static, set both NoiseSpeed to Vector3.Zero and NoiseSpeedRandom to 0. Particles can still move through a static field and change direction. This differs from pausing the emitter simulation.
Influence is multiplied by the lifetime texture and clamped to 0–1 for steering. No texture means a multiplier of 1. For a controlled zero curve, add points (0, 0) and (1, 0) to a Curve and assign it through CurveTexture. A freshly created texture without an authored curve receives a constant-one default when assigned to this material; it is not a zero-influence preset.
Increasing influence does not guarantee greater speed. The implementation blends the existing velocity with a field-oriented velocity; direction and magnitude can both change. Tune the initial velocity separately.
Separate birth position from motion
With initial velocity and gravity zero, turbulence steering does not start meaningful motion from rest. InitialDisplacement can still move the birth position, including when InfluenceMin and InfluenceMax are zero. After that single offset, a particle with no movement contributor remains stationary.
Use this distinction when diagnosing an effect: a different starting position is not evidence of acceleration. Compare the same particle shortly after birth and later, with identical settings and seed.
Troubleshoot
| Symptom | Check |
|---|---|
| Particles are visible but do not move | Initial velocity, gravity or another movement contributor; turbulence redirects existing motion. |
| Motion is straight | TurbulenceEnabled, influence range and lifetime texture. A zero multiplier suppresses steering. |
| The field changes despite zero NoiseSpeed | Set NoiseSpeedRandom to zero too. |
| Particles jump at birth | InitialDisplacement range and emission shape offset. |
| The effect disappears mid-flight | VisibilityAabb, camera and lifetime; the original emission region may no longer contain the trajectory. |
| Two emitters change together | They may share the same ParticleProcessMaterial resource. |
The executed example checks real particle pixels on Windows Forward+/Vulkan: disabled/zero/full influence, a zero lifetime curve, rest and birth displacement. Moving noise, combined collision/attractors and other renderers require separate validation. Measure GPU time in your target scene; there is no universal safe number of turbulence emitters.