Skip to content

Commit

Permalink
bevy_pbr2: Make normal map pipeline selection logic more robust
Browse files Browse the repository at this point in the history
The pipeline layout depends on the availability of vertex tangents, whereas
the normal map texture can be present or not in either case without problem.
  • Loading branch information
superdump committed Sep 6, 2021
1 parent 9991368 commit 08c92e5
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 66 deletions.
4 changes: 4 additions & 0 deletions pipelined/bevy_pbr2/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ bitflags::bitflags! {
const OCCLUSION_TEXTURE = (1 << 3);
const DOUBLE_SIDED = (1 << 4);
const UNLIT = (1 << 5);
const NORMAL_MAP_TEXTURE = (1 << 6);
const NONE = 0;
const UNINITIALIZED = 0xFFFF;
}
Expand Down Expand Up @@ -176,6 +177,9 @@ impl RenderAsset for StandardMaterial {
if material.unlit {
flags |= StandardMaterialFlags::UNLIT;
}
if material.normal_map_texture.is_some() {
flags |= StandardMaterialFlags::NORMAL_MAP_TEXTURE;
}
let value = StandardMaterialUniformData {
base_color: material.base_color.as_rgba_linear().into(),
emissive: material.emissive.into(),
Expand Down
3 changes: 1 addition & 2 deletions pipelined/bevy_pbr2/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,9 +728,8 @@ impl Draw for DrawShadowMesh {
self.params.get(world);
let view_uniform_offset = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
let shadow_shaders = shadow_shaders.into_inner();
if mesh_draw_info.has_normal_map {
if extracted_mesh.has_vertex_tangents {
pass.set_render_pipeline(&shadow_shaders.normal_map_pipeline);
} else {
pass.set_render_pipeline(&shadow_shaders.pipeline);
Expand Down
80 changes: 38 additions & 42 deletions pipelined/bevy_pbr2/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,9 @@ impl FromWorld for PbrShaders {
attributes: &normal_map_vertex_attributes,
}],
&shader_module,
"vertex_with_normal_map",
"vertex_with_tangents",
&shader_module,
"fragment_with_normal_map",
"fragment_with_tangents",
&pipeline_layout,
);

Expand Down Expand Up @@ -470,6 +470,7 @@ struct ExtractedMesh {
material_handle: Handle<StandardMaterial>,
casts_shadows: bool,
receives_shadows: bool,
has_vertex_tangents: bool,
}

pub struct ExtractedMeshes {
Expand Down Expand Up @@ -498,48 +499,45 @@ pub fn extract_meshes(
maybe_not_shadow_receiver,
) in query.iter()
{
if !meshes.contains(mesh_handle) {
continue;
}

if let Some(material) = materials.get(material_handle) {
if let Some(ref image) = material.base_color_texture {
if !images.contains(image) {
continue;
if let Some(mesh) = meshes.get(mesh_handle) {
if let Some(material) = materials.get(material_handle) {
if let Some(ref image) = material.base_color_texture {
if !images.contains(image) {
continue;
}
}
}
if let Some(ref image) = material.emissive_texture {
if !images.contains(image) {
continue;
if let Some(ref image) = material.emissive_texture {
if !images.contains(image) {
continue;
}
}
}
if let Some(ref image) = material.metallic_roughness_texture {
if !images.contains(image) {
continue;
if let Some(ref image) = material.metallic_roughness_texture {
if !images.contains(image) {
continue;
}
}
}
if let Some(ref image) = material.occlusion_texture {
if !images.contains(image) {
continue;
if let Some(ref image) = material.occlusion_texture {
if !images.contains(image) {
continue;
}
}
}
if let Some(ref image) = material.normal_map_texture {
if !images.contains(image) {
continue;
if let Some(ref image) = material.normal_map_texture {
if !images.contains(image) {
continue;
}
}
extracted_meshes.push(ExtractedMesh {
transform: transform.compute_matrix(),
mesh: mesh_handle.clone_weak(),
transform_binding_offset: 0,
material_handle: material_handle.clone_weak(),
// NOTE: Double-negative is so that meshes cast and receive shadows by default
// Not not shadow caster means that this mesh is a shadow caster
casts_shadows: maybe_not_shadow_caster.is_none(),
receives_shadows: maybe_not_shadow_receiver.is_none(),
has_vertex_tangents: mesh.attribute(Mesh::ATTRIBUTE_TANGENT).is_some(),
});
}
extracted_meshes.push(ExtractedMesh {
transform: transform.compute_matrix(),
mesh: mesh_handle.clone_weak(),
transform_binding_offset: 0,
material_handle: material_handle.clone_weak(),
// NOTE: Double-negative is so that meshes cast and receive shadows by default
// Not not shadow caster means that this mesh is a shadow caster
casts_shadows: maybe_not_shadow_caster.is_none(),
receives_shadows: maybe_not_shadow_receiver.is_none(),
});
} else {
continue;
}
}

Expand All @@ -551,7 +549,6 @@ pub fn extract_meshes(
struct MeshDrawInfo {
// TODO: compare cost of doing this vs cloning the BindGroup?
material_bind_group_key: FrameSlabMapKey<BufferId, BindGroup>,
has_normal_map: bool,
}

#[derive(Debug, AsStd140)]
Expand Down Expand Up @@ -831,7 +828,6 @@ pub fn queue_meshes(

mesh_meta.mesh_draw_info.push(MeshDrawInfo {
material_bind_group_key,
has_normal_map: gpu_material.normal_map_texture.is_some(),
});

// NOTE: row 2 of the view matrix dotted with column 3 of the model matrix
Expand Down Expand Up @@ -931,8 +927,7 @@ impl Draw for DrawPbr {
let (view_uniforms, view_lights, mesh_view_bind_groups) = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
let mesh_meta = mesh_meta.into_inner();
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
if mesh_draw_info.has_normal_map {
if extracted_mesh.has_vertex_tangents {
pass.set_render_pipeline(&pbr_shaders.into_inner().normal_map_pipeline);
} else {
pass.set_render_pipeline(&pbr_shaders.into_inner().pipeline);
Expand All @@ -942,6 +937,7 @@ impl Draw for DrawPbr {
&mesh_view_bind_groups.view,
&[view_uniforms.offset, view_lights.gpu_light_binding_index],
);
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
pass.set_bind_group(
1,
// &mesh_meta.material_bind_groups[sort_key & ((1 << 10) - 1)],
Expand Down
48 changes: 26 additions & 22 deletions pipelined/bevy_pbr2/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ fn vertex(vertex: Vertex) -> VertexOutput {
return out;
}

struct VertexWithNormalMaps {
struct VertexWithTangents {
[[location(0)]] position: vec3<f32>;
[[location(1)]] normal: vec3<f32>;
[[location(2)]] uv: vec2<f32>;
[[location(3)]] tangent: vec4<f32>;
};

struct VertexWithNormalMapsOutput {
struct VertexWithTangentsOutput {
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] world_position: vec4<f32>;
[[location(1)]] world_normal: vec3<f32>;
Expand All @@ -68,10 +68,10 @@ struct VertexWithNormalMapsOutput {
};

[[stage(vertex)]]
fn vertex_with_normal_map(vertex: VertexWithNormalMaps) -> VertexWithNormalMapsOutput {
fn vertex_with_tangents(vertex: VertexWithTangents) -> VertexWithTangentsOutput {
let world_position = mesh.model * vec4<f32>(vertex.position, 1.0);

var out: VertexWithNormalMapsOutput;
var out: VertexWithTangentsOutput;
out.uv = vertex.uv;
out.world_position = world_position;
out.clip_position = view.view_proj * world_position;
Expand Down Expand Up @@ -142,6 +142,7 @@ let STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT: u32 = 4u;
let STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT: u32 = 8u;
let STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT: u32 = 16u;
let STANDARD_MATERIAL_FLAGS_UNLIT_BIT: u32 = 32u;
let STANDARD_MATERIAL_FLAGS_NORMAL_MAP_TEXTURE_BIT: u32 = 64u;

struct PointLight {
projection: mat4x4<f32>;
Expand Down Expand Up @@ -684,7 +685,7 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
return output_color;
}

struct FragmentWithNormalMapsInput {
struct FragmentWithTangentsInput {
[[builtin(front_facing)]] is_front: bool;
[[location(0)]] world_position: vec4<f32>;
[[location(1)]] world_normal: vec3<f32>;
Expand All @@ -693,7 +694,7 @@ struct FragmentWithNormalMapsInput {
};

[[stage(fragment)]]
fn fragment_with_normal_map(in: FragmentWithNormalMapsInput) -> [[location(0)]] vec4<f32> {
fn fragment_with_tangents(in: FragmentWithTangentsInput) -> [[location(0)]] vec4<f32> {
var output_color: vec4<f32> = material.base_color;
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv);
Expand All @@ -706,28 +707,31 @@ fn fragment_with_normal_map(in: FragmentWithNormalMapsInput) -> [[location(0)]]
let world_normal = normalize(in.world_normal);
var N: vec3<f32> = world_normal;

// NOTE: The next two lines are normal map-specific
var T: vec3<f32> = normalize(in.world_tangent.xyz);
var B: vec3<f32> = cross(N, T) * in.world_tangent.w;
if ((material.flags & STANDARD_MATERIAL_FLAGS_NORMAL_MAP_TEXTURE_BIT) != 0u) {
var T: vec3<f32> = normalize(in.world_tangent.xyz);
var B: vec3<f32> = cross(N, T) * in.world_tangent.w;

if ((material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u) {
if ((material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u) {
if (!in.is_front) {
N = -N;
T = -T;
B = -B;
}
}

let TBN = mat3x3<f32>(T, B, N);
N = TBN * normalize(textureSampleLevel(
normal_map_texture,
normal_map_sampler,
in.uv,
0.0
).rgb * 2.0 - 1.0);
} elseif ((material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u) {
if (!in.is_front) {
N = -N;
// NOTE: The next two lines are normal map-specific
T = -T;
B = -B;
}
}

// NOTE: The next two lines are normal map-specific
let TBN = mat3x3<f32>(T, B, N);
N = TBN * normalize(textureSampleLevel(
normal_map_texture,
normal_map_sampler,
in.uv,
0.0
).rgb * 2.0 - 1.0);

let V = calculate_view_vector(in.world_position.xyz);

output_color = shade_fragment(
Expand Down

0 comments on commit 08c92e5

Please sign in to comment.