UNPKG

@wolffo/three-fire

Version:

Modern TypeScript volumetric fire effect for Three.js and React Three Fiber

73 lines 5.85 kB
import { Vector3, Vector4, Color, Matrix4, Texture } from 'three'; /** * Uniforms interface for the fire shader */ export interface FireShaderUniforms { /** Fire texture (grayscale mask) */ fireTex: { value: Texture | null; }; /** Fire color tint */ color: { value: Color; }; /** Current time for animation */ time: { value: number; }; /** Random seed for fire variation */ seed: { value: number; }; /** Inverse model matrix for ray marching */ invModelMatrix: { value: Matrix4; }; /** Scale of the fire object */ scale: { value: Vector3; }; /** Noise scaling parameters [x, y, z, time] */ noiseScale: { value: Vector4; }; /** Fire shape intensity */ magnitude: { value: number; }; /** Noise lacunarity (frequency multiplier) */ lacunarity: { value: number; }; /** Noise gain (amplitude multiplier) */ gain: { value: number; }; } /** * Volumetric fire shader using ray marching and simplex noise * * Based on "Real-Time procedural volumetric fire" by Alfred et al. * Uses simplex noise for turbulence and ray marching for volume rendering. * * @example * ```ts * const material = new ShaderMaterial({ * defines: FireShader.defines, * uniforms: FireShader.uniforms, * vertexShader: FireShader.vertexShader, * fragmentShader: FireShader.fragmentShader, * transparent: true * }) * ``` */ export declare const FireShader: { readonly defines: { readonly ITERATIONS: "20"; readonly OCTAVES: "3"; }; readonly uniforms: FireShaderUniforms; readonly vertexShader: "\n varying vec3 vWorldPos;\n\n void main() {\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;\n }\n "; readonly fragmentShader: "\n uniform vec3 color;\n uniform float time;\n uniform float seed;\n uniform mat4 invModelMatrix;\n uniform vec3 scale;\n uniform vec4 noiseScale;\n uniform float magnitude;\n uniform float lacunarity;\n uniform float gain;\n uniform sampler2D fireTex;\n\n varying vec3 vWorldPos;\n\n // GLSL simplex noise function by ashima\n vec3 mod289(vec3 x) {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n }\n\n vec4 mod289(vec4 x) {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n }\n\n vec4 permute(vec4 x) {\n return mod289(((x * 34.0) + 1.0) * x);\n }\n\n vec4 taylorInvSqrt(vec4 r) {\n return 1.79284291400159 - 0.85373472095314 * r;\n }\n\n float snoise(vec3 v) {\n const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\n vec3 i = floor(v + dot(v, C.yyy));\n vec3 x0 = v - i + dot(i, C.xxx);\n\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min(g.xyz, l.zxy);\n vec3 i2 = max(g.xyz, l.zxy);\n\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy;\n vec3 x3 = x0 - D.yyy;\n\n i = mod289(i);\n vec4 p = permute(permute(permute(\n i.z + vec4(0.0, i1.z, i2.z, 1.0))\n + i.y + vec4(0.0, i1.y, i2.y, 1.0))\n + i.x + vec4(0.0, i1.x, i2.x, 1.0));\n\n float n_ = 0.142857142857;\n vec3 ns = n_ * D.wyz - D.xzx;\n\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z);\n\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_);\n\n vec4 x = x_ * ns.x + ns.yyyy;\n vec4 y = y_ * ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n\n vec4 b0 = vec4(x.xy, y.xy);\n vec4 b1 = vec4(x.zw, y.zw);\n\n vec4 s0 = floor(b0) * 2.0 + 1.0;\n vec4 s1 = floor(b1) * 2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n\n vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;\n vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;\n\n vec3 p0 = vec3(a0.xy, h.x);\n vec3 p1 = vec3(a0.zw, h.y);\n vec3 p2 = vec3(a1.xy, h.z);\n vec3 p3 = vec3(a1.zw, h.w);\n\n vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));\n p0 *= norm.x;\n p1 *= norm.y;\n p2 *= norm.z;\n p3 *= norm.w;\n\n vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);\n m = m * m;\n return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));\n }\n\n float turbulence(vec3 p) {\n float sum = 0.0;\n float freq = 1.0;\n float amp = 1.0;\n\n for(int i = 0; i < OCTAVES; i++) {\n sum += abs(snoise(p * freq)) * amp;\n freq *= lacunarity;\n amp *= gain;\n }\n\n return sum;\n }\n\n vec4 samplerFire(vec3 p, vec4 scale) {\n vec2 st = vec2(sqrt(dot(p.xz, p.xz)), p.y);\n\n if(st.x <= 0.0 || st.x >= 1.0 || st.y <= 0.0 || st.y >= 1.0) {\n return vec4(0.0);\n }\n\n p.y -= (seed + time) * scale.w;\n p *= scale.xyz;\n\n st.y += sqrt(st.y) * magnitude * turbulence(p);\n\n if(st.y <= 0.0 || st.y >= 1.0) {\n return vec4(0.0);\n }\n\n return texture2D(fireTex, st);\n }\n\n vec3 localize(vec3 p) {\n return (invModelMatrix * vec4(p, 1.0)).xyz;\n }\n\n void main() {\n vec3 rayPos = vWorldPos;\n vec3 rayDir = normalize(rayPos - cameraPosition);\n float rayLen = 0.0288 * length(scale.xyz);\n\n vec4 col = vec4(0.0);\n\n for(int i = 0; i < ITERATIONS; i++) {\n rayPos += rayDir * rayLen;\n vec3 lp = localize(rayPos);\n lp.y += 0.5;\n lp.xz *= 2.0;\n col += samplerFire(lp, noiseScale);\n }\n\n // Apply color tint to the fire\n col.rgb *= color;\n col.a = col.r;\n gl_FragColor = col;\n }\n "; }; //# sourceMappingURL=FireShader.d.ts.map