From 6e7e21cd274a556e7869061ca5444f2b0f62f7de Mon Sep 17 00:00:00 2001 From: Robin KAY Date: Fri, 8 Jul 2022 20:55:08 +0000 Subject: [PATCH] Allow rendering meshes without UV coordinate data. (#5222) # Objective Bevy requires meshes to include UV coordinates, even if the material does not use any textures, and will fail with an error `ERROR bevy_pbr::material: Mesh is missing requested attribute: Vertex_Uv (MeshVertexAttributeId(2), pipeline type: Some("bevy_pbr::material::MaterialPipeline"))` otherwise. The objective of this PR is to permit this. ## Solution This PR follows the design of #4528, which added support for per-vertex colours. It adds a shader define called VERTEX_UVS which indicates the presence of UV coordinates to the shader. --- crates/bevy_gltf/src/loader.rs | 5 ----- crates/bevy_pbr/src/render/mesh.rs | 6 +++++- crates/bevy_pbr/src/render/mesh.wgsl | 8 ++++++++ crates/bevy_pbr/src/render/pbr.wgsl | 12 ++++++++++++ crates/bevy_pbr/src/render/pbr_functions.wgsl | 4 ++++ 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index f110782f8b82f..b40b6e9ca2dad 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -260,11 +260,6 @@ async fn load_gltf<'a, 'b>( .map(|v| VertexAttributeValues::Float32x2(v.into_f32().collect())) { mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute); - } else { - let len = mesh.count_vertices(); - let uvs = vec![[0.0, 0.0]; len]; - bevy_log::debug!("missing `TEXCOORD_0` vertex attribute, loading zeroed out UVs"); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); } if let Some(vertex_attribute) = reader diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index db3aeee86190e..348c7c48eb88e 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -540,10 +540,14 @@ impl SpecializedMeshPipeline for MeshPipeline { let mut vertex_attributes = vec![ Mesh::ATTRIBUTE_POSITION.at_shader_location(0), Mesh::ATTRIBUTE_NORMAL.at_shader_location(1), - Mesh::ATTRIBUTE_UV_0.at_shader_location(2), ]; let mut shader_defs = Vec::new(); + if layout.contains(Mesh::ATTRIBUTE_UV_0) { + shader_defs.push(String::from("VERTEX_UVS")); + vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2)); + } + if layout.contains(Mesh::ATTRIBUTE_TANGENT) { shader_defs.push(String::from("VERTEX_TANGENTS")); vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3)); diff --git a/crates/bevy_pbr/src/render/mesh.wgsl b/crates/bevy_pbr/src/render/mesh.wgsl index 7fdb17573b012..4d6de7c5bfdc4 100644 --- a/crates/bevy_pbr/src/render/mesh.wgsl +++ b/crates/bevy_pbr/src/render/mesh.wgsl @@ -7,7 +7,9 @@ struct Vertex { [[location(0)]] position: vec3; [[location(1)]] normal: vec3; +#ifdef VERTEX_UVS [[location(2)]] uv: vec2; +#endif #ifdef VERTEX_TANGENTS [[location(3)]] tangent: vec4; #endif @@ -24,7 +26,9 @@ struct VertexOutput { [[builtin(position)]] clip_position: vec4; [[location(0)]] world_position: vec4; [[location(1)]] world_normal: vec3; +#ifdef VERTEX_UVS [[location(2)]] uv: vec2; +#endif #ifdef VERTEX_TANGENTS [[location(3)]] world_tangent: vec4; #endif @@ -44,7 +48,9 @@ fn vertex(vertex: Vertex) -> VertexOutput { out.world_normal = mesh_normal_local_to_world(vertex.normal); #endif out.world_position = mesh_position_local_to_world(model, vec4(vertex.position, 1.0)); +#ifdef VERTEX_UVS out.uv = vertex.uv; +#endif #ifdef VERTEX_TANGENTS out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent); #endif @@ -60,7 +66,9 @@ struct FragmentInput { [[builtin(front_facing)]] is_front: bool; [[location(0)]] world_position: vec4; [[location(1)]] world_normal: vec3; +#ifdef VERTEX_UVS [[location(2)]] uv: vec2; +#endif #ifdef VERTEX_TANGENTS [[location(3)]] world_tangent: vec4; #endif diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 9f19d15f57b29..f632974a464a8 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -13,7 +13,9 @@ struct FragmentInput { [[builtin(position)]] frag_coord: vec4; [[location(0)]] world_position: vec4; [[location(1)]] world_normal: vec3; +#ifdef VERTEX_UVS [[location(2)]] uv: vec2; +#endif #ifdef VERTEX_TANGENTS [[location(3)]] world_tangent: vec4; #endif @@ -28,9 +30,11 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { #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, in.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) { @@ -45,26 +49,32 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { // TODO use .a for exposure compensation in HDR var emissive: vec4 = material.emissive; +#ifdef VERTEX_UVS if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { emissive = vec4(emissive.rgb * textureSample(emissive_texture, emissive_sampler, in.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, in.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, in.uv).r; } +#endif pbr_input.occlusion = occlusion; pbr_input.frag_coord = in.frag_coord; @@ -81,7 +91,9 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { in.world_tangent, #endif #endif +#ifdef VERTEX_UVS in.uv, +#endif in.is_front, ); pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index a5b52a25865fe..35752353daae0 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -10,7 +10,9 @@ fn prepare_normal( world_tangent: vec4, #endif #endif +#ifdef VERTEX_UVS uv: vec2, +#endif is_front: bool, ) -> vec3 { var N: vec3 = normalize(world_normal); @@ -39,6 +41,7 @@ fn prepare_normal( } #ifdef VERTEX_TANGENTS +#ifdef VERTEX_UVS #ifdef STANDARDMATERIAL_NORMAL_MAP // Nt is the tangent-space normal. var Nt = textureSample(normal_map_texture, normal_map_sampler, uv).rgb; @@ -60,6 +63,7 @@ fn prepare_normal( // http://www.mikktspace.com/ N = normalize(Nt.x * T + Nt.y * B + Nt.z * N); #endif +#endif #endif return N;