struct VertexOutput { @builtin(position) pos: vec4, @location(0) eye_pos: vec3, @location(1) world_pos: vec3, @location(2) cube_pos: vec3, @location(3) root_color: vec4, @location(4) root_subdivided: u32 } const N: u32 = 4; const N3: u32 = N * N * N; const MAX_DEPTH: u32 = 4; struct StructureTile { children: array, } struct ColorTile { colors: array, 64> } // BG @group(0) @binding(0) var structure_tiles : array; @group(0) @binding(1) var color_tiles : array; struct TraverseResult { albedo: vec4, normal: vec3, position: vec3, } fn traverse(max_depth: u32, root_color: vec4, root_subdivided: bool, eye_pos: vec3, ray_dir: vec3) -> TraverseResult { var normal = max_mask3(eye_pos) * sign(ray_dir); if max_depth == 0 || !root_subdivided { var res: TraverseResult; res.albedo = root_color; res.normal = normal; res.position = eye_pos; return res; //return viridis_quintic(0.); } // var node_structs = structure_tiles[0]; // var node_colors = color_tiles[0]; var node_tile_idx = u32(0); var stack = array(0, 0, 0, 0); var voxel_scale = N * N * N; var scale_exp = 3; let voxel_scale_lut = array( 1, N, N * N, N * N * N ); let dist = 1. / ray_dir; let origin = eye_pos * 256.; var pos = origin; // In voxel space let offset = - origin * dist; let wall_offset = select(vec3(0.), vec3(1.), ray_dir > vec3(0.)); let step = select(vec3(-1), vec3(1), ray_dir > vec3(0.)); var voxel_pos = clamp(vec3(floor(pos)), vec3(0), vec3(N * N * N * N - 1)); let max = 60.; for(var iter = 0; iter < 256; iter++) { var child_pos = voxel_pos >> vec3(scale_exp * 2); var local_child_pos = child_pos & vec3(3); var child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N; while (structure_tiles[node_tile_idx].children[child_idx] >> 31) != 0 && (u32(4 - scale_exp) < max_depth) { stack[scale_exp] = u32(node_tile_idx); scale_exp -= 1; voxel_scale = voxel_scale_lut[scale_exp]; node_tile_idx = structure_tiles[node_tile_idx].children[child_idx] & 0x3fffffff; child_pos = voxel_pos >> vec3(scale_exp * 2); local_child_pos = child_pos & vec3(3); child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N; } if color_tiles[node_tile_idx].colors[child_idx].w != 0 { var res: TraverseResult; res.albedo = color_tiles[node_tile_idx].colors[child_idx]; res.normal = vec3(normal); res.position = pos; return res; // return vec4(0.5 * (dot(vec3(normal), vec3(1., 1., 1.)) + 1.2)); // return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3(child_pos)); // let color_lut = array, 4>( // vec3(1., 1., 0.2), // vec3(0.2, 1., 0.2), // vec3(0.2, 1., 1.), // vec3(1., 0.2, 1.), // ); // return vec4(color_lut[scale_exp], 1.) * sample_mat(vec3(child_pos)); // return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3(child_pos)); // return inferno_quintic(f32(iter) / max); } // Compute intersection let global_voxel = vec3(child_pos * voxel_scale); let cell_max = global_voxel + vec3(voxel_scale) * wall_offset; let t1 = fma(dist, cell_max, offset); let t_far = min(t1.x, min(t1.y, t1.z)); // Figure out which boundary we crossed to Figure out next neighbor normal = select(vec3(0.), vec3(step), vec3(t_far) == t1); let neighbor_min = vec3(global_voxel) + select(vec3(0), step * vec3(voxel_scale), vec3(t_far) == t1); let neighbor_max = neighbor_min + vec3(voxel_scale); pos = clamp(origin + t_far * ray_dir, vec3(neighbor_min), vec3(neighbor_max) - vec3(1.)); //return vec4(vec3(neighbor_min) / 256., 1.); voxel_pos = vec3(floor(pos)); let diff = vec3(pos + 256) ^ vec3(global_voxel + 256); let diff_exp = (firstLeadingBit((diff.x | diff.y | diff.z)) >> 1); if diff_exp > u32(scale_exp) { if diff_exp > 3 { discard; //return viridis_quintic(f32(iter) / max); } scale_exp = i32(diff_exp); voxel_scale = voxel_scale_lut[scale_exp]; node_tile_idx = stack[scale_exp]; } } var res: TraverseResult; res.albedo = vec4(1., 0., 1., 1.); res.normal = vec3(1., 0., 0.); return res; } struct FragmentOutput { @location(0) albedo: vec4, @location(1) position: vec4, @location(2) normal: vec4, } @fragment fn fragment_main(in: VertexOutput) -> FragmentOutput { var hit_pos = vec3(0.); let dir = normalize(in.world_pos.xyz - in.eye_pos); let chunk_size = 4 * 4 * 4 * 4; let aabb = intersectAABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.)); let norm = -intersect_normal_AABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.)); hit_pos = in.eye_pos + max(aabb.x, 0.) * dir - in.cube_pos; let cube_color = vec3(1.); var pos = hit_pos * f32(chunk_size); let step = vec3( select(-1, 1, dir.x > 0.), select(-1, 1, dir.y > 0.), select(-1, 1, dir.z > 0.) ); var voxel = vec3( clamp(i32(floor(pos.x)), 0, chunk_size - 1), clamp(i32(floor(pos.y)), 0, chunk_size - 1), clamp(i32(floor(pos.z)), 0, chunk_size - 1), ); var div = 1; var overlay = 1.; for(var i = 1; i <= 4; i++) { let x = (voxel.x / div + voxel.y / div + voxel.z / div) % 2 == 0; overlay -= select(0., 1. / (f32(i) * 2.5), x); div *= 4; } overlay = 1.; let max_depth = 4; let glob_dist = aabb.x / 1.5; // Pixel footprint let depth = clamp(4 - u32(floor(log(glob_dist) / log(4.))), 0, 4); let res = traverse(depth, in.root_color, in.root_subdivided != 0, hit_pos, dir); var output: FragmentOutput; output.albedo = res.albedo; output.normal= vec4(-res.normal, 1.); output.position = vec4(res.position, 1.); return output; } fn min_mask3(v: vec3) -> vec3 { let min = min(v.x, min(v.y, v.z)); return vec3 ( select(0., 1., v.x == min), select(0., 1., v.y == min), select(0., 1., v.z == min), ); } fn min_mask3i32(v: vec3) -> vec3 { let min = min(v.x, min(v.y, v.z)); return vec3 ( select(0, 1, v.x == min), select(0, 1, v.y == min), select(0, 1, v.z == min), ); } fn max_mask3(v: vec3) -> vec3 { let max = max(v.x, max(v.y, v.z)); return vec3 ( select(0., 1., v.x == max), select(0., 1., v.y == max), select(0., 1., v.z == max), ); } fn intersectAABB(rayOrigin: vec3, rayDir: vec3, boxMin: vec3, boxMax: vec3) -> vec2 { let tMin = (boxMin - rayOrigin) / rayDir; let tMax = (boxMax - rayOrigin) / rayDir; let t1 = min(tMin, tMax); let t2 = max(tMin, tMax); let tNear = max(max(t1.x, t1.y), t1.z); let tFar = min(min(t2.x, t2.y), t2.z); return vec2(tNear, tFar); }; fn intersect_normal_AABB(rayOrigin: vec3, rayDir: vec3, boxMin: vec3, boxMax: vec3) -> vec3 { let tMin = (boxMin - rayOrigin) / rayDir; let tMax = (boxMax - rayOrigin) / rayDir; let t1 = min(tMin, tMax); let tNear = max(max(t1.x, t1.y), t1.z); return select(vec3(0), sign(rayDir), vec3(tNear) == t1); }; fn inferno_quintic( xx: f32 ) -> vec4 { let x = saturate(xx); let x1 = vec4( 1.0, x, x * x, x * x * x ); // 1 x x2 x3 let x2 = x1 * x1.w * x; // x4 x5 x6 x7 return vec4(saturate( vec3( dot( x1.xyzw, vec4( -0.027780558, 1.228188385, 0.278906882, 3.892783760 ) ) + dot( x2.xy, vec2( -8.490712758, 4.069046086 ) ), dot( x1.xyzw, vec4( 0.014065206, 0.015360518, 1.605395918, -4.821108251 ) ) + dot( x2.xy, vec2( 8.389314011, -4.193858954 ) ), dot( x1.xyzw, vec4( -0.019628385, 3.122510347, -5.893222355, 2.798380308 ) ) + dot( x2.xy, vec2( -3.608884658, 4.324996022 ) ) ) ), 1.); } fn viridis_quintic( xx: f32 ) -> vec4 { let x = saturate( xx ); let x1 = vec4( 1.0, x, x * x, x * x * x ); // 1 x x2 x3 let x2 = x1 * x1.w * x; // x4 x5 x6 x7 return vec4(saturate( vec3( dot( x1.xyzw, vec4( 0.280268003, -0.143510503, 2.225793877, -14.815088879 ) ) + dot( x2.xy, vec2( 25.212752309, -11.772589584 ) ), dot( x1.xyzw, vec4( -0.002117546, 1.617109353, -1.909305070, 2.701152864 ) ) + dot( x2.xy, vec2( -1.685288385, 0.178738871 ) ), dot( x1.xyzw, vec4( 0.300805501, 2.614650302, -12.019139090, 28.933559110 ) ) + dot( x2.xy, vec2( -33.491294770, 13.762053843 ) ) ) ), 1.); }