Native unclipped depth on supported platforms (#16095)

# Objective
- Fixes #16078

## Solution

- Rename things to clarify that we _want_ unclipped depth for
directional light shadow views, and need some way of disabling the GPU's
builtin depth clipping
- Use DEPTH_CLIP_CONTROL instead of the fragment shader emulation on
supported platforms
- Pass only the clip position depth instead of the whole clip position
between vertex->fragment shader (no idea if this helps performance or
not, compiler might optimize it anyways)
- Meshlets
- HW raster always uses DEPTH_CLIP_CONTROL since it targets a more
limited set of platforms
- SW raster was not handling DEPTH_CLAMP_ORTHO correctly, it ended up
pretty much doing nothing.
- This PR made me realize that SW raster technically should have depth
clipping for all views that are not directional light shadows, but I
decided not to bother writing it. I'm not sure that it ever matters in
practice. If proven otherwise, I can add it.

## Testing

- Did you test these changes? If so, how?
- Lighting example. Both opaque (no fragment shader) and alpha masked
geometry (fragment shader emulation) are working with
depth_clip_control, and both work when it's turned off. Also tested
meshlet example.
- Are there any parts that need more testing?
  - Performance. I can't figure out a good test scene.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Toggle depth_clip_control_supported in prepass/mod.rs line 323 to turn
this PR on or off.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - Native

---

## Migration Guide
- `MeshPipelineKey::DEPTH_CLAMP_ORTHO` is now
`MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO`
- The `DEPTH_CLAMP_ORTHO` shaderdef has been renamed to
`UNCLIPPED_DEPTH_ORTHO_EMULATION`
- `clip_position_unclamped: vec4<f32>` is now `unclipped_depth: f32`
This commit is contained in:
JMS55 2024-12-03 09:30:14 -08:00 committed by GitHub
parent f375422ddd
commit d221665386
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 60 additions and 105 deletions

View file

@ -142,6 +142,7 @@ impl MeshletPlugin {
WgpuFeatures::SHADER_INT64_ATOMIC_MIN_MAX WgpuFeatures::SHADER_INT64_ATOMIC_MIN_MAX
| WgpuFeatures::SHADER_INT64 | WgpuFeatures::SHADER_INT64
| WgpuFeatures::SUBGROUP | WgpuFeatures::SUBGROUP
| WgpuFeatures::DEPTH_CLIP_CONTROL
| WgpuFeatures::PUSH_CONSTANTS | WgpuFeatures::PUSH_CONSTANTS
} }
} }

View file

