bevy/crates/bevy_pbr/src/render/pbr_prepass.wgsl
JMS55 9cc7e7c080
Meshlet screenspace-derived tangents (#15084)
* Save 16 bytes per vertex by calculating tangents in the shader at
runtime, rather than storing them in the vertex data.
* Based on https://jcgt.org/published/0009/03/04,
https://www.jeremyong.com/graphics/2023/12/16/surface-gradient-bump-mapping.
* Fixed visbuffer resolve to use the updated algorithm that flips ddy
correctly
* Added some more docs about meshlet material limitations, and some
TODOs about transforming UV coordinates for the future.


![image](https://github.com/user-attachments/assets/222d8192-8c82-4d77-945d-53670a503761)

For testing add a normal map to the bunnies with StandardMaterial like
below, and then test that on both main and this PR (make sure to
download the correct bunny for each). Results should be mostly
identical.

```rust
normal_map_texture: Some(asset_server.load_with_settings(
    "textures/BlueNoise-Normal.png",
    |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
)),
```
2024-09-29 18:39:25 +00:00

113 lines
3.1 KiB
WebGPU Shading Language

#import bevy_pbr::{
pbr_prepass_functions,
pbr_bindings,
pbr_bindings::material,
pbr_types,
pbr_functions,
pbr_functions::SampleBias,
prepass_io,
mesh_view_bindings::view,
}
#ifdef MESHLET_MESH_MATERIAL_PASS
#import bevy_pbr::meshlet_visibility_buffer_resolve::resolve_vertex_output
#endif
#ifdef PREPASS_FRAGMENT
@fragment
fn fragment(
#ifdef MESHLET_MESH_MATERIAL_PASS
@builtin(position) frag_coord: vec4<f32>,
#else
in: prepass_io::VertexOutput,
@builtin(front_facing) is_front: bool,
#endif
) -> prepass_io::FragmentOutput {
#ifdef MESHLET_MESH_MATERIAL_PASS
let in = resolve_vertex_output(frag_coord);
let is_front = true;
#else
pbr_prepass_functions::prepass_alpha_discard(in);
#endif
var out: prepass_io::FragmentOutput;
#ifdef DEPTH_CLAMP_ORTHO
out.frag_depth = in.clip_position_unclamped.z;
#endif // DEPTH_CLAMP_ORTHO
#ifdef NORMAL_PREPASS
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
if (material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
let double_sided = (material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u;
let world_normal = pbr_functions::prepare_world_normal(
in.world_normal,
double_sided,
is_front,
);
var normal = world_normal;
#ifdef VERTEX_UVS
#ifdef VERTEX_TANGENTS
#ifdef STANDARD_MATERIAL_NORMAL_MAP
// TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass
#ifdef STANDARD_MATERIAL_NORMAL_MAP_UV_B
let uv = (material.uv_transform * vec3(in.uv_b, 1.0)).xy;
#else
let uv = (material.uv_transform * vec3(in.uv, 1.0)).xy;
#endif
// Fill in the sample bias so we can sample from textures.
var bias: SampleBias;
#ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv = in.ddx_uv;
bias.ddy_uv = in.ddy_uv;
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias = view.mip_bias;
#endif // MESHLET_MESH_MATERIAL_PASS
let Nt = pbr_functions::sample_texture(
pbr_bindings::normal_map_texture,
pbr_bindings::normal_map_sampler,
uv,
bias,
).rgb;
let TBN = pbr_functions::calculate_tbn_mikktspace(normal, in.world_tangent);
normal = pbr_functions::apply_normal_mapping(
material.flags,
TBN,
double_sided,
is_front,
Nt,
);
#endif // STANDARD_MATERIAL_NORMAL_MAP
#endif // VERTEX_TANGENTS
#endif // VERTEX_UVS
out.normal = vec4(normal * 0.5 + vec3(0.5), 1.0);
} else {
out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0);
}
#endif // NORMAL_PREPASS
#ifdef MOTION_VECTOR_PREPASS
#ifdef MESHLET_MESH_MATERIAL_PASS
out.motion_vector = in.motion_vector;
#else
out.motion_vector = pbr_prepass_functions::calculate_motion_vector(in.world_position, in.previous_world_position);
#endif
#endif
return out;
}
#else
@fragment
fn fragment(in: prepass_io::VertexOutput) {
pbr_prepass_functions::prepass_alpha_discard(in);
}
#endif // PREPASS_FRAGMENT