Skip to content

Commit

Permalink
gpu based normal calculations and resampling
Browse files Browse the repository at this point in the history
  • Loading branch information
Uriopass committed Dec 13, 2023
1 parent 7b529f5 commit 7acd357
Show file tree
Hide file tree
Showing 13 changed files with 601 additions and 224 deletions.
53 changes: 53 additions & 0 deletions assets/shaders/terrain/calc_normals.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "unpack.wgsl"

struct VertexOutput {
@location(0) v_TexCoord: vec2<f32>,
@builtin(position) member: vec4<f32>,
}

@vertex
fn vert(@builtin(vertex_index) vi: u32) -> VertexOutput {
var tc: vec2<f32> = vec2(0.0, 0.0);
switch (vi) {
case 0u: {tc = vec2(1.0, 0.0);}
case 1u: {tc = vec2(1.0, 1.0);}
case 2u: {tc = vec2(0.0, 0.0);}
case 3u: {tc = vec2(0.0, 1.0);}
default: {}
}
let pos: vec2<f32> = tc * 2.0 - 1.0;
let gl_Position = vec4(pos.x, -pos.y, 0.5, 1.0);

return VertexOutput(tc, gl_Position);
}


struct FragmentOutput {
@location(0) o_Target: u32,
}

@group(0) @binding(0) var t_terrain: texture_2d<u32>;

fn pack_diffs(diffs: vec2<f32>, lod_pow2: f32) -> u32 {
let x = u32((clamp(diffs.x, -MAX_DIFF, MAX_DIFF) / (MAX_DIFF * lod_pow2)) * 127.0 + 128.0);
let y = u32((clamp(diffs.y, -MAX_DIFF, MAX_DIFF) / (MAX_DIFF * lod_pow2)) * 127.0 + 128.0);
return (x << 8u) | y;
}

@fragment
fn calc_normals(@location(0) v_TexCoord: vec2<f32>) -> FragmentOutput {
let dim: vec2<u32> = textureDimensions(t_terrain);

let id = vec2<u32>(v_TexCoord * vec2<f32>(dim));



let hR: f32 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(1u, 0u), 0).r);
let hL: f32 = unpack_height(textureLoad(t_terrain, id - vec2<u32>(1u, 0u), 0).r);
let hT: f32 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(0u, 1u), 0).r);
let hB: f32 = unpack_height(textureLoad(t_terrain, id - vec2<u32>(0u, 1u), 0).r);

let diffs = vec2<f32>((hL - hR), (hB - hT));

return FragmentOutput(pack_diffs(diffs, 1.0));
}
90 changes: 90 additions & 0 deletions assets/shaders/terrain/resample.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "unpack.wgsl"

struct VertexOutput {
@location(0) v_TexCoord: vec2<f32>,
@builtin(position) member: vec4<f32>,
}

@vertex
fn vert(@builtin(vertex_index) vi: u32) -> VertexOutput {
var tc: vec2<f32> = vec2(0.0, 0.0);
switch (vi) {
case 0u: {tc = vec2(1.0, 0.0);}
case 1u: {tc = vec2(1.0, 1.0);}
case 2u: {tc = vec2(0.0, 0.0);}
case 3u: {tc = vec2(0.0, 1.0);}
default: {}
}
let pos: vec2<f32> = tc * 2.0 - 1.0;
let gl_Position = vec4(pos.x, -pos.y, 0.5, 1.0);

return VertexOutput(tc, gl_Position);
}


struct FragmentOutput {
@location(0) o_Target: u32,
}

fn pack_height(h: f32) -> u32 {
return u32((h - MIN_HEIGHT) / HEIGHT_RANGE * 65535.0);
}

@group(0) @binding(0) var t_terrain: texture_2d<u32>;

@fragment
fn downsample(@location(0) v_TexCoord: vec2<f32>) -> FragmentOutput {
let dim = textureDimensions(t_terrain);

let id = vec2<u32>(v_TexCoord * vec2<f32>(dim));


let h0 = unpack_height(textureLoad(t_terrain, id, 0).r);
let h1 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(1u, 0u), 0).r);
let h2 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(0u, 1u), 0).r);
let h3 = unpack_height(textureLoad(t_terrain, id - vec2<u32>(1u, 0u), 0).r);
let h4 = unpack_height(textureLoad(t_terrain, id - vec2<u32>(0u, 1u), 0).r);
let h5 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(1u, 1u), 0).r);
let h6 = unpack_height(textureLoad(t_terrain, id - vec2<u32>(1u, 1u), 0).r);
let h7 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(1u, 0u) - vec2<u32>(0u, 1u), 0).r);
let h8 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(0u, 1u) - vec2<u32>(1u, 0u), 0).r);