@ -34,10 +34,9 @@ pub struct MeshletPipelines {
downsample_depth_second_shadow_view: CachedComputePipelineId, downsample_depth_second_shadow_view: CachedComputePipelineId,
visibility_buffer_software_raster: CachedComputePipelineId, visibility_buffer_software_raster: CachedComputePipelineId,
visibility_buffer_software_raster_depth_only: CachedComputePipelineId, visibility_buffer_software_raster_depth_only: CachedComputePipelineId,
visibility_buffer_software_raster_depth_only_clamp_ortho: CachedComputePipelineId,
visibility_buffer_hardware_raster: CachedRenderPipelineId, visibility_buffer_hardware_raster: CachedRenderPipelineId,
visibility_buffer_hardware_raster_depth_only: CachedRenderPipelineId, visibility_buffer_hardware_raster_depth_only: CachedRenderPipelineId,
visibility_buffer_hardware_raster_depth_only_clamp_ortho: CachedRenderPipelineId, visibility_buffer_hardware_raster_depth_only_unclipped: CachedRenderPipelineId,
resolve_depth: CachedRenderPipelineId, resolve_depth: CachedRenderPipelineId,
resolve_depth_shadow_view: CachedRenderPipelineId, resolve_depth_shadow_view: CachedRenderPipelineId,
resolve_material_depth: CachedRenderPipelineId, resolve_material_depth: CachedRenderPipelineId,
@ -215,29 +214,6 @@ impl FromWorld for MeshletPipelines {
}, },
), ),
visibility_buffer_software_raster_depth_only_clamp_ortho: pipeline_cache
.queue_compute_pipeline(ComputePipelineDescriptor {
label: Some(
"meshlet_visibility_buffer_software_raster_depth_only_clamp_ortho_pipeline"
.into(),
),
layout: vec![visibility_buffer_raster_layout.clone()],
push_constant_ranges: vec![],
shader: MESHLET_VISIBILITY_BUFFER_SOFTWARE_RASTER_SHADER_HANDLE,
shader_defs: vec![
"MESHLET_VISIBILITY_BUFFER_RASTER_PASS".into(),
"DEPTH_CLAMP_ORTHO".into(),
if remap_1d_to_2d_dispatch_layout.is_some() {
"MESHLET_2D_DISPATCH"
} else {
""
}
.into(),
],
entry_point: "rasterize_cluster".into(),
zero_initialize_workgroup_memory: false,
}),
visibility_buffer_hardware_raster: pipeline_cache.queue_render_pipeline( visibility_buffer_hardware_raster: pipeline_cache.queue_render_pipeline(
RenderPipelineDescriptor { RenderPipelineDescriptor {
label: Some("meshlet_visibility_buffer_hardware_raster_pipeline".into()), label: Some("meshlet_visibility_buffer_hardware_raster_pipeline".into()),
@ -324,10 +300,10 @@ impl FromWorld for MeshletPipelines {
}, },
), ),
visibility_buffer_hardware_raster_depth_only_clamp_ortho: pipeline_cache visibility_buffer_hardware_raster_depth_only_unclipped: pipeline_cache
.queue_render_pipeline(RenderPipelineDescriptor { .queue_render_pipeline(RenderPipelineDescriptor {
label: Some( label: Some(
"meshlet_visibility_buffer_hardware_raster_depth_only_clamp_ortho_pipeline" "meshlet_visibility_buffer_hardware_raster_depth_only_unclipped_pipeline"
.into(), .into(),
), ),
layout: vec![visibility_buffer_raster_layout], layout: vec![visibility_buffer_raster_layout],
@ -337,10 +313,7 @@ impl FromWorld for MeshletPipelines {
}], }],
vertex: VertexState { vertex: VertexState {
shader: MESHLET_VISIBILITY_BUFFER_HARDWARE_RASTER_SHADER_HANDLE, shader: MESHLET_VISIBILITY_BUFFER_HARDWARE_RASTER_SHADER_HANDLE,
shader_defs: vec![ shader_defs: vec!["MESHLET_VISIBILITY_BUFFER_RASTER_PASS".into()],
"MESHLET_VISIBILITY_BUFFER_RASTER_PASS".into(),
"DEPTH_CLAMP_ORTHO".into(),
],
entry_point: "vertex".into(), entry_point: "vertex".into(),
buffers: vec![], buffers: vec![],
}, },
@ -349,7 +322,7 @@ impl FromWorld for MeshletPipelines {
strip_index_format: None, strip_index_format: None,
front_face: FrontFace::Ccw, front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back), cull_mode: Some(Face::Back),
unclipped_depth: false, unclipped_depth: true,
polygon_mode: PolygonMode::Fill, polygon_mode: PolygonMode::Fill,
conservative: false, conservative: false,
}, },
@ -357,10 +330,7 @@ impl FromWorld for MeshletPipelines {
multisample: MultisampleState::default(), multisample: MultisampleState::default(),
fragment: Some(FragmentState { fragment: Some(FragmentState {
shader: MESHLET_VISIBILITY_BUFFER_HARDWARE_RASTER_SHADER_HANDLE, shader: MESHLET_VISIBILITY_BUFFER_HARDWARE_RASTER_SHADER_HANDLE,
shader_defs: vec![ shader_defs: vec!["MESHLET_VISIBILITY_BUFFER_RASTER_PASS".into()],
"MESHLET_VISIBILITY_BUFFER_RASTER_PASS".into(),
"DEPTH_CLAMP_ORTHO".into(),
],
entry_point: "fragment".into(), entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState { targets: vec![Some(ColorTargetState {
format: TextureFormat::R8Uint, format: TextureFormat::R8Uint,
@ -484,7 +454,6 @@ impl MeshletPipelines {
&ComputePipeline, &ComputePipeline,
&ComputePipeline, &ComputePipeline,
&ComputePipeline, &ComputePipeline,
&ComputePipeline,
&RenderPipeline, &RenderPipeline,
&RenderPipeline, &RenderPipeline,
&RenderPipeline, &RenderPipeline,
@ -506,14 +475,11 @@ impl MeshletPipelines {
pipeline_cache.get_compute_pipeline(pipeline.visibility_buffer_software_raster)?, pipeline_cache.get_compute_pipeline(pipeline.visibility_buffer_software_raster)?,
pipeline_cache pipeline_cache
.get_compute_pipeline(pipeline.visibility_buffer_software_raster_depth_only)?, .get_compute_pipeline(pipeline.visibility_buffer_software_raster_depth_only)?,
pipeline_cache.get_compute_pipeline(
pipeline.visibility_buffer_software_raster_depth_only_clamp_ortho,
)?,
pipeline_cache.get_render_pipeline(pipeline.visibility_buffer_hardware_raster)?, pipeline_cache.get_render_pipeline(pipeline.visibility_buffer_hardware_raster)?,
pipeline_cache pipeline_cache
.get_render_pipeline(pipeline.visibility_buffer_hardware_raster_depth_only)?, .get_render_pipeline(pipeline.visibility_buffer_hardware_raster_depth_only)?,
pipeline_cache.get_render_pipeline( pipeline_cache.get_render_pipeline(
pipeline.visibility_buffer_hardware_raster_depth_only_clamp_ortho, pipeline.visibility_buffer_hardware_raster_depth_only_unclipped,
)?, )?,
pipeline_cache.get_render_pipeline(pipeline.resolve_depth)?, pipeline_cache.get_render_pipeline(pipeline.resolve_depth)?,
pipeline_cache.get_render_pipeline(pipeline.resolve_depth_shadow_view)?, pipeline_cache.get_render_pipeline(pipeline.resolve_depth_shadow_view)?,

View file

@ -23,9 +23,6 @@ struct VertexOutput {
#ifdef MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT #ifdef MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT
@location(0) @interpolate(flat) packed_ids: u32, @location(0) @interpolate(flat) packed_ids: u32,
#endif #endif
#ifdef DEPTH_CLAMP_ORTHO
@location(0) unclamped_clip_depth: f32,
#endif
} }
@vertex @vertex
@ -45,19 +42,12 @@ fn vertex(@builtin(instance_index) instance_index: u32, @builtin(vertex_index) v
let vertex_position = get_meshlet_vertex_position(&meshlet, vertex_id); let vertex_position = get_meshlet_vertex_position(&meshlet, vertex_id);
let world_from_local = affine3_to_square(instance_uniform.world_from_local); let world_from_local = affine3_to_square(instance_uniform.world_from_local);
let world_position = mesh_position_local_to_world(world_from_local, vec4(vertex_position, 1.0)); let world_position = mesh_position_local_to_world(world_from_local, vec4(vertex_position, 1.0));
var clip_position = view.clip_from_world * vec4(world_position.xyz, 1.0); let clip_position = view.clip_from_world * vec4(world_position.xyz, 1.0);
#ifdef DEPTH_CLAMP_ORTHO
let unclamped_clip_depth = clip_position.z;
clip_position.z = min(clip_position.z, 1.0);
#endif
return VertexOutput( return VertexOutput(
clip_position, clip_position,
#ifdef MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT #ifdef MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT
(cluster_id << 7u) | triangle_id, (cluster_id << 7u) | triangle_id,
#endif
#ifdef DEPTH_CLAMP_ORTHO
unclamped_clip_depth,
#endif #endif
); );
} }
@ -70,9 +60,6 @@ fn fragment(vertex_output: VertexOutput) {
let depth = bitcast<u32>(vertex_output.position.z); let depth = bitcast<u32>(vertex_output.position.z);
let visibility = (u64(depth) << 32u) | u64(vertex_output.packed_ids); let visibility = (u64(depth) << 32u) | u64(vertex_output.packed_ids);
atomicMax(&meshlet_visibility_buffer[frag_coord_1d], visibility); atomicMax(&meshlet_visibility_buffer[frag_coord_1d], visibility);
#else ifdef DEPTH_CLAMP_ORTHO
let depth = bitcast<u32>(vertex_output.unclamped_clip_depth);
atomicMax(&meshlet_visibility_buffer[frag_coord_1d], depth);
#else #else
let depth = bitcast<u32>(vertex_output.position.z); let depth = bitcast<u32>(vertex_output.position.z);
atomicMax(&meshlet_visibility_buffer[frag_coord_1d], depth); atomicMax(&meshlet_visibility_buffer[frag_coord_1d], depth);
@ -84,9 +71,6 @@ fn dummy_vertex() -> VertexOutput {
vec4(divide(0.0, 0.0)), // NaN vertex position vec4(divide(0.0, 0.0)), // NaN vertex position
#ifdef MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT #ifdef MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT
0u, 0u,
#endif
#ifdef DEPTH_CLAMP_ORTHO
0.0,
#endif #endif
); );
} }

View file

@ -85,10 +85,9 @@ impl Node for MeshletVisibilityBufferRasterPassNode {
downsample_depth_second_shadow_view_pipeline, downsample_depth_second_shadow_view_pipeline,
visibility_buffer_software_raster_pipeline, visibility_buffer_software_raster_pipeline,
visibility_buffer_software_raster_depth_only_pipeline, visibility_buffer_software_raster_depth_only_pipeline,
visibility_buffer_software_raster_depth_only_clamp_ortho,
visibility_buffer_hardware_raster_pipeline, visibility_buffer_hardware_raster_pipeline,
visibility_buffer_hardware_raster_depth_only_pipeline, visibility_buffer_hardware_raster_depth_only_pipeline,
visibility_buffer_hardware_raster_depth_only_clamp_ortho, visibility_buffer_hardware_raster_depth_only_unclipped_pipeline,
resolve_depth_pipeline, resolve_depth_pipeline,
resolve_depth_shadow_view_pipeline, resolve_depth_shadow_view_pipeline,
resolve_material_depth_pipeline, resolve_material_depth_pipeline,
@ -223,18 +222,11 @@ impl Node for MeshletVisibilityBufferRasterPassNode {
continue; continue;
}; };
let ( let shadow_visibility_buffer_hardware_raster_pipeline =
shadow_visibility_buffer_software_raster_pipeline, if let LightEntity::Directional { .. } = light_type {
shadow_visibility_buffer_hardware_raster_pipeline, visibility_buffer_hardware_raster_depth_only_unclipped_pipeline
) = match light_type { } else {
LightEntity::Directional { .. } => ( visibility_buffer_hardware_raster_depth_only_pipeline
visibility_buffer_software_raster_depth_only_clamp_ortho,
visibility_buffer_hardware_raster_depth_only_clamp_ortho,
),
_ => (
visibility_buffer_software_raster_depth_only_pipeline,
visibility_buffer_hardware_raster_depth_only_pipeline,
),
}; };
render_context.command_encoder().push_debug_group(&format!( render_context.command_encoder().push_debug_group(&format!(
@ -270,7 +262,7 @@ impl Node for MeshletVisibilityBufferRasterPassNode {
&meshlet_view_resources.dummy_render_target.default_view, &meshlet_view_resources.dummy_render_target.default_view,
meshlet_view_bind_groups, meshlet_view_bind_groups,
view_offset, view_offset,
shadow_visibility_buffer_software_raster_pipeline, visibility_buffer_software_raster_depth_only_pipeline,
shadow_visibility_buffer_hardware_raster_pipeline, shadow_visibility_buffer_hardware_raster_pipeline,
None, None,
meshlet_view_resources.raster_cluster_rightmost_slot, meshlet_view_resources.raster_cluster_rightmost_slot,
@ -306,7 +298,7 @@ impl Node for MeshletVisibilityBufferRasterPassNode {
&meshlet_view_resources.dummy_render_target.default_view, &meshlet_view_resources.dummy_render_target.default_view,
meshlet_view_bind_groups, meshlet_view_bind_groups,
view_offset, view_offset,
shadow_visibility_buffer_software_raster_pipeline, visibility_buffer_software_raster_depth_only_pipeline,
shadow_visibility_buffer_hardware_raster_pipeline, shadow_visibility_buffer_hardware_raster_pipeline,
None, None,
meshlet_view_resources.raster_cluster_rightmost_slot, meshlet_view_resources.raster_cluster_rightmost_slot,

View file

@ -57,10 +57,7 @@ fn rasterize_cluster(
// Project vertex to viewport space // Project vertex to viewport space
let world_position = mesh_position_local_to_world(world_from_local, vec4(vertex_position, 1.0)); let world_position = mesh_position_local_to_world(world_from_local, vec4(vertex_position, 1.0));
let clip_position = view.clip_from_world * vec4(world_position.xyz, 1.0); let clip_position = view.clip_from_world * vec4(world_position.xyz, 1.0);
var ndc_position = clip_position.xyz / clip_position.w; let ndc_position = clip_position.xyz / clip_position.w;
#ifdef DEPTH_CLAMP_ORTHO
ndc_position.z = 1.0 / clip_position.z;
#endif
let viewport_position_xy = ndc_to_uv(ndc_position.xy) * view.viewport.zw; let viewport_position_xy = ndc_to_uv(ndc_position.xy) * view.viewport.zw;
// Write vertex to workgroup shared memory // Write vertex to workgroup shared memory
@ -176,9 +173,6 @@ fn write_visibility_buffer_pixel(x: f32, y: f32, z: f32, packed_ids: u32) {
let depth = bitcast<u32>(z); let depth = bitcast<u32>(z);
let visibility = (u64(depth) << 32u) | u64(packed_ids); let visibility = (u64(depth) << 32u) | u64(packed_ids);
atomicMax(&meshlet_visibility_buffer[frag_coord_1d], visibility); atomicMax(&meshlet_visibility_buffer[frag_coord_1d], visibility);
#else ifdef DEPTH_CLAMP_ORTHO
let depth = bitcast<u32>(1.0 / z);
atomicMax(&meshlet_visibility_buffer[frag_coord_1d], depth);
#else #else
let depth = bitcast<u32>(z); let depth = bitcast<u32>(z);
atomicMax(&meshlet_visibility_buffer[frag_coord_1d], depth); atomicMax(&meshlet_visibility_buffer[frag_coord_1d], depth);

View file

@ -251,6 +251,7 @@ pub struct PrepassPipeline<M: Material> {
pub deferred_material_vertex_shader: Option<Handle<Shader>>, pub deferred_material_vertex_shader: Option<Handle<Shader>>,
pub deferred_material_fragment_shader: Option<Handle<Shader>>, pub deferred_material_fragment_shader: Option<Handle<Shader>>,
pub material_pipeline: MaterialPipeline<M>, pub material_pipeline: MaterialPipeline<M>,
pub depth_clip_control_supported: bool,
_marker: PhantomData<M>, _marker: PhantomData<M>,
} }
@ -289,6 +290,10 @@ impl<M: Material> FromWorld for PrepassPipeline<M> {
let mesh_pipeline = world.resource::<MeshPipeline>(); let mesh_pipeline = world.resource::<MeshPipeline>();
let depth_clip_control_supported = render_device
.features()
.contains(WgpuFeatures::DEPTH_CLIP_CONTROL);
PrepassPipeline { PrepassPipeline {
view_layout_motion_vectors, view_layout_motion_vectors,
view_layout_no_motion_vectors, view_layout_no_motion_vectors,
@ -315,6 +320,7 @@ impl<M: Material> FromWorld for PrepassPipeline<M> {
}, },
material_layout: M::bind_group_layout(render_device), material_layout: M::bind_group_layout(render_device),
material_pipeline: world.resource::<MaterialPipeline<M>>().clone(), material_pipeline: world.resource::<MaterialPipeline<M>>().clone(),
depth_clip_control_supported,
_marker: PhantomData, _marker: PhantomData,
} }
} }
@ -379,8 +385,14 @@ where
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
} }
if key.mesh_key.contains(MeshPipelineKey::DEPTH_CLAMP_ORTHO) { // For directional light shadow map views, use unclipped depth via either the native GPU feature,
shader_defs.push("DEPTH_CLAMP_ORTHO".into()); // or emulated by setting depth in the fragment shader for GPUs that don't support it natively.
let emulate_unclipped_depth = key
.mesh_key
.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
&& !self.depth_clip_control_supported;
if emulate_unclipped_depth {
shader_defs.push("UNCLIPPED_DEPTH_ORTHO_EMULATION".into());
// PERF: This line forces the "prepass fragment shader" to always run in // PERF: This line forces the "prepass fragment shader" to always run in
// common scenarios like "directional light calculation". Doing so resolves // common scenarios like "directional light calculation". Doing so resolves
// a pretty nasty depth clamping bug, but it also feels a bit excessive. // a pretty nasty depth clamping bug, but it also feels a bit excessive.
@ -389,6 +401,10 @@ where
// https://github.com/bevyengine/bevy/pull/8877 // https://github.com/bevyengine/bevy/pull/8877
shader_defs.push("PREPASS_FRAGMENT".into()); shader_defs.push("PREPASS_FRAGMENT".into());
} }
let unclipped_depth = key
.mesh_key
.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
&& self.depth_clip_control_supported;
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) { if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
shader_defs.push("VERTEX_UVS".into()); shader_defs.push("VERTEX_UVS".into());
@ -488,10 +504,10 @@ where
} }
// The fragment shader is only used when the normal prepass or motion vectors prepass // The fragment shader is only used when the normal prepass or motion vectors prepass
// is enabled or the material uses alpha cutoff values and doesn't rely on the standard // is enabled, the material uses alpha cutoff values and doesn't rely on the standard
// prepass shader or we are clamping the orthographic depth. // prepass shader, or we are emulating unclipped depth in the fragment shader.
let fragment_required = !targets.is_empty() let fragment_required = !targets.is_empty()
|| key.mesh_key.contains(MeshPipelineKey::DEPTH_CLAMP_ORTHO) || emulate_unclipped_depth
|| (key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) || (key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
&& self.prepass_material_fragment_shader.is_some()); && self.prepass_material_fragment_shader.is_some());
@ -544,7 +560,7 @@ where
strip_index_format: None, strip_index_format: None,
front_face: FrontFace::Ccw, front_face: FrontFace::Ccw,
cull_mode: None, cull_mode: None,
unclipped_depth: false, unclipped_depth,
polygon_mode: PolygonMode::Fill, polygon_mode: PolygonMode::Fill,
conservative: false, conservative: false,
}, },

View file

@ -76,10 +76,10 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
out.world_position = mesh_functions::mesh_position_local_to_world(world_from_local, vec4<f32>(vertex.position, 1.0)); out.world_position = mesh_functions::mesh_position_local_to_world(world_from_local, vec4<f32>(vertex.position, 1.0));
out.position = position_world_to_clip(out.world_position.xyz); out.position = position_world_to_clip(out.world_position.xyz);
#ifdef DEPTH_CLAMP_ORTHO #ifdef UNCLIPPED_DEPTH_ORTHO_EMULATION
out.clip_position_unclamped = out.position; out.unclipped_depth = out.position.z;
out.position.z = min(out.position.z, 1.0); out.position.z = min(out.position.z, 1.0); // Clamp depth to avoid clipping
#endif // DEPTH_CLAMP_ORTHO #endif // UNCLIPPED_DEPTH_ORTHO_EMULATION
#ifdef VERTEX_UVS_A #ifdef VERTEX_UVS_A
out.uv = vertex.uv; out.uv = vertex.uv;
@ -173,9 +173,9 @@ fn fragment(in: VertexOutput) -> FragmentOutput {
out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0); out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0);
#endif #endif
#ifdef DEPTH_CLAMP_ORTHO #ifdef UNCLIPPED_DEPTH_ORTHO_EMULATION
out.frag_depth = in.clip_position_unclamped.z; out.frag_depth = in.unclipped_depth;
#endif // DEPTH_CLAMP_ORTHO #endif // UNCLIPPED_DEPTH_ORTHO_EMULATION
#ifdef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
let clip_position_t = view.unjittered_clip_from_world * in.world_position; let clip_position_t = view.unjittered_clip_from_world * in.world_position;

View file

@ -60,9 +60,9 @@ struct VertexOutput {
@location(5) previous_world_position: vec4<f32>, @location(5) previous_world_position: vec4<f32>,
#endif #endif
#ifdef DEPTH_CLAMP_ORTHO #ifdef UNCLIPPED_DEPTH_ORTHO_EMULATION
@location(6) clip_position_unclamped: vec4<f32>, @location(6) unclipped_depth: f32,
#endif // DEPTH_CLAMP_ORTHO #endif // UNCLIPPED_DEPTH_ORTHO_EMULATION
#ifdef VERTEX_OUTPUT_INSTANCE_INDEX #ifdef VERTEX_OUTPUT_INSTANCE_INDEX
@location(7) instance_index: u32, @location(7) instance_index: u32,
#endif #endif
@ -87,8 +87,8 @@ struct FragmentOutput {
@location(3) deferred_lighting_pass_id: u32, @location(3) deferred_lighting_pass_id: u32,
#endif #endif
#ifdef DEPTH_CLAMP_ORTHO #ifdef UNCLIPPED_DEPTH_ORTHO_EMULATION
@builtin(frag_depth) frag_depth: f32, @builtin(frag_depth) frag_depth: f32,
#endif // DEPTH_CLAMP_ORTHO #endif // UNCLIPPED_DEPTH_ORTHO_EMULATION
} }
#endif //PREPASS_FRAGMENT #endif //PREPASS_FRAGMENT

View file

@ -1554,7 +1554,7 @@ pub fn queue_shadows<M: Material>(
.expect("Failed to get spot light visible entities"), .expect("Failed to get spot light visible entities"),
}; };
let mut light_key = MeshPipelineKey::DEPTH_PREPASS; let mut light_key = MeshPipelineKey::DEPTH_PREPASS;
light_key.set(MeshPipelineKey::DEPTH_CLAMP_ORTHO, is_directional_light); light_key.set(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO, is_directional_light);
// NOTE: Lights with shadow mapping disabled will have no visible entities // NOTE: Lights with shadow mapping disabled will have no visible entities
// so no meshes will be queued // so no meshes will be queued

View file

@ -1489,7 +1489,9 @@ bitflags::bitflags! {
// See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test // See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
const ENVIRONMENT_MAP = 1 << 8; const ENVIRONMENT_MAP = 1 << 8;
const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 9; const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 9;
const DEPTH_CLAMP_ORTHO = 1 << 10; const UNCLIPPED_DEPTH_ORTHO = 1 << 10; // Disables depth clipping for use with directional light shadow views
// Emulated via fragment shader depth on hardware that doesn't support it natively
// See: https://www.w3.org/TR/webgpu/#depth-clipping and https://therealmjp.github.io/posts/shadow-maps/#disabling-z-clipping
const TEMPORAL_JITTER = 1 << 11; const TEMPORAL_JITTER = 1 << 11;
const READS_VIEW_TRANSMISSION_TEXTURE = 1 << 12; const READS_VIEW_TRANSMISSION_TEXTURE = 1 << 12;
const LIGHTMAPPED = 1 << 13; const LIGHTMAPPED = 1 << 13;

View file

@ -32,9 +32,9 @@ fn fragment(
var out: prepass_io::FragmentOutput; var out: prepass_io::FragmentOutput;
#ifdef DEPTH_CLAMP_ORTHO #ifdef UNCLIPPED_DEPTH_ORTHO_EMULATION
out.frag_depth = in.clip_position_unclamped.z; out.frag_depth = in.unclipped_depth;
#endif // DEPTH_CLAMP_ORTHO #endif // UNCLIPPED_DEPTH_ORTHO_EMULATION
#ifdef NORMAL_PREPASS #ifdef NORMAL_PREPASS
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit