From 04e86bbe537f744dfe8ef90ff12b333dd1246d37 Mon Sep 17 00:00:00 2001 From: Paris DOUADY Date: Mon, 11 Dec 2023 01:00:22 +0100 Subject: [PATCH] stitch terrain to avoid t junctions + no vertex buffer + bit more settings --- assets/shaders/background.wgsl | 2 +- assets/shaders/terrain.vert.wgsl | 44 +++++++++--- engine/src/drawables/terrain.rs | 114 ++++++++++++++++++------------- engine/src/gfx.rs | 8 ++- engine/src/vertex_types.rs | 21 ------ engine_demo/src/main.rs | 3 + engine_demo/src/terrain.rs | 5 +- 7 files changed, 116 insertions(+), 81 deletions(-) diff --git a/assets/shaders/background.wgsl b/assets/shaders/background.wgsl index 11f14100..623ff445 100644 --- a/assets/shaders/background.wgsl +++ b/assets/shaders/background.wgsl @@ -31,7 +31,7 @@ fn frag(@location(0) in_pos: vec3, @builtin(position) position: vec4) 3.40282347E+38, ); - //color = textureSampleLevel(t_environment, s_environment, pos, 0.0).rgb; + //var color: vec3 = textureSampleLevel(t_environment, s_environment, pos, 0.0).rgb; color = color + max(pos.z + 0.1, 0.0) * 5.0 * textureSample(t_starfield, s_starfield, vec2(longitude, pos.z)).rgb; // starfield color = color + max(pos.z, 0.0) * 10000.0 * smoothstep(0.99993, 1.0, dot(fsun, pos)); // sun diff --git a/assets/shaders/terrain.vert.wgsl b/assets/shaders/terrain.vert.wgsl index e35e710d..838eec88 100644 --- a/assets/shaders/terrain.vert.wgsl +++ b/assets/shaders/terrain.vert.wgsl @@ -14,11 +14,12 @@ struct VertexOutput { } struct ChunkData { - lod: u32, - resolution: u32, + lod: u32, // 0 = most details, 1 = half details, 2 = quarter details, etc. + lod_pow2: f32, // 2^lod + resolution: u32, // number of vertices per side distance_lod_cutoff: f32, // max distance at which to switch to the next lod to have smooth transitions - cell_size: f32, - inv_cell_size: f32, + cell_size: f32, // size of a cell in world space at lod0 + inv_cell_size: f32, // 1 / cell_size } @group(0) @binding(0) var global: Uniforms; @@ -50,7 +51,33 @@ fn unpack_diffs(v: u32) -> vec2 { } @vertex -fn vert(@builtin(vertex_index) vid: u32, @location(0) in_position: vec2, @location(1) in_off: vec2) -> VertexOutput { +fn vert(@builtin(vertex_index) vid: u32, + @location(0) in_off: vec2, + @location(1) stitch_dir_flags: u32, // 4 lowest bits are 1 if we need to stitch in that direction. 0 = x+, 1 = y+, 2 = x-, 3 = y- + ) -> VertexOutput { + let idx_x: u32 = vid % cdata.resolution; + let idx_y: u32 = vid / cdata.resolution; + + var in_position: vec2 = vec2(f32(idx_x), f32(idx_y)) * cdata.cell_size * cdata.lod_pow2; + + let stitch_x_pos: bool = (stitch_dir_flags & 1u) != 0u; + let stitch_y_pos: bool = (stitch_dir_flags & 2u) != 0u; + let stitch_x_neg: bool = (stitch_dir_flags & 4u) != 0u; + let stitch_y_neg: bool = (stitch_dir_flags & 8u) != 0u; + + if (stitch_x_pos && idx_x == cdata.resolution - 1u && (idx_y % 2u) == 1u) { + in_position = in_position - vec2(0.0, cdata.cell_size * cdata.lod_pow2); + } + if (stitch_x_neg && idx_x == 0u && (idx_y % 2u) == 1u) { + in_position = in_position - vec2(0.0, cdata.cell_size * cdata.lod_pow2); + } + if (stitch_y_pos && (vid >= (cdata.resolution * (cdata.resolution - 1u))) && ((idx_x) % 2u) == 1u) { + in_position = in_position - vec2(cdata.cell_size * cdata.lod_pow2, 0.0); + } + if (stitch_y_neg && (vid < cdata.resolution) && ((idx_x) % 2u) == 1u) { + in_position = in_position - vec2(cdata.cell_size * cdata.lod_pow2, 0.0); + } + let tpos: vec2 = vec2((in_position + in_off) * cdata.inv_cell_size); let texLoad: vec2 = textureLoad(t_terraindata, tpos, 0).rg; @@ -60,8 +87,8 @@ fn vert(@builtin(vertex_index) vid: u32, @location(0) in_position: vec2, @l let step: i32 = i32(pow(2.0, f32(cdata.lod))); - let zf_off: vec2 = vec2( step * (i32(vid % cdata.resolution) % 2), - step * (i32(vid / cdata.resolution) % 2)); + let zf_off: vec2 = vec2( step * (i32(idx_x) % 2), + step * (i32(idx_y) % 2)); let world_pos: vec3 = vec3(in_position + in_off, height); @@ -73,7 +100,8 @@ fn vert(@builtin(vertex_index) vid: u32, @location(0) in_position: vec2, @l let position: vec4 = global.u_view_proj * vec4(world_pos, 1.0); #ifdef DEBUG - var debug = f32(cdata.lod); + var debug = 0.0; + debug = f32(cdata.lod); if(height >= MAX_HEIGHT) { debug = diffs.x; diff --git a/engine/src/drawables/terrain.rs b/engine/src/drawables/terrain.rs index 35b7de54..b88abe79 100644 --- a/engine/src/drawables/terrain.rs +++ b/engine/src/drawables/terrain.rs @@ -1,6 +1,6 @@ use crate::{ bg_layout_litmesh, pbuffer::PBuffer, CompiledModule, Drawable, FrameContext, GfxContext, - IndexType, PipelineBuilder, RenderParams, TerrainVertex, Texture, Uniform, TL, + IndexType, PipelineBuilder, RenderParams, Texture, Uniform, TL, }; use geom::{vec2, vec3, Camera, InfiniteFrustrum, Intersect3, Matrix4, Vec2, AABB3}; use std::sync::Arc; @@ -23,24 +23,22 @@ pub struct TerrainRender { terrain_tex: Arc, #[allow(unused)] grass_tex: Arc, // kept alive - vertices: [PBuffer; LOD], indices: [(PBuffer, u32); LOD], instances: [(PBuffer, u32); LOD], - bgs: [Arc; LOD], + bgs: Arc<[wgpu::BindGroup; LOD]>, w: u32, h: u32, } pub struct TerrainPrepared { - terrainbgs: [Arc; LOD], - vertices: [PBuffer; LOD], + terrainbgs: Arc<[wgpu::BindGroup; LOD]>, indices: [(PBuffer, u32); LOD], instances: [(PBuffer, u32); LOD], } impl TerrainRender { pub fn new(gfx: &mut GfxContext, w: u32, h: u32, grass: Arc) -> Self { - let (indices, vertices) = Self::generate_indices_mesh(gfx); + let indices = Self::generate_indices_mesh(gfx); let mut tex = Texture::create_fbo( &gfx.device, (w * CRESOLUTION as u32 + 1, h * CRESOLUTION as u32 + 1), @@ -60,10 +58,12 @@ impl TerrainRender TerrainRender TerrainRender TerrainRender::new(); LOD]; + + // We calculate lod in 2 passes to be able to generate the stitches + // special: lod 0 = dont render, stored as 1 + lod + let mut assigned_lod: Vec = vec![0; (self.h * self.w) as usize]; + for y in 0..self.h { for x in 0..self.w { let chunk_corner = vec2(x as f32, y as f32) * CSIZE as f32; @@ -233,8 +237,34 @@ impl TerrainRender our lod, we need to stitch + // lod 0 means dont render + let h = self.h as usize; + let w = self.w as usize; + for y in 0..h { + for x in 0..w { + let idx = y * w + x; + let lod = assigned_lod[idx]; + if lod == 0 { + continue; + } + + let stitch_right = (x + 1 != w) && (assigned_lod[idx + 1] > lod); + let stitch_left = (x != 0) && (assigned_lod[idx - 1] > lod); + let stitch_up = (y + 1 != h) && (assigned_lod[idx + w] > lod); + let stitch_down = (y != 0) && (assigned_lod[idx - w] > lod); + + instances[lod as usize - 1].push(TerrainInstance { + offset: vec2(x as f32, y as f32) * CSIZE as f32, + stitch_dir_flags: (stitch_right as u32) + | (stitch_up as u32) << 1 + | (stitch_left as u32) << 2 + | (stitch_down as u32) << 3, }) } } @@ -248,43 +278,34 @@ impl TerrainRender ([(PBuffer, u32); LOD], [PBuffer; LOD]) { + fn generate_indices_mesh(gfx: &GfxContext) -> [(PBuffer, u32); LOD] { let mut indlod = vec![]; - let mut vertlod = vec![]; - let cell_size = (CSIZE / CRESOLUTION) as f32; + for lod in 0..LOD { let scale = 1 << lod; let resolution = CRESOLUTION / scale; let mut indices: Vec = Vec::with_capacity(6 * resolution * resolution); - let mut vertices: Vec = - Vec::with_capacity((resolution + 1) * (resolution + 1)); let resolution = resolution as IndexType; let w = resolution + 1; - for y in 0..=resolution { - for x in 0..=resolution { - let pos = vec2(x as f32, y as f32) * cell_size * scale as f32; - vertices.push(TerrainVertex { - position: [pos.x, pos.y], - }); - - if x < resolution && y < resolution { - let idx = y * w + x; - indices.push(idx); - indices.push(idx + 1); - indices.push(idx + w + 1); - - indices.push(idx); - indices.push(idx + w + 1); - indices.push(idx + w); - } + + // iterate over the grid, adding two triangles for each cell + for y in 0..resolution { + for x in 0..resolution { + let idx = y * w + x; + indices.push(idx); + indices.push(idx + 1); + indices.push(idx + w + 1); + + indices.push(idx); + indices.push(idx + w + 1); + indices.push(idx + w); } } @@ -293,12 +314,9 @@ impl TerrainRender Float32x2]; +const ATTRS: &[VertexAttribute] = &wgpu::vertex_attr_array![0 => Float32x2, 1 => Uint32]; impl TerrainInstance { fn desc() -> VertexBufferLayout<'static> { @@ -347,11 +367,9 @@ impl TerrainPrepared { } let (ind, n_indices) = &self.indices[lod]; - let vertices = &self.vertices[lod]; rp.set_bind_group(2, &self.terrainbgs[lod], &[]); - rp.set_vertex_buffer(0, vertices.slice().unwrap()); - rp.set_vertex_buffer(1, instances.slice().unwrap()); + rp.set_vertex_buffer(0, instances.slice().unwrap()); rp.set_index_buffer(ind.slice().unwrap(), IndexFormat::Uint32); rp.draw_indexed(0..*n_indices, 0, 0..*n_instances); } @@ -387,14 +405,14 @@ impl PipelineBuilder for TerrainPipeline { &terrainlayout, &bg_layout_litmesh(&gfx.device), ], - &[TerrainVertex::desc(), TerrainInstance::desc()], + &[TerrainInstance::desc()], vert, frag, ); } gfx.depth_pipeline_bglayout( - &[TerrainVertex::desc(), TerrainInstance::desc()], + &[TerrainInstance::desc()], vert, None, self.smap, diff --git a/engine/src/gfx.rs b/engine/src/gfx.rs index 353c57d5..ad656d74 100644 --- a/engine/src/gfx.rs +++ b/engine/src/gfx.rs @@ -124,6 +124,8 @@ pub struct GfxSettings { pub fog: bool, pub ssao: bool, pub terrain_grid: bool, + pub shader_debug: bool, + pub pbr_enabled: bool, } impl Default for GfxSettings { @@ -135,6 +137,8 @@ impl Default for GfxSettings { fog: true, ssao: true, terrain_grid: true, + shader_debug: false, + pbr_enabled: true, } } } @@ -514,6 +518,8 @@ impl GfxContext { self.set_define_flag("FOG", settings.fog); self.set_define_flag("SSAO", settings.ssao); self.set_define_flag("TERRAIN_GRID", settings.terrain_grid); + self.set_define_flag("DEBUG", settings.shader_debug); + self.set_define_flag("PBR_ENABLED", settings.pbr_enabled); self.settings = Some(settings); } @@ -592,7 +598,7 @@ impl GfxContext { let enc_dep_ext = &mut encs.depth_prepass; let enc_smap_ext = &mut encs.smap; - { + if self.defines.contains_key("PBR_ENABLED") { profiling::scope!("pbr prepass"); let mut pbr_enc = self .device diff --git a/engine/src/vertex_types.rs b/engine/src/vertex_types.rs index 22960999..16cd719b 100644 --- a/engine/src/vertex_types.rs +++ b/engine/src/vertex_types.rs @@ -38,27 +38,6 @@ impl MeshVertex { } } -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct TerrainVertex { - pub position: [f32; 2], -} - -u8slice_impl!(TerrainVertex); - -const ATTRS_TV: &[VertexAttribute] = &wgpu::vertex_attr_array![0 => Float32x2]; - -impl TerrainVertex { - pub(crate) const fn desc() -> wgpu::VertexBufferLayout<'static> { - use std::mem; - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: ATTRS_TV, - } - } -} - #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct UvVertex { diff --git a/engine_demo/src/main.rs b/engine_demo/src/main.rs index c4f14597..80aa0d9c 100644 --- a/engine_demo/src/main.rs +++ b/engine_demo/src/main.rs @@ -210,6 +210,9 @@ impl engine::framework::State for State { } else { ShadowQuality::NoShadows }; + + ui.checkbox(&mut self.settings.shader_debug, "Shader debug"); + ui.checkbox(&mut self.settings.pbr_enabled, "PBR Environment Update"); }); } } diff --git a/engine_demo/src/terrain.rs b/engine_demo/src/terrain.rs index 8d77811a..4ec613f0 100644 --- a/engine_demo/src/terrain.rs +++ b/engine_demo/src/terrain.rs @@ -33,9 +33,10 @@ impl DemoElement for Terrain { heights[y][x][i][j] = 600.0 * (0.5 + geom::fnoise( - 0.005 * vec2((x * CRESO + j) as f32, (y * CRESO + i) as f32), + 0.01 * vec2((x * CRESO + j) as f32, (y * CRESO + i) as f32), ) - .0); + .0 + .powi(2)); } } }