Skip to content

Commit

Permalink
Improve the entity depth calculation and shadow rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
hasenbanck committed Dec 14, 2024
1 parent df7a995 commit 36bea90
Show file tree
Hide file tree
Showing 16 changed files with 178 additions and 260 deletions.
2 changes: 2 additions & 0 deletions korangar/src/graphics/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub struct WaterInstruction<'a> {
#[derive(Clone, Debug)]
pub struct DirectionalShadowCasterInstruction {
pub view_projection_matrix: Matrix4<f32>,
pub view_matrix: Matrix4<f32>,
pub direction: Vector3<f32>,
pub color: Color,
}
Expand All @@ -86,6 +87,7 @@ pub struct DirectionalShadowCasterInstruction {
#[derive(Clone, Debug)]
pub struct PointShadowCasterInstruction {
pub view_projection_matrices: [Matrix4<f32>; 6],
pub view_matrices: [Matrix4<f32>; 6],
pub position: Point3<f32>,
pub color: Color,
pub range: f32,
Expand Down
10 changes: 10 additions & 0 deletions korangar/src/graphics/passes/directional_shadow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::num::NonZeroU64;
use std::sync::OnceLock;

use bytemuck::{Pod, Zeroable};
use cgmath::{Matrix4, SquareMatrix};
pub(crate) use entity::DirectionalShadowEntityDrawer;
pub(crate) use indicator::DirectionalShadowIndicatorDrawer;
pub(crate) use model::DirectionalShadowModelDrawer;
Expand All @@ -26,6 +27,8 @@ const PASS_NAME: &str = "directional shadow render pass";
#[repr(C)]
struct PassUniforms {
view_projection: [[f32; 4]; 4],
view: [[f32; 4]; 4],
inverse_view: [[f32; 4]; 4],
animation_timer: f32,
padding: [u32; 3],
}
Expand Down Expand Up @@ -132,6 +135,13 @@ impl Prepare for DirectionalShadowRenderPassContext {
fn prepare(&mut self, _device: &Device, instructions: &RenderInstruction) {
self.uniforms_data = PassUniforms {
view_projection: instructions.directional_light_with_shadow.view_projection_matrix.into(),
view: instructions.directional_light_with_shadow.view_matrix.into(),
inverse_view: instructions
.directional_light_with_shadow
.view_matrix
.invert()
.unwrap_or(Matrix4::identity())
.into(),
animation_timer: instructions.uniforms.animation_timer,
padding: Default::default(),
};
Expand Down
78 changes: 30 additions & 48 deletions korangar/src/graphics/passes/directional_shadow/shader/entity.wgsl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
struct PassUniforms {
view_projection: mat4x4<f32>,
view: mat4x4<f32>,
inverse_view: mat4x4<f32>,
animation_timer: f32,
}

Expand All @@ -20,18 +22,17 @@ struct InstanceData {
struct Vertex {
position: vec3<f32>,
texture_coordinates: vec2<f32>,
depth_multiplier: f32,
curvature_multiplier: f32,
}

struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) texture_coordinates: vec2<f32>,
@location(1) depth_offset: f32,
@location(2) curvature: f32,
@location(3) @interpolate(flat) original_depth_offset: f32,
@location(4) @interpolate(flat) original_curvature: f32,
@location(5) alpha: f32,
@location(0) world_position: vec4<f32>,
@location(1) texture_coordinates: vec2<f32>,
@location(2) depth_offset: f32,
@location(3) curvature: f32,
@location(4) @interpolate(flat) original_depth_offset: f32,
@location(5) @interpolate(flat) original_curvature: f32,
@location(6) alpha: f32,
}

@group(0) @binding(3) var texture_sampler: sampler;
Expand All @@ -47,9 +48,11 @@ fn vs_main(
let instance = instance_data[instance_index];
let vertex = vertex_data(vertex_index);
let frame_part_vertex = instance.frame_part_transform * vec4<f32>(vertex.position, 1.0);
let world_position = instance.world * frame_part_vertex;

var output: VertexOutput;
output.position = pass_uniforms.view_projection * instance.world * frame_part_vertex;
output.world_position = world_position;
output.position = pass_uniforms.view_projection * world_position;
output.texture_coordinates = instance.texture_position + vertex.texture_coordinates * instance.texture_size;

if (instance.mirror != 0u) {
Expand All @@ -67,7 +70,7 @@ fn vs_main(
// Because we have to transform the vertex of the frame part, we can't use the depth and curvature
// directly and are using the fact, that y / depth and x / curvature correlate to each other.
// An offset is also added for frame parts not stay at the same depth.
output.depth_offset = (frame_part_vertex.y - 1.0) * proportion_y + instance.extra_depth_offset;
output.depth_offset = frame_part_vertex.y * proportion_y + instance.extra_depth_offset;
output.curvature = frame_part_vertex.x * proportion_x;

output.original_depth_offset = instance.depth_offset;
Expand All @@ -83,35 +86,30 @@ fn fs_main(input: VertexOutput) -> @builtin(frag_depth) f32 {
discard;
}

// TODO: This is only a temporary change, we will fix the depth_offset later.
// The idea for the temporary change is to create two parabolas to correct the plane inclination.
let sign_depth_offset = select(2.0, -2.0, input.depth_offset < 0.0);
let scaled_depth_offset = sign_depth_offset * pow(input.depth_offset, 2.0) * input.original_depth_offset;
let scaled_curvature_offset = (0.5 - pow(input.curvature, 2.0)) * input.original_curvature;
// Adjust the sprite as if it was standing upright
let depth_offset = input.depth_offset * input.original_depth_offset;
let curvature_offset = (0.5 - pow(input.curvature, 2.0)) * input.original_curvature;
let view_position = pass_uniforms.view * input.world_position;
let adjusted_view_position = view_position - vec4<f32>(0.0, 0.0, depth_offset + curvature_offset, 0.0);
let adjusted_world_position = pass_uniforms.inverse_view * adjusted_view_position;
let clip_position = pass_uniforms.view_projection * adjusted_world_position;
let depth = saturate(clip_position.z / clip_position.w);

// Shadows use orthographic projections, so values are linear, but "compressed".
let absolute_depth = decompress_depth_ortho(input.position.z);
let adjusted_linear_z: f32 = 3.5 + absolute_depth - scaled_depth_offset - scaled_curvature_offset;
let depth = compress_depth_ortho(adjusted_linear_z);
let clamped_depth = clamp(depth, 0.0, 1.0);

return clamped_depth;
return depth;
}

// Optimized version of the following truth table:
//
// vertex_index x y z u v d c
// 0 -1 2 1 0 0 1 -1
// 1 -1 0 1 0 1 -1 -1
// 2 1 2 1 1 0 1 1
// 3 1 2 1 1 0 1 1
// 4 -1 0 1 0 1 -1 -1
// 5 1 0 1 1 1 -1 1
// vertex_index x y z u v
// 0 -1 2 1 0 0
// 1 -1 0 1 0 1
// 2 1 2 1 1 0
// 3 1 2 1 1 0
// 4 -1 0 1 0 1
// 5 1 0 1 1 1
//
// (x,y,z) are the vertex position
// (u,v) are the UV coordinates
// (depth) is the depth multiplier
// (curve) is the curvature multiplier
fn vertex_data(vertex_index: u32) -> Vertex {
let index = 1u << vertex_index;

Expand All @@ -123,22 +121,6 @@ fn vertex_data(vertex_index: u32) -> Vertex {
let z = 1.0;
let u = f32(1 - case0);
let v = f32(1 - case1);
let depth = y - 1.0;
let curve = x;

return Vertex(vec3<f32>(x, y, z), vec2<f32>(u, v), depth, curve);
}

fn decompress_depth_ortho(depth: f32) -> f32 {
let depth_scale = -pass_uniforms.view_projection[2][2];
let depth_far = pass_uniforms.view_projection[3][2];
let far = depth_far / depth_scale;
return far - depth / depth_scale;
}

fn compress_depth_ortho(depth: f32) -> f32 {
let depth_scale = -pass_uniforms.view_projection[2][2];
let depth_far = pass_uniforms.view_projection[3][2];
let far = depth_far / depth_scale;
return -depth * depth_scale + depth_far;
return Vertex(vec3<f32>(x, y, z), vec2<f32>(u, v));
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
struct PassUniforms {
view_projection: mat4x4<f32>,
view: mat4x4<f32>,
inverse_view: mat4x4<f32>,
animation_timer: f32,
}

Expand All @@ -20,19 +22,18 @@ struct InstanceData {
struct Vertex {
position: vec3<f32>,
texture_coordinates: vec2<f32>,
depth_multiplier: f32,
curvature_multiplier: f32,
}

struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) texture_coordinates: vec2<f32>,
@location(1) depth_offset: f32,
@location(2) curvature: f32,
@location(3) @interpolate(flat) original_depth_offset: f32,
@location(4) @interpolate(flat) original_curvature: f32,
@location(5) texture_index: i32,
@location(6) alpha: f32,
@location(0) world_position: vec4<f32>,
@location(1) texture_coordinates: vec2<f32>,
@location(2) depth_offset: f32,
@location(3) curvature: f32,
@location(4) @interpolate(flat) original_depth_offset: f32,
@location(5) @interpolate(flat) original_curvature: f32,
@location(6) texture_index: i32,
@location(7) alpha: f32,
}

@group(0) @binding(3) var texture_sampler: sampler;
Expand All @@ -48,9 +49,11 @@ fn vs_main(
let instance = instance_data[instance_index];
let vertex = vertex_data(vertex_index);
let frame_part_vertex = instance.frame_part_transform * vec4<f32>(vertex.position, 1.0);
let world_position = instance.world * frame_part_vertex;

var output: VertexOutput;
output.position = pass_uniforms.view_projection * instance.world * frame_part_vertex;
output.world_position = world_position;
output.position = pass_uniforms.view_projection * world_position;
output.texture_coordinates = instance.texture_position + vertex.texture_coordinates * instance.texture_size;

if (instance.mirror != 0u) {
Expand All @@ -68,7 +71,7 @@ fn vs_main(
// Because we have to transform the vertex of the frame part, we can't use the depth and curvature
// directly and are using the fact, that y / depth and x / curvature correlate to each other.
// An offset is also added for frame parts not stay at the same depth.
output.depth_offset = (frame_part_vertex.y - 1.0) * proportion_y + instance.extra_depth_offset;
output.depth_offset = frame_part_vertex.y * proportion_y + instance.extra_depth_offset;
output.curvature = frame_part_vertex.x * proportion_x;

output.original_depth_offset = instance.depth_offset;
Expand All @@ -85,35 +88,30 @@ fn fs_main(input: VertexOutput) -> @builtin(frag_depth) f32 {
discard;
}

// TODO: This is only a temporary change, we will fix the depth_offset later.
// The idea for the temporary change is to create two parabolas to correct the plane inclination.
let sign_depth_offset = select(2.0, -2.0, input.depth_offset < 0.0);
let scaled_depth_offset = sign_depth_offset * pow(input.depth_offset, 2.0) * input.original_depth_offset;
let scaled_curvature_offset = (0.5 - pow(input.curvature, 2.0)) * input.original_curvature;
// Adjust the sprite as if it was standing upright
let depth_offset = input.depth_offset * input.original_depth_offset;
let curvature_offset = (0.5 - pow(input.curvature, 2.0)) * input.original_curvature;
let view_position = pass_uniforms.view * input.world_position;
let adjusted_view_position = view_position - vec4<f32>(0.0, 0.0, depth_offset + curvature_offset, 0.0);
let adjusted_world_position = pass_uniforms.inverse_view * adjusted_view_position;
let clip_position = pass_uniforms.view_projection * adjusted_world_position;
let depth = saturate(clip_position.z / clip_position.w);

// Shadows use orthographic projections, so values are linear, but "compressed".
let absolute_depth = decompress_depth_ortho(input.position.z);
let adjusted_linear_z: f32 = 3.5 + absolute_depth - scaled_depth_offset - scaled_curvature_offset;
let depth = compress_depth_ortho(adjusted_linear_z);
let clamped_depth = clamp(depth, 0.0, 1.0);

return clamped_depth;
return depth;
}

// Optimized version of the following truth table:
//
// vertex_index x y z u v d c
// 0 -1 2 1 0 0 1 -1
// 1 -1 0 1 0 1 -1 -1
// 2 1 2 1 1 0 1 1
// 3 1 2 1 1 0 1 1
// 4 -1 0 1 0 1 -1 -1
// 5 1 0 1 1 1 -1 1
// vertex_index x y z u v
// 0 -1 2 1 0 0
// 1 -1 0 1 0 1
// 2 1 2 1 1 0
// 3 1 2 1 1 0
// 4 -1 0 1 0 1
// 5 1 0 1 1 1
//
// (x,y,z) are the vertex position
// (u,v) are the UV coordinates
// (depth) is the depth multiplier
// (curve) is the curvature multiplier
fn vertex_data(vertex_index: u32) -> Vertex {
let index = 1u << vertex_index;

Expand All @@ -125,22 +123,6 @@ fn vertex_data(vertex_index: u32) -> Vertex {
let z = 1.0;
let u = f32(1 - case0);
let v = f32(1 - case1);
let depth = y - 1.0;
let curve = x;

return Vertex(vec3<f32>(x, y, z), vec2<f32>(u, v), depth, curve);
}

fn decompress_depth_ortho(depth: f32) -> f32 {
let depth_scale = -pass_uniforms.view_projection[2][2];
let depth_far = pass_uniforms.view_projection[3][2];
let far = depth_far / depth_scale;
return far - depth / depth_scale;
}

fn compress_depth_ortho(depth: f32) -> f32 {
let depth_scale = -pass_uniforms.view_projection[2][2];
let depth_far = pass_uniforms.view_projection[3][2];
let far = depth_far / depth_scale;
return -depth * depth_scale + depth_far;
return Vertex(vec3<f32>(x, y, z), vec2<f32>(u, v));
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ struct GlobalUniforms {

struct PassUniforms {
view_projection: mat4x4<f32>,
view: mat4x4<f32>,
inverse_view: mat4x4<f32>,
animation_timer: f32,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
struct PassUniforms {
view_projection: mat4x4<f32>,
view: mat4x4<f32>,
inverse_view: mat4x4<f32>,
animation_timer: f32,
}

Expand Down
Loading

0 comments on commit 36bea90

Please sign in to comment.