Introduction
Simulating fire in a 3D scene is a challenging yet rewarding task. This article walks you through the creation of a dynamic fire shader in Three.js, utilizing noise functions and custom shader logic to produce a realistic flame effect.
The Concept Behind Fire Shaders
Fire is characterized by:
- Dynamic Movement: Flames flicker and change shape over time.
- Gradient Colors: Transitioning hues from red to yellow to white.
- Transparency: Variable opacity to simulate flame edges.
By combining vertex and fragment shaders, we can simulate these characteristics effectively.
Vertex Shader Breakdown
Purpose
The vertex shader applies subtle distortions to mimic the chaotic movement of fire.
Code Explanation
uniform float time; // Animation time variable.
varying vec3 vPosition; // Pass the vertex position to the fragment shader.
float noise(vec3 p) {
return sin(p.x * 10.0 + p.y * 10.0 + p.z * 10.0) * 0.5 + 0.5;
// Generate noise using sine waves for flame distortion.
}
void main() {
vPosition = position; // Pass the original vertex position.
vec3 pos = position;
pos.y += noise(vec3(pos.x, pos.y + time, pos.z)) * 0.5; // Add vertical flicker.
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0); // Transform vertex.
}
Key Features
- Noise for Flickering: Creates vertical displacement for a flickering effect.
- Dynamic Movement: The
timeuniform drives continuous motion.
Fragment Shader Breakdown
Purpose
The fragment shader handles the visual appearance of fire, including color gradients and transparency.
Code Explanation
uniform float time; // Time for animation.
varying vec3 vPosition; // Vertex position passed from vertex shader.
void main() {
float intensity = 1.0 - smoothstep(0.0, 1.0, length(vPosition.xy));
// Calculate intensity based on distance from the center.
vec3 color = mix(vec3(1.0, 0.2, 0.0), vec3(1.0, 1.0, 0.0), intensity);
// Gradient from red to yellow based on intensity.
color = mix(color, vec3(1.0), pow(intensity, 3.0));
// Add a white-hot core using intensity falloff.
gl_FragColor = vec4(color, intensity * 0.7);
// Apply transparency based on intensity.
}
Key Features
- Color Gradient: Blends red, yellow, and white to simulate fire hues.
- Intensity Control: Adjusts brightness based on proximity to the flame’s core.
- Dynamic Transparency: Edges fade out naturally with lower intensity.
Integrating the Shader in Three.js
- Geometry: Use a cone or plane to represent the fire volume.
- Material: Apply the custom shader using
ShaderMaterial. - Animation: Continuously update the
timeuniform to animate the fire.
const fireMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0.0 }
},
vertexShader: `...`, // Vertex shader code here
fragmentShader: `...`, // Fragment shader code here
transparent: true
});
const fireGeometry = new THREE.ConeGeometry(1, 2, 32);
const fireMesh = new THREE.Mesh(fireGeometry, fireMaterial);
scene.add(fireMesh);
Customizing the Fire Shader
Use lil-gui to tweak parameters like:
- Flame Height: Adjust vertex noise amplitude.
- Color Gradients: Modify colors for unique flame effects.
- Opacity: Control transparency for various levels of realism.
Applications of Fire Shaders
- Campfire Scenes: Realistic flames for outdoor settings.
- Torches and Candles: Subtle and controlled fire for medieval environments.
- Fantasy Effects: Magical fire in games or animations.
Conclusion
The fire shader combines dynamic vertex deformation and color blending in the fragment shader to create a compelling visual effect. Experimenting with noise functions, color transitions, and animation speed can lead to a wide variety of fire effects, adding depth and realism to your Three.js projects!