let gaussian = h0 * 0.25
+ h1 * 0.125
+ h2 * 0.125
+ h3 * 0.125
+ h4 * 0.125
+ h5 * 0.0625
+ h6 * 0.0625
+ h7 * 0.0625
+ h8 * 0.0625;
let maxv = max(max(max(max(max(max(max(max(h0, h1), h2), h3), h4), h5), h6), h7), h8);

let final_height = (gaussian + maxv) * 0.5;

return FragmentOutput(pack_height(final_height));
}

@fragment
fn upsample(@location(0) v_TexCoord: vec2<f32>) -> FragmentOutput {
let dim = textureDimensions(t_terrain);

let id = vec2<u32>(v_TexCoord * vec2<f32>(dim));

let h0 = unpack_height(textureLoad(t_terrain, id, 0).r);
let h1 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(1u, 0u), 0).r);
let h2 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(0u, 1u), 0).r);
let h3 = unpack_height(textureLoad(t_terrain, id + vec2<u32>(1u, 1u), 0).r);

// bilinear interpolation
let x = fract(v_TexCoord.x * vec2<f32>(dim));
let y = fract(v_TexCoord.y * vec2<f32>(dim));

let h01 = mix(h0, h1, x.x);
let h23 = mix(h2, h3, x.x);
let h = mix(h01, h23, y.y);

let final_height = h;

