Skip to content

Commit

Permalink
Allow rendering meshes without UV coordinate data. (bevyengine#5222)
Browse files Browse the repository at this point in the history
# 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<bevy_pbr::pbr_material::StandardMaterial>"))` otherwise. The objective of this PR is to permit this.

## Solution

This PR follows the design of bevyengine#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.
  • Loading branch information
komadori authored and inodentry committed Aug 8, 2022
1 parent 079e408 commit 7836552
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 6 deletions.
5 changes: 0 additions & 5 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_pbr/src/render/mesh.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
struct Vertex {
[[location(0)]] position: vec3<f32>;
[[location(1)]] normal: vec3<f32>;
#ifdef VERTEX_UVS
[[location(2)]] uv: vec2<f32>;
#endif
#ifdef VERTEX_TANGENTS
[[location(3)]] tangent: vec4<f32>;
#endif
Expand All @@ -24,7 +26,9 @@ struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] world_position: vec4<f32>;
[[location(1)]] world_normal: vec3<f32>;
#ifdef VERTEX_UVS
[[location(2)]] uv: vec2<f32>;
#endif
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
Expand All @@ -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<f32>(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
Expand All @@ -60,7 +66,9 @@ struct FragmentInput {
[[builtin(front_facing)]] is_front: bool;
[[location(0)]] world_position: vec4<f32>;
[[location(1)]] world_normal: vec3<f32>;
#ifdef VERTEX_UVS
[[location(2)]] uv: vec2<f32>;
#endif
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
Expand Down
12 changes: 12 additions & 0 deletions crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ struct FragmentInput {
[[builtin(position)]] frag_coord: vec4<f32>;
[[location(0)]] world_position: vec4<f32>;
[[location(1)]] world_normal: vec3<f32>;
#ifdef VERTEX_UVS
[[location(2)]] uv: vec2<f32>;
#endif
#ifdef VERTEX_TANGENTS
[[location(3)]] world_tangent: vec4<f32>;
#endif
Expand All @@ -28,9 +30,11 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
#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) {
Expand All @@ -45,26 +49,32 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {

// TODO use .a for exposure compensation in HDR
var emissive: vec4<f32> = material.emissive;
#ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
emissive = vec4<f32>(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;
Expand All @@ -81,7 +91,9 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
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);
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ fn prepare_normal(
world_tangent: vec4<f32>,
#endif
#endif
#ifdef VERTEX_UVS
uv: vec2<f32>,
#endif
is_front: bool,
) -> vec3<f32> {
var N: vec3<f32> = normalize(world_normal);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit 7836552

Please sign in to comment.