279 lines
9.1 KiB
WebGPU Shading Language
279 lines
9.1 KiB
WebGPU Shading Language
struct VertexOutput
|
|
{
|
|
@builtin(position) pos: vec4<f32>,
|
|
@location(0) eye_pos: vec3<f32>,
|
|
@location(1) world_pos: vec3<f32>,
|
|
@location(2) cube_pos: vec3<f32>,
|
|
@location(3) root_color: vec4<f32>,
|
|
@location(4) root_subdivided: u32
|
|
}
|
|
|
|
const N: u32 = 4;
|
|
const N3: u32 = N * N * N;
|
|
const MAX_DEPTH: u32 = 4;
|
|
|
|
struct StructureTile
|
|
{
|
|
children: array<u32, 64>,
|
|
}
|
|
|
|
struct ColorTile
|
|
{
|
|
colors: array<vec4<f32>, 64>
|
|
}
|
|
|
|
// BG
|
|
@group(0) @binding(0) var<storage> structure_tiles : array<StructureTile>;
|
|
@group(0) @binding(1) var<storage> color_tiles : array<ColorTile>;
|
|
|
|
struct TraverseResult
|
|
{
|
|
albedo: vec4<f32>,
|
|
normal: vec3<f32>,
|
|
position: vec3<f32>,
|
|
}
|
|
|
|
fn traverse(max_depth: u32, root_color: vec4<f32>, root_subdivided: bool, eye_pos: vec3<f32>, ray_dir: vec3<f32>) -> 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<u32, 4>(0, 0, 0, 0);
|
|
|
|
var voxel_scale = N * N * N;
|
|
var scale_exp = 3;
|
|
let voxel_scale_lut = array<u32, 4>(
|
|
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<u32>(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<u32>(scale_exp * 2);
|
|
var local_child_pos = child_pos & vec3<u32>(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<u32>(scale_exp * 2);
|
|
local_child_pos = child_pos & vec3<u32>(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<f32>(normal);
|
|
res.position = pos;
|
|
return res;
|
|
// return vec4(0.5 * (dot(vec3<f32>(normal), vec3<f32>(1., 1., 1.)) + 1.2));
|
|
// return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3<i32>(child_pos));
|
|
// let color_lut = array<vec3<f32>, 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<i32>(child_pos));
|
|
// return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3<i32>(child_pos));
|
|
// return inferno_quintic(f32(iter) / max);
|
|
}
|
|
|
|
// Compute intersection
|
|
let global_voxel = vec3<f32>(child_pos * voxel_scale);
|
|
let cell_max = global_voxel + vec3<f32>(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<f32>(step), vec3(t_far) == t1);
|
|
let neighbor_min = vec3<i32>(global_voxel) + select(vec3(0), step * vec3<i32>(voxel_scale), vec3(t_far) == t1);
|
|
let neighbor_max = neighbor_min + vec3<i32>(voxel_scale);
|
|
pos = clamp(origin + t_far * ray_dir, vec3<f32>(neighbor_min), vec3<f32>(neighbor_max) - vec3<f32>(1.));
|
|
//return vec4(vec3<f32>(neighbor_min) / 256., 1.);
|
|
|
|
voxel_pos = vec3<u32>(floor(pos));
|
|
|
|
let diff = vec3<u32>(pos + 256) ^ vec3<u32>(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<f32>(1., 0., 0.);
|
|
return res;
|
|
}
|
|
|
|
struct FragmentOutput
|
|
{
|
|
@location(0) albedo: vec4<f32>,
|
|
@location(1) position: vec4<f32>,
|
|
@location(2) normal: vec4<f32>,
|
|
}
|
|
|
|
@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<i32>(
|
|
select(-1, 1, dir.x > 0.),
|
|
select(-1, 1, dir.y > 0.),
|
|
select(-1, 1, dir.z > 0.)
|
|
);
|
|
|
|
var voxel = vec3<i32>(
|
|
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<f32>) -> vec3<f32>
|
|
{
|
|
let min = min(v.x, min(v.y, v.z));
|
|
|
|
return vec3<f32>
|
|
(
|
|
select(0., 1., v.x == min),
|
|
select(0., 1., v.y == min),
|
|
select(0., 1., v.z == min),
|
|
);
|
|
}
|
|
|
|
fn min_mask3i32(v: vec3<f32>) -> vec3<i32>
|
|
{
|
|
let min = min(v.x, min(v.y, v.z));
|
|
|
|
return vec3<i32>
|
|
(
|
|
select(0, 1, v.x == min),
|
|
select(0, 1, v.y == min),
|
|
select(0, 1, v.z == min),
|
|
);
|
|
}
|
|
|
|
fn max_mask3(v: vec3<f32>) -> vec3<f32>
|
|
{
|
|
let max = max(v.x, max(v.y, v.z));
|
|
|
|
return vec3<f32>
|
|
(
|
|
select(0., 1., v.x == max),
|
|
select(0., 1., v.y == max),
|
|
select(0., 1., v.z == max),
|
|
);
|
|
}
|
|
|
|
fn intersectAABB(rayOrigin: vec3<f32>, rayDir: vec3<f32>, boxMin: vec3<f32>, boxMax: vec3<f32>) -> vec2<f32> {
|
|
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<f32>, rayDir: vec3<f32>, boxMin: vec3<f32>, boxMax: vec3<f32>) -> vec3<f32> {
|
|
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<f32>
|
|
{
|
|
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<f32>
|
|
{
|
|
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.);
|
|
}
|