
# Objective the pbr prepass vertex shader currently only sets `VertexOutput::world_position` when deferred or motion prepasses are enabled. the field is always in the vertex output so is otherwise undetermined, and the calculation is very cheap. ## Solution always set the world position in the pbr prepass vert shader.
157 lines
5.8 KiB
WebGPU Shading Language
157 lines
5.8 KiB
WebGPU Shading Language
#import bevy_pbr::{
|
||
prepass_bindings,
|
||
mesh_functions,
|
||
prepass_io::{Vertex, VertexOutput, FragmentOutput},
|
||
skinning,
|
||
morph,
|
||
mesh_view_bindings::{view, previous_view_proj},
|
||
}
|
||
|
||
#import bevy_render::instance_index::get_instance_index
|
||
|
||
#ifdef DEFERRED_PREPASS
|
||
#import bevy_pbr::rgb9e5
|
||
#endif
|
||
|
||
#ifdef MORPH_TARGETS
|
||
fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
||
var vertex = vertex_in;
|
||
let weight_count = morph::layer_count();
|
||
for (var i: u32 = 0u; i < weight_count; i ++) {
|
||
let weight = morph::weight_at(i);
|
||
if weight == 0.0 {
|
||
continue;
|
||
}
|
||
vertex.position += weight * morph::morph(vertex.index, morph::position_offset, i);
|
||
#ifdef VERTEX_NORMALS
|
||
vertex.normal += weight * morph::morph(vertex.index, morph::normal_offset, i);
|
||
#endif
|
||
#ifdef VERTEX_TANGENTS
|
||
vertex.tangent += vec4(weight * morph::morph(vertex.index, morph::tangent_offset, i), 0.0);
|
||
#endif
|
||
}
|
||
return vertex;
|
||
}
|
||
#endif
|
||
|
||
@vertex
|
||
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
||
var out: VertexOutput;
|
||
|
||
#ifdef MORPH_TARGETS
|
||
var vertex = morph_vertex(vertex_no_morph);
|
||
#else
|
||
var vertex = vertex_no_morph;
|
||
#endif
|
||
|
||
#ifdef SKINNED
|
||
var model = skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
|
||
#else // SKINNED
|
||
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
|
||
// See https://github.com/gfx-rs/naga/issues/2416
|
||
var model = mesh_functions::get_model_matrix(vertex_no_morph.instance_index);
|
||
#endif // SKINNED
|
||
|
||
out.position = mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0));
|
||
#ifdef DEPTH_CLAMP_ORTHO
|
||
out.clip_position_unclamped = out.position;
|
||
out.position.z = min(out.position.z, 1.0);
|
||
#endif // DEPTH_CLAMP_ORTHO
|
||
|
||
#ifdef VERTEX_UVS
|
||
out.uv = vertex.uv;
|
||
#endif // VERTEX_UVS
|
||
|
||
#ifdef NORMAL_PREPASS_OR_DEFERRED_PREPASS
|
||
#ifdef SKINNED
|
||
out.world_normal = skinning::skin_normals(model, vertex.normal);
|
||
#else // SKINNED
|
||
out.world_normal = mesh_functions::mesh_normal_local_to_world(
|
||
vertex.normal,
|
||
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
|
||
// See https://github.com/gfx-rs/naga/issues/2416
|
||
get_instance_index(vertex_no_morph.instance_index)
|
||
);
|
||
#endif // SKINNED
|
||
|
||
#ifdef VERTEX_TANGENTS
|
||
out.world_tangent = mesh_functions::mesh_tangent_local_to_world(
|
||
model,
|
||
vertex.tangent,
|
||
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
|
||
// See https://github.com/gfx-rs/naga/issues/2416
|
||
get_instance_index(vertex_no_morph.instance_index)
|
||
);
|
||
#endif // VERTEX_TANGENTS
|
||
#endif // NORMAL_PREPASS_OR_DEFERRED_PREPASS
|
||
|
||
#ifdef VERTEX_COLORS
|
||
out.color = vertex.color;
|
||
#endif
|
||
|
||
out.world_position = mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
|
||
|
||
#ifdef MOTION_VECTOR_PREPASS
|
||
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
|
||
// See https://github.com/gfx-rs/naga/issues/2416
|
||
out.previous_world_position = mesh_functions::mesh_position_local_to_world(
|
||
mesh_functions::get_previous_model_matrix(vertex_no_morph.instance_index),
|
||
vec4<f32>(vertex.position, 1.0)
|
||
);
|
||
#endif // MOTION_VECTOR_PREPASS
|
||
|
||
#ifdef VERTEX_OUTPUT_INSTANCE_INDEX
|
||
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
|
||
// See https://github.com/gfx-rs/naga/issues/2416
|
||
out.instance_index = get_instance_index(vertex_no_morph.instance_index);
|
||
#endif
|
||
#ifdef BASE_INSTANCE_WORKAROUND
|
||
// Hack: this ensures the push constant is always used, which works around this issue:
|
||
// https://github.com/bevyengine/bevy/issues/10509
|
||
// This can be removed when wgpu 0.19 is released
|
||
out.position.x += min(f32(get_instance_index(0u)), 0.0);
|
||
#endif
|
||
|
||
return out;
|
||
}
|
||
|
||
#ifdef PREPASS_FRAGMENT
|
||
@fragment
|
||
fn fragment(in: VertexOutput) -> FragmentOutput {
|
||
var out: FragmentOutput;
|
||
|
||
#ifdef NORMAL_PREPASS
|
||
out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0);
|
||
#endif
|
||
|
||
#ifdef DEPTH_CLAMP_ORTHO
|
||
out.frag_depth = in.clip_position_unclamped.z;
|
||
#endif // DEPTH_CLAMP_ORTHO
|
||
|
||
#ifdef MOTION_VECTOR_PREPASS
|
||
let clip_position_t = view.unjittered_view_proj * in.world_position;
|
||
let clip_position = clip_position_t.xy / clip_position_t.w;
|
||
let previous_clip_position_t = prepass_bindings::previous_view_proj * in.previous_world_position;
|
||
let previous_clip_position = previous_clip_position_t.xy / previous_clip_position_t.w;
|
||
// These motion vectors are used as offsets to UV positions and are stored
|
||
// in the range -1,1 to allow offsetting from the one corner to the
|
||
// diagonally-opposite corner in UV coordinates, in either direction.
|
||
// A difference between diagonally-opposite corners of clip space is in the
|
||
// range -2,2, so this needs to be scaled by 0.5. And the V direction goes
|
||
// down where clip space y goes up, so y needs to be flipped.
|
||
out.motion_vector = (clip_position - previous_clip_position) * vec2(0.5, -0.5);
|
||
#endif // MOTION_VECTOR_PREPASS
|
||
|
||
#ifdef DEFERRED_PREPASS
|
||
// There isn't any material info available for this default prepass shader so we are just writing
|
||
// emissive magenta out to the deferred gbuffer to be rendered by the first deferred lighting pass layer.
|
||
// The is here so if the default prepass fragment is used for deferred magenta will be rendered, and also
|
||
// as an example to show that a user could write to the deferred gbuffer if they were to start from this shader.
|
||
out.deferred = vec4(0u, bevy_pbr::rgb9e5::vec3_to_rgb9e5_(vec3(1.0, 0.0, 1.0)), 0u, 0u);
|
||
out.deferred_lighting_pass_id = 1u;
|
||
#endif
|
||
|
||
return out;
|
||
}
|
||
#endif // PREPASS_FRAGMENT
|