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?
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).
@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).