Skip to content

Commit d67b1df

Browse files
committed
bevy_pbr: Support flipping tangent space normal map y for DirectX normal maps (#4433)
# Objective - Normal maps authored for DirectX use a left-handed convention and have their tangent space normal in the texture inverted from what we need. Support this. - Details here: https://doc.babylonjs.com/divingDeeper/materials/advanced/normalMaps ## Solution - Add a `StandardMaterial` `flip_normal_map_y` boolean field - Add a `STANDARDMATERIAL_FLIP_NORMAL_MAP_Y` flag to `StandardMaterialFlags` and in the PBR shader - Flip the y-component of the tangent space normal just after sampling it from the normal map texture ## Screenshots ### Before <img width="1392" alt="Screenshot 2022-04-06 at 21 04 44" src="https://user-images.githubusercontent.com/302146/162050314-e7bfaaf6-9ee1-4756-9821-f6f5ff78f508.png"> ### After <img width="1392" alt="Screenshot 2022-04-06 at 21 03 39" src="https://user-images.githubusercontent.com/302146/162050255-36ee0745-1d79-4fd2-9a1c-18085376b643.png"> --- ## Changelog - Added: Support for flipping the normal map texture y component for normal maps authored for use with DirectX
1 parent f907d67 commit d67b1df

File tree

2 files changed

+13
-0
lines changed

2 files changed

+13
-0
lines changed

crates/bevy_pbr/src/pbr_material.rs

+8
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ pub struct StandardMaterial {
4747
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
4848
pub reflectance: f32,
4949
pub normal_map_texture: Option<Handle<Image>>,
50+
/// Normal map textures authored for DirectX have their y-component flipped. Set this to flip
51+
/// it to right-handed conventions.
52+
pub flip_normal_map_y: bool,
5053
pub occlusion_texture: Option<Handle<Image>>,
5154
/// Support two-sided lighting by automatically flipping the normals for "back" faces
5255
/// within the PBR lighting shader.
@@ -84,6 +87,7 @@ impl Default for StandardMaterial {
8487
reflectance: 0.5,
8588
occlusion_texture: None,
8689
normal_map_texture: None,
90+
flip_normal_map_y: false,
8791
double_sided: false,
8892
cull_mode: Some(Face::Back),
8993
unlit: false,
@@ -124,6 +128,7 @@ bitflags::bitflags! {
124128
const ALPHA_MODE_MASK = (1 << 7);
125129
const ALPHA_MODE_BLEND = (1 << 8);
126130
const TWO_COMPONENT_NORMAL_MAP = (1 << 9);
131+
const FLIP_NORMAL_MAP_Y = (1 << 10);
127132
const NONE = 0;
128133
const UNINITIALIZED = 0xFFFF;
129134
}
@@ -262,6 +267,9 @@ impl RenderAsset for StandardMaterial {
262267
}
263268
_ => {}
264269
}
270+
if material.flip_normal_map_y {
271+
flags |= StandardMaterialFlags::FLIP_NORMAL_MAP_Y;
272+
}
265273
}
266274
// NOTE: 0.5 is from the glTF default - do we want this?
267275
let mut alpha_cutoff = 0.5;

crates/bevy_pbr/src/render/pbr.wgsl

+5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE: u32 = 64u;
5959
let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK: u32 = 128u;
6060
let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND: u32 = 256u;
6161
let STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP: u32 = 512u;
62+
let STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y: u32 = 1024u;
6263

6364
[[group(1), binding(0)]]
6465
var<uniform> material: StandardMaterial;
@@ -525,6 +526,10 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
525526
} else {
526527
Nt = textureSample(normal_map_texture, normal_map_sampler, in.uv).rgb * 2.0 - 1.0;
527528
}
529+
// Normal maps authored for DirectX require flipping the y component
530+
if ((material.flags & STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u) {
531+
Nt.y = -Nt.y;
532+
}
528533
N = normalize(TBN * Nt);
529534
#endif
530535
#endif

0 commit comments

Comments
 (0)