From 5bf3f20ea4338f18db998dcb5d039388f056a0b2 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Fri, 7 Jun 2024 01:32:44 -0400 Subject: [PATCH] lights, shader work, cube, anti aliasing --- Cargo.lock | 20 ++ Cargo.toml | 1 + src/client/render/renderer.rs | 20 +- src/client/render/voxel/group.rs | 5 +- src/client/render/voxel/pipeline.rs | 50 +++- src/client/render/voxel/shader.wgsl | 278 +++------------------- src/client/render/voxel/shader_stuff.wgsl | 235 +++++++++++++----- 7 files changed, 295 insertions(+), 314 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fef7c04..417e9f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -801,6 +801,7 @@ dependencies = [ "indexmap", "log", "num-traits", + "pp-rs", "rustc-hash", "spirv", "termcolor", @@ -1092,6 +1093,7 @@ dependencies = [ "pollster", "rand", "simba", + "smaa", "wgpu", "winit", ] @@ -1123,6 +1125,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "pp-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb458bb7f6e250e6eb79d5026badc10a3ebb8f9a15d1fff0f13d17c71f4d6dee" +dependencies = [ + "unicode-xid", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1362,6 +1373,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "smaa" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08dff9e842337b9aaa0e4e920676dc582cc6e6cd1056393ed209524a1758146b" +dependencies = [ + "wgpu", +] + [[package]] name = "smallvec" version = "1.13.2" diff --git a/Cargo.toml b/Cargo.toml index 8e88c7e..ca376b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,6 @@ nalgebra = {version="0.32.5", features=["bytemuck"]} pollster = "0.3" rand = "0.8.5" simba = "0.8.1" +smaa = "0.14.0" wgpu = "0.20" winit = {version="0.30", features=["serde"]} diff --git a/src/client/render/renderer.rs b/src/client/render/renderer.rs index fbb419c..e3a7c2a 100644 --- a/src/client/render/renderer.rs +++ b/src/client/render/renderer.rs @@ -1,7 +1,7 @@ -use std::sync::Arc; - +use smaa::{SmaaTarget, SmaaMode}; use super::voxel::VoxelPipeline; use crate::client::{rsc::CLEAR_COLOR, ClientState}; +use std::sync::Arc; use winit::{ dpi::PhysicalSize, window::{Fullscreen, Window}, @@ -17,6 +17,7 @@ pub struct Renderer<'a> { encoder: Option, staging_belt: wgpu::util::StagingBelt, voxel_pipeline: VoxelPipeline, + smaa_target: SmaaTarget, } impl<'a> Renderer<'a> { @@ -84,6 +85,15 @@ impl<'a> Renderer<'a> { // doesn't affect performance much and depends on "normal" zoom let staging_belt = wgpu::util::StagingBelt::new(4096 * 4); + let smaa_target = SmaaTarget::new( + &device, + &queue, + size.width, + size.height, + surface_format, + SmaaMode::Smaa1X, + ); + Self { size, voxel_pipeline: VoxelPipeline::new(&device, &config.format), @@ -94,6 +104,7 @@ impl<'a> Renderer<'a> { adapter, config, queue, + smaa_target, } } @@ -110,11 +121,12 @@ impl<'a> Renderer<'a> { .texture .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = self.encoder.take().unwrap_or(self.create_encoder()); + let smaa_frame = self.smaa_target.start_frame(&self.device, &self.queue, &view); { let render_pass = &mut encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, + view: &smaa_frame, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(CLEAR_COLOR), @@ -127,6 +139,7 @@ impl<'a> Renderer<'a> { }); self.voxel_pipeline.draw(render_pass); } + smaa_frame.resolve(); self.staging_belt.finish(); self.queue.submit(std::iter::once(encoder.finish())); @@ -157,6 +170,7 @@ impl<'a> Renderer<'a> { self.config.width = size.width; self.config.height = size.height; self.surface.configure(&self.device, &self.config); + self.smaa_target.resize(&self.device, size.width, size.height); } pub fn size(&self) -> &PhysicalSize { diff --git a/src/client/render/voxel/group.rs b/src/client/render/voxel/group.rs index 92bf26d..dce2357 100644 --- a/src/client/render/voxel/group.rs +++ b/src/client/render/voxel/group.rs @@ -1,9 +1,10 @@ -use nalgebra::{Transform3, Vector3}; +use nalgebra::{Projective3, Vector3}; #[repr(C, align(16))] #[derive(Debug, Clone, Copy, PartialEq, bytemuck::Zeroable)] pub struct VoxelGroup { - pub transform: Transform3, + pub transform: Projective3, + pub transform_inv: Projective3, pub dimensions: Vector3, pub offset: u32, } diff --git a/src/client/render/voxel/pipeline.rs b/src/client/render/voxel/pipeline.rs index fcfceb4..bf9f6e9 100644 --- a/src/client/render/voxel/pipeline.rs +++ b/src/client/render/voxel/pipeline.rs @@ -1,4 +1,4 @@ -use nalgebra::{Rotation3, Transform3, Translation3, Vector3}; +use nalgebra::{Projective3, Rotation3, Transform3, Translation3, UnitVector3, Vector3}; use super::{color::VoxelColor, group::VoxelGroup, view::View}; use crate::client::render::{ @@ -167,6 +167,19 @@ impl VoxelPipeline { } } data.append(&mut data2); + let lx3 = 3; + let ly3 = 3; + let lz3 = 3; + let offset3 = data.len(); + data.append(&mut vec![ + VoxelColor { + r: 255, + g: 0, + b: 255, + a: 255, + }; + lx3 * ly3 * lz3 + ]); self.voxels.update( device, encoder, @@ -174,33 +187,56 @@ impl VoxelPipeline { data.len(), &[ArrBufUpdate { offset: 0, data }], ); - let thing = Translation3::new(0.0, 0.0, 20.0) + let proj = Projective3::identity() + * Translation3::new(0.0, 0.0, 20.0) * Rotation3::from_axis_angle(&Vector3::y_axis(), 0.5) * Translation3::new(-(lx as f32 / 2.0), -(ly as f32 / 2.0), -(lz as f32 / 2.0)); let group = VoxelGroup { - transform: Transform3::identity() * thing.inverse(), + transform: proj, + transform_inv: proj.inverse(), dimensions: Vector3::new(lx as u32, ly as u32, lz as u32), offset: 0, }; - let thing2 = Translation3::new(0.0, 2.5, 20.0) + let proj2 = Projective3::identity() + * Translation3::new(0.0, -2.1, 20.0) * Translation3::new( -(lx2 as f32 / 2.0), -(ly2 as f32 / 2.0), -(lz2 as f32 / 2.0), ); let group2 = VoxelGroup { - transform: Transform3::identity() * thing2.inverse(), + transform: proj2, + transform_inv: proj2.inverse(), dimensions: Vector3::new(lx2 as u32, ly2 as u32, lz2 as u32), offset: offset2 as u32, }; + let proj3 = Projective3::identity() + * Translation3::new(0.0, 0.0, 10.0) + * Rotation3::from_axis_angle(&Vector3::y_axis(), std::f32::consts::PI / 4.0) + * Rotation3::from_axis_angle( + &UnitVector3::new_normalize(Vector3::new(1.0, 0.0, 1.0)), + std::f32::consts::PI / 4.0, + ) + * Translation3::new( + -(lx3 as f32 / 2.0), + -(ly3 as f32 / 2.0), + -(lz3 as f32 / 2.0), + ); + let group3 = VoxelGroup { + transform: proj3, + transform_inv: proj3.inverse(), + dimensions: Vector3::new(lx3 as u32, ly3 as u32, lz3 as u32), + offset: offset3 as u32, + }; + let groups = vec![group, group2, group3]; self.voxel_groups.update( device, encoder, belt, - 2, + groups.len(), &[ArrBufUpdate { offset: 0, - data: vec![group, group2], + data: groups, }], ); self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { diff --git a/src/client/render/voxel/shader.wgsl b/src/client/render/voxel/shader.wgsl index 918b1ab..5565848 100644 --- a/src/client/render/voxel/shader.wgsl +++ b/src/client/render/voxel/shader.wgsl @@ -15,6 +15,7 @@ struct View { struct VoxelGroup { transform: mat4x4, + transform_inv: mat4x4, dimensions: vec3, offset: u32, }; @@ -44,11 +45,6 @@ fn vs_main( // Fragment shader -const ZERO3F = vec3(0.0); -const ZERO2F = vec2(0.0); -const DEPTH = 20; -const FULL_ALPHA = 0.9999; - @fragment fn fs_main( in: VertexOutput, @@ -58,247 +54,26 @@ fn fs_main( let aspect = win_dim.y / win_dim.x; let pixel_pos = vec3( (in.clip_position.xy / win_dim - vec2(0.5)) * vec2(2.0, -2.0 * aspect), - 0.0 + 1.0 ); // move to position in world - let dir_view = view.transform * vec4(normalize(pixel_pos + vec3(0.0, 0.0, 1.0)), 0.0); - let pos_view = view.transform * vec4(pixel_pos, 1.0); + let pos = view.transform * vec4(pixel_pos, 1.0); + let dir = view.transform * vec4(normalize(pixel_pos), 0.0); - var color = trace_full(pos_view, dir_view); + var color = trace_full(pos, dir); + let light_mult = clamp((-dot(dir.xyz, normalize(GLOBAL_LIGHT)) - 0.99) * 200.0, 0.0, 1.0); + let sky_color = light_mult * vec3(1.0, 1.0, 1.0); + color += vec4(sky_color * (1.0 - color.a), 1.0 - color.a); color.a = 1.0; return color; } -fn trace_one(gi: u32, pos_view: vec4, dir_view: vec4) -> vec4 { - let group = voxel_groups[gi]; - let dim_f = vec3(group.dimensions); - let dim_i = vec3(group.dimensions); - - // transform so that group is at 0,0 - var pos = (group.transform * pos_view).xyz; - let dir = (group.transform * dir_view).xyz; - - let dir_if = sign(dir); - - - - // find where ray intersects with group - let plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; - var t_offset = 0.0; - if outside3f(pos, ZERO3F, dim_f) { - // time of intersection; x = td + p, solve for t - let t_i = (plane_point - pos) / dir; - // points of intersection - let px = pos + t_i.x * dir; - let py = pos + t_i.y * dir; - let pz = pos + t_i.z * dir; - - // check if point is in bounds - let hit = vec3( - inside2f(px.yz, ZERO2F, dim_f.yz), - inside2f(py.xz, ZERO2F, dim_f.xz), - inside2f(pz.xy, ZERO2F, dim_f.xy), - ) && (t_i > ZERO3F); - if !any(hit) { - return vec4(0.0); - } - pos = select(select(pz, py, hit.y), px, hit.x); - t_offset = select(select(t_i.z, t_i.y, hit.y), t_i.x, hit.x); - } - var vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); - - - - let dir_i = vec3(dir_if); - // time to move 1 unit using dir - let inc_t = abs(1.0 / dir); - let corner = vec3(vox_pos) + vec3(0.5) + dir_if / 2.0; - - // time of next plane hit for each direction - var next_t = inc_t * abs(pos - corner); - var color = vec4(0.0); - loop { - let i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; - var vcolor = unpack4x8unorm(voxels[i]); - - // select next voxel to move to next based on least time - let axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); - vox_pos[axis] += dir_i[axis]; - next_t[axis] += inc_t[axis]; - color += vec4(vcolor.xyz * vcolor.a * (1.0 - color.a), (1.0 - color.a) * vcolor.a); - - if color.a >= FULL_ALPHA || vox_pos[axis] < 0 || vox_pos[axis] >= dim_i[axis] { - break; - } - } - return color; -} - -fn trace_opaque(pos_view: vec4, dir_view: vec4) -> vec4 { - var depth = 9999999999999.0; - var result = vec4(0.0); - for (var gi: u32 = 0; gi < arrayLength(&voxel_groups); gi = gi + 1) { - let group = voxel_groups[gi]; - let dim_f = vec3(group.dimensions); - let dim_i = vec3(group.dimensions); - - // transform so that group is at 0,0 - var pos = (group.transform * pos_view).xyz; - let dir = (group.transform * dir_view).xyz; - - let dir_if = sign(dir); - - - - // find where ray intersects with group - let plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; - var t_offset = 0.0; - if outside3f(pos, ZERO3F, dim_f) { - // time of intersection; x = td + p, solve for t - let t_i = (plane_point - pos) / dir; - // points of intersection - let px = pos + t_i.x * dir; - let py = pos + t_i.y * dir; - let pz = pos + t_i.z * dir; - - // check if point is in bounds - let hit = vec3( - inside2f(px.yz, ZERO2F, dim_f.yz), - inside2f(py.xz, ZERO2F, dim_f.xz), - inside2f(pz.xy, ZERO2F, dim_f.xy), - ) && (t_i > ZERO3F); - if !any(hit) { - continue; - } - pos = select(select(pz, py, hit.y), px, hit.x); - t_offset = select(select(t_i.z, t_i.y, hit.y), t_i.x, hit.x); - } - var vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); - - - - let dir_i = vec3(dir_if); - // time to move 1 unit using dir - let inc_t = abs(1.0 / dir); - let corner = vec3(vox_pos) + vec3(0.5) + dir_if / 2.0; - - // time of next plane hit for each direction - var next_t = inc_t * abs(pos - corner); - var t = 0.0; - var prev_t = t; - var color = vec4(0.0); - var gdepth = 9999999999999.0; - loop { - let i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; - var vcolor = unpack4x8unorm(voxels[i]); - - // select next voxel to move to next based on least time - let axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); - prev_t = t; - t = next_t[axis]; - vox_pos[axis] += dir_i[axis]; - next_t[axis] += inc_t[axis]; - - // hit a voxel - if vcolor.a > 0.0 { - let full_t = t_offset + prev_t; - gdepth = min(gdepth, full_t); - color = vcolor; - break; - } - - if vox_pos[axis] < 0 || vox_pos[axis] >= dim_i[axis] { - break; - } - } - result = select(result, color, gdepth < depth); - depth = min(gdepth, depth); - } - return result; -} - -fn trace_first(pos_view: vec4, dir_view: vec4) -> vec4 { - var depth = 9999999999999.0; - var result = vec4(0.0); - for (var gi: u32 = 0; gi < arrayLength(&voxel_groups); gi = gi + 1) { - let group = voxel_groups[gi]; - let dim_f = vec3(group.dimensions); - let dim_i = vec3(group.dimensions); - - // transform so that group is at 0,0 - var pos = (group.transform * pos_view).xyz; - let dir = (group.transform * dir_view).xyz; - - let dir_if = sign(dir); - - - - // find where ray intersects with group - let plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; - var t_offset = 0.0; - if outside3f(pos, ZERO3F, dim_f) { - // time of intersection; x = td + p, solve for t - let t_i = (plane_point - pos) / dir; - // points of intersection - let px = pos + t_i.x * dir; - let py = pos + t_i.y * dir; - let pz = pos + t_i.z * dir; - - // check if point is in bounds - let hit = vec3( - inside2f(px.yz, ZERO2F, dim_f.yz), - inside2f(py.xz, ZERO2F, dim_f.xz), - inside2f(pz.xy, ZERO2F, dim_f.xy), - ) && (t_i > ZERO3F); - if !any(hit) { - continue; - } - pos = select(select(pz, py, hit.y), px, hit.x); - t_offset = select(select(t_i.z, t_i.y, hit.y), t_i.x, hit.x); - } - var vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); - - - - let dir_i = vec3(dir_if); - // time to move 1 unit using dir - let inc_t = abs(1.0 / dir); - let corner = vec3(vox_pos) + vec3(0.5) + dir_if / 2.0; - - // time of next plane hit for each direction - var next_t = inc_t * abs(pos - corner); - var t = 0.0; - var prev_t = t; - var color = vec4(0.0); - var gdepth = 9999999999999.0; - loop { - let i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; - var vcolor = unpack4x8unorm(voxels[i]); - - // select next voxel to move to next based on least time - let axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); - prev_t = t; - t = next_t[axis]; - vox_pos[axis] += dir_i[axis]; - next_t[axis] += inc_t[axis]; - - // hit a voxel - if vcolor.a > 0.0 { - let full_t = t_offset + prev_t; - gdepth = min(gdepth, full_t); - color += vec4(vcolor.xyz * vcolor.a * (1.0 - color.a), (1.0 - color.a) * vcolor.a); - } - - if color.a >= FULL_ALPHA || vox_pos[axis] < 0 || vox_pos[axis] >= dim_i[axis] { - break; - } - } - result = select(result, color, gdepth < depth); - depth = min(gdepth, depth); - } - return result; -} +const ZERO3F = vec3(0.0); +const ZERO2F = vec2(0.0); +const DEPTH = 20; +const FULL_ALPHA = 0.9999; +const GLOBAL_LIGHT = vec3(-0.5, -4.0, 2.0); fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { // GPUs hate this @@ -319,6 +94,10 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { return color; } +// apparently GPUs don't like dynamic indexing cause they just have +// a ton of registers instead of fast memory access; should probably +// try to optimize for that where I can + fn apply_group( gi: u32, pos_view: vec4, dir_view: vec4, depths: ptr>, @@ -329,13 +108,22 @@ fn apply_group( let dim_i = vec3(group.dimensions); // transform so that group is at 0,0 - var pos = (group.transform * pos_view).xyz; - let dir = (group.transform * dir_view).xyz; + var pos = (group.transform_inv * pos_view).xyz; + let dir = (group.transform_inv * dir_view).xyz; let dir_if = sign(dir); + // calculate normals; maybe should do this on cpu? + let normals = mat3x3( + (group.transform * vec4(dir_if.x, 0.0, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, dir_if.y, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, 0.0, dir_if.z, 0.0)).xyz, + ); + var next_normal = vec3(0.0, 0.0, 0.0); + let norm_light = normalize(GLOBAL_LIGHT); + // find where ray intersects with group let plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; var t_offset = 0.0; @@ -358,6 +146,7 @@ fn apply_group( } pos = select(select(pz, py, hit.y), px, hit.x); t_offset = select(select(t_i.z, t_i.y, hit.y), t_i.x, hit.x); + next_normal = select(select(normals[2], normals[1], hit.y), normals[0], hit.x); } var vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); @@ -378,10 +167,13 @@ fn apply_group( loop { let i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; var vcolor = unpack4x8unorm(voxels[i]); + let normal = next_normal; // select next voxel to move to next based on least time let axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); + next_normal = select(select(normals[2], normals[1], axis == 1), normals[0], axis == 0); prev_t = t; + // might want to make multiplication mask w select instead of dynamically indexing t = next_t[axis]; vox_pos[axis] += dir_i[axis]; next_t[axis] += inc_t[axis]; @@ -400,15 +192,17 @@ fn apply_group( a = unpack4x8unorm((*colors)[depth]).a; } var move_d = depth; - // move further depth hits back + // move further depth hits back (top 10 efficient algorithms) while move_d < DEPTH - 1 && unpack4x8unorm((*colors)[move_d]).a != 0.0 { (*colors)[move_d + 1] = (*colors)[move_d]; (*depths)[move_d + 1] = (*depths)[move_d]; move_d += 1; } // add hit + let light = max(dot(norm_light, normal) * 1.3 + 0.1, 0.1); + var color = vec4(vcolor.xyz * light, vcolor.a); (*depths)[depth] = full_t; - (*colors)[depth] = voxels[i]; + (*colors)[depth] = pack4x8unorm(color); prev_a = vcolor.a; depth += 1; alpha += (1.0 - alpha) * vcolor.a; diff --git a/src/client/render/voxel/shader_stuff.wgsl b/src/client/render/voxel/shader_stuff.wgsl index 4717f8c..1533db7 100644 --- a/src/client/render/voxel/shader_stuff.wgsl +++ b/src/client/render/voxel/shader_stuff.wgsl @@ -1,62 +1,97 @@ -fn trace_first(pos_view: vec4, dir_view: vec4) -> vec4 { +fn trace_one(gi: u32, pos_view: vec4, dir_view: vec4) -> vec4 { + let group = voxel_groups[gi]; + let dim_f = vec3(group.dimensions); + let dim_i = vec3(group.dimensions); + + // transform so that group is at 0,0 + var pos = (group.transform * pos_view).xyz; + let dir = (group.transform * dir_view).xyz; + + let dir_if = sign(dir); + + + + // find where ray intersects with group + let plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; + var t_offset = 0.0; + if outside3f(pos, ZERO3F, dim_f) { + // time of intersection; x = td + p, solve for t + let t_i = (plane_point - pos) / dir; + // points of intersection + let px = pos + t_i.x * dir; + let py = pos + t_i.y * dir; + let pz = pos + t_i.z * dir; + + // check if point is in bounds + let hit = vec3( + inside2f(px.yz, ZERO2F, dim_f.yz), + inside2f(py.xz, ZERO2F, dim_f.xz), + inside2f(pz.xy, ZERO2F, dim_f.xy), + ) && (t_i > ZERO3F); + if !any(hit) { + return vec4(0.0); + } + pos = select(select(pz, py, hit.y), px, hit.x); + t_offset = select(select(t_i.z, t_i.y, hit.y), t_i.x, hit.x); + } + var vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); + + + + let dir_i = vec3(dir_if); + // time to move 1 unit using dir + let inc_t = abs(1.0 / dir); + let corner = vec3(vox_pos) + vec3(0.5) + dir_if / 2.0; + + // time of next plane hit for each direction + var next_t = inc_t * abs(pos - corner); + var color = vec4(0.0); + loop { + let i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; + var vcolor = unpack4x8unorm(voxels[i]); + + // select next voxel to move to next based on least time + let axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); + vox_pos[axis] += dir_i[axis]; + next_t[axis] += inc_t[axis]; + color += vec4(vcolor.xyz * vcolor.a * (1.0 - color.a), (1.0 - color.a) * vcolor.a); + + if color.a >= FULL_ALPHA || vox_pos[axis] < 0 || vox_pos[axis] >= dim_i[axis] { + break; + } + } + return color; +} + +fn trace_opaque(pos_view: vec4, dir_view: vec4) -> vec4 { var depth = 9999999999999.0; var result = vec4(0.0); - - var group: VoxelGroup; - var dim_f: vec3; - var dim_i: vec3; - var pos: vec3; - var dir: vec3; - var dir_if: vec3; - - var plane_point: vec3; - var t_offset: f32; - var t_i: vec3; - var px: vec3; - var py: vec3; - var pz: vec3; - var hit: vec3; - var vox_pos: vec3; - - var dir_i: vec3; - var inc_t: vec3; - var corner: vec3; - var next_t: vec3; - var t: f32; - var prev_t: f32; - var color: vec4; - var gdepth: f32; - var i: u32; - var vcolor: vec4; - var axis: i32; - var full_t: f32; - for (var gi: u32 = 0; gi < arrayLength(&voxel_groups); gi = gi + 1) { - group = voxel_groups[gi]; - dim_f = vec3(group.dimensions); - dim_i = vec3(group.dimensions); + let group = voxel_groups[gi]; + let dim_f = vec3(group.dimensions); + let dim_i = vec3(group.dimensions); // transform so that group is at 0,0 - pos = (group.transform * pos_view).xyz; - dir = (group.transform * dir_view).xyz; + var pos = (group.transform * pos_view).xyz; + let dir = (group.transform * dir_view).xyz; - dir_if = sign(dir); + let dir_if = sign(dir); // find where ray intersects with group - plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; - t_offset = 0.0; + let plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; + var t_offset = 0.0; if outside3f(pos, ZERO3F, dim_f) { // time of intersection; x = td + p, solve for t - t_i = (plane_point - pos) / dir; + let t_i = (plane_point - pos) / dir; // points of intersection - px = pos + t_i.x * dir; - py = pos + t_i.y * dir; - pz = pos + t_i.z * dir; + let px = pos + t_i.x * dir; + let py = pos + t_i.y * dir; + let pz = pos + t_i.z * dir; // check if point is in bounds - hit = vec3( + let hit = vec3( inside2f(px.yz, ZERO2F, dim_f.yz), inside2f(py.xz, ZERO2F, dim_f.xz), inside2f(pz.xy, ZERO2F, dim_f.xy), @@ -67,27 +102,27 @@ fn trace_first(pos_view: vec4, dir_view: vec4) -> vec4 { pos = select(select(pz, py, hit.y), px, hit.x); t_offset = select(select(t_i.z, t_i.y, hit.y), t_i.x, hit.x); } - vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); + var vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); - dir_i = vec3(dir_if); + let dir_i = vec3(dir_if); // time to move 1 unit using dir - inc_t = abs(1.0 / dir); - corner = vec3(vox_pos) + vec3(0.5) + dir_if / 2.0; + let inc_t = abs(1.0 / dir); + let corner = vec3(vox_pos) + vec3(0.5) + dir_if / 2.0; // time of next plane hit for each direction - next_t = inc_t * abs(pos - corner); - t = 0.0; - prev_t = t; - color = vec4(0.0); - gdepth = 9999999999999.0; + var next_t = inc_t * abs(pos - corner); + var t = 0.0; + var prev_t = t; + var color = vec4(0.0); + var gdepth = 9999999999999.0; loop { - i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; - vcolor = unpack4x8unorm(voxels[i]); + let i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; + var vcolor = unpack4x8unorm(voxels[i]); // select next voxel to move to next based on least time - axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); + let axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); prev_t = t; t = next_t[axis]; vox_pos[axis] += dir_i[axis]; @@ -95,12 +130,92 @@ fn trace_first(pos_view: vec4, dir_view: vec4) -> vec4 { // hit a voxel if vcolor.a > 0.0 { - full_t = t_offset + prev_t; + let full_t = t_offset + prev_t; + gdepth = min(gdepth, full_t); + color = vcolor; + break; + } + + if vox_pos[axis] < 0 || vox_pos[axis] >= dim_i[axis] { + break; + } + } + result = select(result, color, gdepth < depth); + depth = min(gdepth, depth); + } + return result; +} + +fn trace_first(pos_view: vec4, dir_view: vec4) -> vec4 { + var depth = 9999999999999.0; + var result = vec4(0.0); + for (var gi: u32 = 0; gi < arrayLength(&voxel_groups); gi = gi + 1) { + let group = voxel_groups[gi]; + let dim_f = vec3(group.dimensions); + let dim_i = vec3(group.dimensions); + + // transform so that group is at 0,0 + var pos = (group.transform * pos_view).xyz; + let dir = (group.transform * dir_view).xyz; + + let dir_if = sign(dir); + + + + // find where ray intersects with group + let plane_point = (vec3(1.0) - dir_if) / 2.0 * dim_f; + var t_offset = 0.0; + if outside3f(pos, ZERO3F, dim_f) { + // time of intersection; x = td + p, solve for t + let t_i = (plane_point - pos) / dir; + // points of intersection + let px = pos + t_i.x * dir; + let py = pos + t_i.y * dir; + let pz = pos + t_i.z * dir; + + // check if point is in bounds + let hit = vec3( + inside2f(px.yz, ZERO2F, dim_f.yz), + inside2f(py.xz, ZERO2F, dim_f.xz), + inside2f(pz.xy, ZERO2F, dim_f.xy), + ) && (t_i > ZERO3F); + if !any(hit) { + continue; + } + pos = select(select(pz, py, hit.y), px, hit.x); + t_offset = select(select(t_i.z, t_i.y, hit.y), t_i.x, hit.x); + } + var vox_pos = clamp(vec3(pos), vec3(0), dim_i - vec3(1)); + + + + let dir_i = vec3(dir_if); + // time to move 1 unit using dir + let inc_t = abs(1.0 / dir); + let corner = vec3(vox_pos) + vec3(0.5) + dir_if / 2.0; + + // time of next plane hit for each direction + var next_t = inc_t * abs(pos - corner); + var t = 0.0; + var prev_t = t; + var color = vec4(0.0); + var gdepth = 9999999999999.0; + loop { + let i = u32(vox_pos.x + vox_pos.y * dim_i.x + vox_pos.z * dim_i.x * dim_i.y) + group.offset; + var vcolor = unpack4x8unorm(voxels[i]); + + // select next voxel to move to next based on least time + let axis = select(select(2, 1, next_t.y < next_t.z), 0, next_t.x < next_t.y && next_t.x < next_t.z); + prev_t = t; + t = next_t[axis]; + vox_pos[axis] += dir_i[axis]; + next_t[axis] += inc_t[axis]; + + // hit a voxel + if vcolor.a > 0.0 { + let full_t = t_offset + prev_t; gdepth = min(gdepth, full_t); color += vec4(vcolor.xyz * vcolor.a * (1.0 - color.a), (1.0 - color.a) * vcolor.a); - if color.a >= FULL_ALPHA { - break; - } } if color.a >= FULL_ALPHA || vox_pos[axis] < 0 || vox_pos[axis] >= dim_i[axis] {