Skip to content

Commit

Permalink
bevy_pbr: Support flipping tangent space normal map y for DirectX nor…
Browse files Browse the repository at this point in the history
…mal 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
  • Loading branch information
superdump committed Apr 7, 2022
1 parent e7e7445 commit 579928e
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 0 deletions.
8 changes: 8 additions & 0 deletions crates/bevy_pbr/src/pbr_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Handle<Image>>,
/// 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<Handle<Image>>,
/// Support two-sided lighting by automatically flipping the normals for "back" faces
/// within the PBR lighting shader.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -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<uniform> material: StandardMaterial;
Expand Down Expand Up @@ -525,6 +526,10 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
} 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
Expand Down

0 comments on commit 579928e

Please sign in to comment.