3D fire shader
Read complete fire shader tutorial on my gamedev/shaders website. You can change the values of uniform variables to achieve different styles. Some values result in stylized look as in cel shaded games, other values result in more realistic-ish output. If you like it, you can check out more of my tutorials as well.
Shader code
shader_type spatial;
render_mode unshaded, cull_disabled;
// fire uniforms
uniform float detail_strength = 3.0;
uniform float scroll_speed = 1.2;
uniform float fire_height = 1.0;
uniform float fire_shape = 1.5;
uniform float fire_thickness = 0.55;
uniform float fire_sharpness = 1.0;
uniform float intensity = 1.0;
// noise uniforms
uniform int noise_octaves = 6;
uniform float noise_lacunarity = 3.0;
uniform float noise_gain = 0.5;
uniform float noise_amplitude = 1.0;
uniform float noise_frequency = 1.5;
// 2D Value noise function
float hash(vec2 p) {
p = fract(p * 0.3183099 + vec2(0.1, 0.1));
p *= 17.0;
return fract(p.x * p.y * (p.x + p.y));
}
// 2D Value noise function (smooth)
float noise(vec2 x) {
vec2 p = floor(x);
vec2 f = fract(x);
float n =
hash(p) * (1.0 - f.x) * (1.0 - f.y) +
hash(p + vec2(1.0, 0.0)) * f.x * (1.0 - f.y) +
hash(p + vec2(0.0, 1.0)) * (1.0 - f.x) * f.y +
hash(p + vec2(1.0, 1.0)) * f.x * f.y;
return n;
}
// Fractional Brownian Motion function (2D)
float fbm(vec2 p) {
int octaves = noise_octaves;
float lacunarity = noise_lacunarity;
float gain = noise_gain;
float amplitude = noise_amplitude;
float frequency = noise_frequency;
float total = 0.0;
for (int i = 0; i < octaves; i++) {
total += noise(p * frequency) * amplitude;
frequency *= lacunarity;
amplitude *= gain;
}
return total * 0.5;
}
void fragment() {
vec2 uv = UV;
// modified_uv for offset and animating UVs for fire
vec2 modified_uv = -uv;
modified_uv.x = mod(modified_uv.x, 1.0) - 0.5;
modified_uv.y += 0.84; // size vertical
// fire noise scroll effect
float scroll = scroll_speed * detail_strength * TIME;
// sample noise for fire
float noise = fbm(detail_strength * modified_uv - vec2(0.0, scroll));
// controls the intensity of noise
// at different points in uvs. it thus
// controls the shape of fire
float fire_intensity = intensity - 16.0 * fire_sharpness * pow(
max(
0.0,
length(
modified_uv * vec2((1.0 / fire_thickness) + modified_uv.y * fire_shape, 1.0 / fire_height)
) - noise * max(0.0, modified_uv.y + 0.25)
),
1.2
);
float fire_intensity1 = noise * fire_intensity * (1.5 - pow(1.0 * uv.y, 14.0));
fire_intensity1 = clamp(fire_intensity1, 0.0, 1.0);
// compute fire color
// red channel is most intense, green less than it
// blue least intense because [pow of x while(x < 1) is less than x]
vec3 fire_color = vec3(
1.5 * fire_intensity1,
1.5 * pow(fire_intensity1, 3.0),
pow(fire_intensity1, 6.0)
);
// add alpha to color
float alpha = fire_intensity * (1.0 - pow(uv.y, 3.0));
vec4 final_color = vec4(
mix(vec3(0.0), fire_color, alpha),
alpha
);
// apply to fragment
ALBEDO = final_color.rgb;
ALPHA = final_color.a;
}
this is absolutely incredible, thank you for this
I’d mention this only works in compatibility renderer