return FragmentOutput(pack_height(final_height));
}
12 changes: 5 additions & 7 deletions assets/shaders/terrain/terrain.frag.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ struct ChunkData {

@group(1) @binding(0) var<uniform> params: RenderParams;

@group(2) @binding(0) var t_terraindata: texture_2d<f32>;
@group(2) @binding(1) var s_terraindata: sampler;
@group(2) @binding(2) var t_grass: texture_2d<f32>;
@group(2) @binding(3) var s_grass: sampler;
@group(2) @binding(4) var<uniform> cdata: ChunkData;
@group(2) @binding(4) var t_grass: texture_2d<f32>;
@group(2) @binding(5) var s_grass: sampler;
@group(2) @binding(6) var<uniform> cdata: ChunkData;

@group(3) @binding(0) var t_ssao: texture_2d<f32>;
@group(3) @binding(1) var s_ssao: sampler;
Expand Down Expand Up @@ -145,8 +143,8 @@ fn frag(@builtin(position) position: vec4<f32>,
let V_denorm: vec3<f32> = params.cam_pos.xyz - in_wpos;
let depth: f32 = length(V_denorm);
let V: vec3<f32> = V_denorm / depth;
let F0: vec3<f32> = vec3(0.02);
let roughness: f32 = 1.0;
let F0: vec3<f32> = vec3(0.00);
let roughness: f32 = 1.3; // avoid specular highlights which look weird on terrain
let normal: vec3<f32> = normalize(in_normal);
let F_spec: vec3<f32> = F0; // simplified with constant folding: fresnelSchlickRoughness(max(dot(normal, V), 0.0), F0, roughness);

Expand Down
83 changes: 51 additions & 32 deletions assets/shaders/terrain/terrain.vert.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ struct Uniforms {
}

struct VertexOutput {
@builtin(position) member: vec4<f32>,
@location(0) out_normal: vec3<f32>,
@location(1) out_wpos: vec3<f32>,
#ifdef DEBUG
@location(2) debug: f32,
#endif
@builtin(position) member: vec4<f32>,
}

struct ChunkData {
Expand All @@ -27,16 +27,24 @@ struct ChunkData {

@group(1) @binding(0) var<uniform> params: RenderParams;

@group(2) @binding(0) var t_terraindata: texture_2d<u32>;
@group(2) @binding(1) var s_terraindata: sampler;
@group(2) @binding(4) var<uniform> cdata: ChunkData;
@group(2) @binding(0) var t_terrain: texture_2d<u32>;
@group(2) @binding(1) var s_terrain: sampler;
@group(2) @binding(2) var t_normals: texture_2d<u32>;
@group(2) @binding(3) var s_normals: sampler;
@group(2) @binding(6) var<uniform> cdata: ChunkData;

/*
normal: vec3(self.cell_size * scale as f32, 0.0, hx - height)
.cross(vec3(0.0, self.cell_size * scale as f32, hy - height))
.normalize(),
*/

fn sampleHeightDxDy(pos: vec2<i32>, lod: i32) -> vec3<f32> {
let height: f32 = unpack_height(textureLoad(t_terrain, pos, lod).r);
let diffs: vec2<f32> = unpack_diffs(textureLoad(t_normals, pos, lod).r, 1.0);
return vec3<f32>(height, diffs);
}

@vertex
fn vert(@builtin(vertex_index) vid: u32,
@location(0) in_off: vec2<f32>,
Expand All @@ -47,45 +55,56 @@ fn vert(@builtin(vertex_index) vid: u32,

var in_position: vec2<i32> = vec2(i32(idx_x), i32(idx_y));

if (idx_x == 0u) { // x_neg
in_position.y &= -1 << ((stitch_dir_flags & 4u) >> 2u);
}
else if (idx_x == cdata.resolution - 1u) { // x_pos
in_position.y &= -1 << (stitch_dir_flags & 1u);
}
if (idx_y == 0u) { // y_neg
in_position.x &= -1 << ((stitch_dir_flags & 8u) >> 3u);
}
else if (idx_y == cdata.resolution - 1u) { // y_pos
in_position.x &= -1 << ((stitch_dir_flags & 2u) >> 1u);
}
//if (idx_x == 0u) { // x_neg
// in_position.y &= -1 << ((stitch_dir_flags & 4u) >> 2u);
//}
//else if (idx_x == cdata.resolution - 1u) { // x_pos
// in_position.y &= -1 << (stitch_dir_flags & 1u);
//}
//if (idx_y == 0u) { // y_neg
// in_position.x &= -1 << ((stitch_dir_flags & 8u) >> 3u);
//}
//else if (idx_y == cdata.resolution - 1u) { // y_pos
// in_position.x &= -1 << ((stitch_dir_flags & 2u) >> 1u);
//}

let tpos: vec2<i32> = in_position * i32(cdata.lod_pow2) + vec2<i32>(in_off * cdata.inv_cell_size);
let tpos: vec2<i32> = in_position + vec2<i32>(in_off * cdata.inv_cell_size / f32(cdata.lod_pow2));

let h_dx_dy: vec3<f32> = unpack(textureLoad(t_terraindata, tpos, 0).r, 1.0);
let height_dx_dy: vec3<f32> = sampleHeightDxDy(tpos, i32(cdata.lod));

let world_pos: vec3<f32> = vec3(vec2<f32>(in_position * i32(cdata.lod_pow2)) * cdata.cell_size + in_off, h_dx_dy.x);
let clip_pos: vec4<f32> = global.u_view_proj * vec4(world_pos, 1.0);
var normal: vec3<f32> = normalize(vec3(height_dx_dy.yz, cdata.cell_size * 2.0)); // https://stackoverflow.com/questions/49640250/calculate-normals-from-heightmap

//let dist_to_cam: f32 = length(params.cam_pos.xyz - vec3(pos.xy, 0.0));
//let transition_alpha: f32 = smoothstep(cdata.distance_lod_cutoff * 0.8, cdata.distance_lod_cutoff, dist_to_cam);
var world_pos: vec3<f32> = vec3(vec2<f32>(in_position * i32(cdata.lod_pow2)) * cdata.cell_size + in_off, height_dx_dy.x);

var out_normal: vec3<f32> = normalize(vec3(h_dx_dy.yz, cdata.cell_size * 2.0)); // https://stackoverflow.com/questions/49640250/calculate-normals-from-heightmap
let height_dx_dy_next: vec3<f32> = sampleHeightDxDy(tpos / 2, i32(cdata.lod) + 1);
let normal_next: vec3<f32> = normalize(vec3(height_dx_dy_next.yz, cdata.cell_size * 2.0));

#ifdef DEBUG
var debug = 0.0;
debug = f32(cdata.lod);

if(height >= MAX_HEIGHT) {
debug = diffs.x;
}
if (cdata.lod < 4u) {
let dist_to_cam: f32 = length(params.cam_pos.xyz - vec3(world_pos.xy, 0.0));
let transition_alpha: f32 = smoothstep(cdata.distance_lod_cutoff * 0.8, cdata.distance_lod_cutoff, dist_to_cam);

#ifdef DEBUG
debug = (f32(cdata.lod) + transition_alpha + 1.0) / 5.0;
#endif

return VertexOutput(
out_normal,
var world_pos_next: vec3<f32> = vec3(vec2<f32>(in_position / 2 * i32(cdata.lod_pow2)) * cdata.cell_size * 2.0 + in_off, height_dx_dy_next.x);

normal = mix(normal, normal_next, transition_alpha);
world_pos = mix(world_pos, world_pos_next, transition_alpha);
} else {
debug = 1.0;
}

let clip_pos: vec4<f32> = global.u_view_proj * vec4(world_pos, 1.0);


return VertexOutput(clip_pos,
normal,
world_pos,
#ifdef DEBUG
debug,
debug
#endif
clip_pos);
);
}
6 changes: 0 additions & 6 deletions assets/shaders/terrain/unpack.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,3 @@ fn unpack_diffs(v: u32, lod_pow2: f32) -> vec2<f32> {
let y = (f32(v & 0xFFu) - 128.0) / 127.0 * (MAX_DIFF * lod_pow2);
return vec2<f32>(x, y);
}

fn unpack(v: u32, lod_pow2: f32) -> vec3<f32> {
let h = unpack_height(v & 0xFFFFu);
let d = unpack_diffs(v >> 16u, lod_pow2);
return vec3(h, d);
}
Loading

0 comments on commit 7acd357

Please sign in to comment.