bevy/crates/bevy_pbr/src/render/pbr.wgsl
Nicola Papale c488b7089c
Fix pbr shader breaking on missing UVs (#8412)
# Objective

Fix #8409

Since `in` doesn't have a `uv` field when `VERTEX_UVS` is not defined,
it shouldn't be accessed outside of related `#ifdef`s
2023-04-17 06:22:34 +00:00

156 lines
5.4 KiB
WebGPU Shading Language

#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::pbr_bindings
#import bevy_pbr::mesh_bindings
#import bevy_pbr::utils
#import bevy_pbr::clustered_forward
#import bevy_pbr::lighting
#import bevy_pbr::pbr_ambient
#import bevy_pbr::shadows
#import bevy_pbr::fog
#import bevy_pbr::pbr_functions
#import bevy_pbr::parallax_mapping
#import bevy_pbr::prepass_utils
struct FragmentInput {
@builtin(front_facing) is_front: bool,
@builtin(position) frag_coord: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
};
@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
let is_orthographic = view.projection[3].w == 1.0;
let V = calculate_view(in.world_position, is_orthographic);
#ifdef VERTEX_UVS
var uv = in.uv;
#ifdef VERTEX_TANGENTS
if ((material.flags & STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) {
let N = in.world_normal;
let T = in.world_tangent.xyz;
let B = in.world_tangent.w * cross(N, T);
// Transform V from fragment to camera in world space to tangent space.
let Vt = vec3(dot(V, T), dot(V, B), dot(V, N));
uv = parallaxed_uv(
material.parallax_depth_scale,
material.max_parallax_layer_count,
material.max_relief_mapping_search_steps,
uv,
// Flip the direction of Vt to go toward the surface to make the
// parallax mapping algorithm easier to understand and reason
// about.
-Vt,
);
}
#endif
#endif
var output_color: vec4<f32> = material.base_color;
#ifdef VERTEX_COLORS
output_color = output_color * in.color;
#endif
#ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
output_color = output_color * textureSample(base_color_texture, base_color_sampler, uv);
}
#endif
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
if ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {
// Prepare a 'processed' StandardMaterial by sampling all textures to resolve
// the material members
var pbr_input: PbrInput;
pbr_input.material.base_color = output_color;
pbr_input.material.reflectance = material.reflectance;
pbr_input.material.flags = material.flags;
pbr_input.material.alpha_cutoff = material.alpha_cutoff;
// TODO use .a for exposure compensation in HDR
var emissive: vec4<f32> = material.emissive;
#ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
emissive = vec4<f32>(emissive.rgb * textureSample(emissive_texture, emissive_sampler, uv).rgb, 1.0);
}
#endif
pbr_input.material.emissive = emissive;
var metallic: f32 = material.metallic;
var perceptual_roughness: f32 = material.perceptual_roughness;
#ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
let metallic_roughness = textureSample(metallic_roughness_texture, metallic_roughness_sampler, uv);
// Sampling from GLTF standard channels for now
metallic = metallic * metallic_roughness.b;
perceptual_roughness = perceptual_roughness * metallic_roughness.g;
}
#endif
pbr_input.material.metallic = metallic;
pbr_input.material.perceptual_roughness = perceptual_roughness;
var occlusion: f32 = 1.0;
#ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
occlusion = textureSample(occlusion_texture, occlusion_sampler, uv).r;
}
#endif
pbr_input.frag_coord = in.frag_coord;
pbr_input.world_position = in.world_position;
#ifdef LOAD_PREPASS_NORMALS
pbr_input.world_normal = prepass_normal(in.frag_coord, 0u);
#else // LOAD_PREPASS_NORMALS
pbr_input.world_normal = prepare_world_normal(
in.world_normal,
(material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
in.is_front,
);
#endif // LOAD_PREPASS_NORMALS
pbr_input.is_orthographic = is_orthographic;
pbr_input.N = apply_normal_mapping(
material.flags,
pbr_input.world_normal,
#ifdef VERTEX_TANGENTS
#ifdef STANDARDMATERIAL_NORMAL_MAP
in.world_tangent,
#endif
#endif
#ifdef VERTEX_UVS
uv,
#endif
);
pbr_input.V = V;
pbr_input.occlusion = occlusion;
pbr_input.flags = mesh.flags;
output_color = pbr(pbr_input);
} else {
output_color = alpha_discard(material, output_color);
}
// fog
if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
output_color = apply_fog(output_color, in.world_position.xyz, view.world_position.xyz);
}
#ifdef TONEMAP_IN_SHADER
output_color = tone_mapping(output_color);
#ifdef DEBAND_DITHER
var output_rgb = output_color.rgb;
output_rgb = powsafe(output_rgb, 1.0 / 2.2);
output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy);
// This conversion back to linear space is required because our output texture format is
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
output_rgb = powsafe(output_rgb, 2.2);
output_color = vec4(output_rgb, output_color.a);
#endif
#endif
#ifdef PREMULTIPLY_ALPHA
output_color = premultiply_alpha(material.flags, output_color);
#endif
return output_color;
}