Atmospheric Effects and Fog Systems
The mist system uses custom shader materials with per-particle attributes to create an atmospheric fog effect reminiscent of Chinese landscape paintings. Each mist particle has a random size, uses GPU-based animation for optimal performance, clusters organically through random positioning, and animates with gentle floating motion using sine waves.
Interactive Shader Demo
Here's a simplified fragment shader that demonstrates the core concepts of particle-based fog effects using procedural generation:
<GLSLIDE width="800" height="500" showCodeEditor="true" defaultView="split" code="void mainImage(out vec4 fragColor, in vec2 fragCoord) { vec2 uv = fragCoord / iResolution.xy; uv.x *= iResolution.x / iResolution.y;
vec3 color = vec3(0.05, 0.05, 0.1);
// Procedural particle generation
float particleCount = 50.0;
float fogIntensity = 0.0;
for (float i = 0.0; i < particleCount; i += 1.0) {
// Generate particle position using hash
float seed = i * 100.0;
vec2 particlePos = vec2(
fract(sin(seed * 12.9898)),
fract(sin(seed * 78.233))
);
// Animate particle using sine waves (GPU-based animation)
particlePos.y += sin(iTime * 0.5 + particlePos.x * 0.1) * 0.1;
particlePos.x += sin(iTime * 0.3 + particlePos.y * 0.1) * 0.05;
// Calculate distance from current pixel
float d = length(uv - particlePos);
// Particle size and intensity
float size = 0.01 + fract(sin(seed * 43.758)) * 0.02;
// Fixed: smoothstep needs 3 arguments (edge0, edge1, x) where edge0 < edge1
// We want high intensity when close (d small), low when far (d large)
float intensity = 1.0 - smoothstep(0.0, size * 0.5, d);
// Additive blending for fog effect
fogIntensity += intensity * 0.2;
}
// Apply fog color with additive blending
// Fixed: mix(vec3, vec3, float) - both colors must be vec3
color = mix(color, vec3(1.0, 1.0, 1.0), fogIntensity);
// Fixed: vec4 constructor takes 4 arguments (x, y, z, w)
fragColor = vec4(color, 1.0);
}" />
This simplified demo shows procedural particle generation and GPU-based animation using sine waves, similar to the fog system. The particles animate entirely in the fragment shader, demonstrating the performance benefits of GPU-based particle systems.
The visualization above demonstrates the fog particle system. Adjust particle count, animation speed, and particle size to see how these parameters affect the atmospheric effect. Click and drag to rotate the camera, or scroll to zoom.
Technical Implementation
The fog system uses GPU-based animation for optimal performance, moving all calculations to the vertex shader:
// Create mist particles
const mistGeometry = new THREE.BufferGeometry();
const particleCount = 200;
const positions = new Float32Array(particleCount * 3);
const sizes = new Float32Array(particleCount);
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
positions[i3] = (Math.random() - 0.5) * 20;
positions[i3 + 1] = Math.random() * 5;
positions[i3 + 2] = (Math.random() - 0.5) * 20;
sizes[i] = 0.5 + Math.random() * 1.5;
}
mistGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
mistGeometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
// GPU-based shader material
const mistMaterial = new THREE.ShaderMaterial({
uniforms: { time: { value: 0 } },
vertexShader: `
attribute float size;
uniform float time;
void main() {
vec3 pos = position;
// Animate on GPU - no CPU updates needed!
pos.y += sin(time + position.x * 0.1 + position.z * 0.1) * 0.1;
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
void main() {
float dist = length(gl_PointCoord - vec2(0.5));
float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
gl_FragColor = vec4(1.0, 1.0, 1.0, alpha * 0.3);
}
`,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false,
});
GPU-Based Animation
All animation happens in the vertex shader, which means:
-
No CPU overhead: No JavaScript updates needed each frame
-
Highly efficient: GPU processes all particles in parallel
-
Smooth performance: Can handle hundreds of particles without frame drops
The animation uses sine waves with different phases for each particle, creating organic, floating motion:
pos.y += sin(time + position.x * 0.1 + position.z * 0.1) * 0.1;
Each particle moves based on its position and the current time, creating varied motion patterns.
Visual Aesthetic
The fog system creates an atmospheric effect that:
-
Clusters organically through random positioning
-
Floats gently with sine wave animation
-
Adds depth to the scene with atmospheric perspective
-
Matches the aesthetic of Chinese landscape paintings
The particles use additive blending to create a soft, glowing effect that enhances the minimalist, stylized look of the background.
Performance Optimization
The fog system is optimized for performance:
-
GPU-based animation (no CPU updates)
-
Efficient point sprite rendering
-
Configurable particle count (typically 200-400 particles)
-
IntersectionObserver pauses rendering when not visible
References
-
Three.js Points - Point sprite rendering
-
WebGL Shaders - Shader programming basics
-
Particle Systems - General particle system concepts
Related Articles
-
An adventure with 3js 3d Backgrounds - Overview of the animated background system
-
3d Background - Cell Shading - Rendering techniques used in the scene
-
Performance Optimization - Performance considerations for the background
Sub-Topics
Explore specific atmospheric effects:
- 3d Background - Atmospheric Effects - Fog Visualization - Detailed fog implementation
- 3d Background - Atmospheric Effects - Rain Visualization - Rain particle systems
- 3d Background - Atmospheric Effects - Snow Visualization - Snow effects and accumulation