Flying Through Clouds (Like Flight of the Navigator)
This reminds me of the movie 'flight of the navigator'! An all time classic in my opinion - when the boy flies the space ship through the clouds? Do you remember?
Flight of the navigator movie - from the late 80s, when a boy fly's through the clouds in an alien space ship! A classic!
Of course, for those, who haven't seen it or haven't even heard of it (might be before your time) - well it's in lots of other movies - it's also a common thing to see if you fly a lot?
You can use the common concept of volumetric noise (smooth 3d noise) and mix it with a bit of fractals (fractal brownian noise).
The full code is given below and runs on the fragment shader - you can see the working implementation at the bottom in the links (run it in a browser).
Flying through clouds gif screenshot recording of the output for the shader.
@group(0) @binding(2) var <uniform> mytimer : f32;
fn randomsmooth(st: vec2<f32>) -> f32 { let i = floor(st * 3.0); let f = fract(st * 3.0);
let a = random(i); let b = random(i + vec2<f32>(1.0, 0.0)); let c = random(i + vec2<f32>(0.0, 1.0)); let d = random(i + vec2<f32>(1.0, 1.0));
let f2 = f * f * (3.0 - 2.0 * f);
let x1 = mix(a, b, f2.x); let x2 = mix(c, d, f2.x); return mix(x1, x2, f2.y); }
fn setCamera(ro: vec3<f32>, ta: vec3<f32>, cr: f32) -> mat3x3<f32> { let cw = normalize(ta - ro); let cp = vec3<f32>(sin(cr), cos(cr), 0.0); let cu = normalize(cross(cw, cp)); let cv = normalize(cross(cu, cw)); return mat3x3<f32>(cu, cv, cw); }
// smooth noise in 3d fn noise(x: vec3<f32>, tt:vec2<f32>) -> f32 {
let p = floor(x); let f = fract(x); let f2 = f * f * (3.0 - 2.0 * f);
let uv = (p.xy + p.z ) + f.xy;
// modify smooth noise 2d to work with 3d return randomsmooth( uv * 2.0 ) * 0.5; }
// fractal brownian noise - make the noise more fractal and look like a cloud fn fbn(p: vec3<f32>, time: f32, uv: vec2<f32>) -> f32 { var q = p - vec3<f32>(0.0, 0.1, 1.0) * time; var f: f32; f = 0.50000 * noise(q, uv); q = q * 4.02; f += 0.25000 * noise(q, uv); q = q * 4.03; f += 0.12500 * noise(q, uv); q = q * 4.01; f += 0.06250 * noise(q, uv); q = q * 4.02; f += 0.03125 * noise(q, uv)*4.0; return clamp(1.5 - p.y - 2.0 + 1.75 * f, 0.0, 1.0); }
// Implementations for map4, map3, and map2 are similar with adjusted noise calculations. fn raymarch(ro: vec3<f32>, rd: vec3<f32>, bgcol: vec3<f32>, time: f32, uv:vec2<f32> ) -> vec4<f32> { var sum = vec4<f32>(0.0); var t = 0.0;//0.5 * randomsmooth( ro.xy);
for (var i = 0; i < 120; i++) { let pos = ro + t * rd; if (pos.y < -3.0 || pos.y > 2.0 || sum.a > 0.99) { break; } let den = fbn(pos, time, uv); // Fractal brownian noise if (den > 0.01 ) { let dif = clamp((den - fbn(pos + 0.3 * sundir, time, uv)) / 0.6, 0.0, 1.0); let lin = vec3<f32>(1.0, 0.6, 0.3) * dif + vec3<f32>(0.91, 0.98, 1.05); var col = vec4<f32>(mix(vec3<f32>(1.0, 0.95, 0.8), vec3<f32>(0.25, 0.3, 0.35), den), den); col = vec4(col.xyz*lin, col.w); col = vec4( mix(col.xyz, bgcol, 1.0 - exp(-0.003 * t * t)), col.w ); col = vec4( col.xyz, col.w*0.4 ); col = vec4( col.rgb*col.a, col.a ); sum += col * (1.0 - sum.a); } t += 0.02; // t += max(0.06, 0.05 * t)*0.5; }
@fragment fn main(@location(0) uvs: vec2<f32>) -> @location(0) vec4<f32> { let resolution = vec2<f32>( 512, 512 ); let mouse = vec2<f32>(0.0); let fragCoord = uvs * resolution;
let p = (2.0 * fragCoord - resolution.xy) / resolution.y; let m = mouse.xy / resolution.xy;
let ro = 4.0 * normalize(vec3<f32>(sin(3.0 * m.x), 0.8 * m.y, cos(3.0 * m.x))) - vec3<f32>(0.0, 0.1, 0.0); let ta = vec3<f32>(0.0, -1.0, 0.0); let ca = setCamera(ro, ta, 0.07 * cos(0.25 * mytimer));
let rd = ca * normalize(vec3<f32>(p, 1.5));
return render(ro, rd, mytimer, uvs); }
The main points of the implementation is the noise - smooth 3d point (from a 3d point you can calculate noise value). You then use a simple ray marching algorithm.
As you're flying above the clouds you want to bias the noise so the bottom is denser - top has no clouds (or very few) - so it still looks like you're flying through the clouds (not stuck inside one).