diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index a9dccbdc3b960..a63950042757c 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -47,6 +47,9 @@ pub struct StandardMaterial { /// defaults to 0.5 which is mapped to 4% reflectance in the shader pub reflectance: f32, pub normal_map_texture: Option>, + /// Normal map textures authored for DirectX have their y-component flipped. Set this to flip + /// it to right-handed conventions. + pub flip_normal_map_y: bool, pub occlusion_texture: Option>, /// Support two-sided lighting by automatically flipping the normals for "back" faces /// within the PBR lighting shader. @@ -84,6 +87,7 @@ impl Default for StandardMaterial { reflectance: 0.5, occlusion_texture: None, normal_map_texture: None, + flip_normal_map_y: false, double_sided: false, cull_mode: Some(Face::Back), unlit: false, @@ -124,6 +128,7 @@ bitflags::bitflags! { const ALPHA_MODE_MASK = (1 << 7); const ALPHA_MODE_BLEND = (1 << 8); const TWO_COMPONENT_NORMAL_MAP = (1 << 9); + const FLIP_NORMAL_MAP_Y = (1 << 10); const NONE = 0; const UNINITIALIZED = 0xFFFF; } @@ -262,6 +267,9 @@ impl RenderAsset for StandardMaterial { } _ => {} } + if material.flip_normal_map_y { + flags |= StandardMaterialFlags::FLIP_NORMAL_MAP_Y; + } } // NOTE: 0.5 is from the glTF default - do we want this? let mut alpha_cutoff = 0.5; diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 1e1e69f02a235..9a7854ce32cfc 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -59,6 +59,7 @@ let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE: u32 = 64u; let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK: u32 = 128u; let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND: u32 = 256u; let STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP: u32 = 512u; +let STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y: u32 = 1024u; [[group(1), binding(0)]] var material: StandardMaterial; @@ -525,6 +526,10 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { } else { Nt = textureSample(normal_map_texture, normal_map_sampler, in.uv).rgb * 2.0 - 1.0; } + // Normal maps authored for DirectX require flipping the y component + if ((material.flags & STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u) { + Nt.y = -Nt.y; + } N = normalize(TBN * Nt); #endif #endif