From 8596c4c54e8570e5e24f969277c2288536ddecea Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Fri, 16 Feb 2024 17:53:15 +0100 Subject: [PATCH 01/10] Add support for KHR_texture_transform Co-authored-by: Al McElrath --- crates/bevy_gltf/Cargo.toml | 1 + crates/bevy_gltf/src/loader.rs | 34 +++++++++++++++---- crates/bevy_pbr/src/pbr_material.rs | 9 ++++- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 2 +- .../src/render/pbr_prepass_functions.wgsl | 3 +- crates/bevy_pbr/src/render/pbr_types.wgsl | 2 ++ 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/crates/bevy_gltf/Cargo.toml b/crates/bevy_gltf/Cargo.toml index 78306fd6db3a9..322ec50447749 100644 --- a/crates/bevy_gltf/Cargo.toml +++ b/crates/bevy_gltf/Cargo.toml @@ -43,6 +43,7 @@ gltf = { version = "1.4.0", default-features = false, features = [ "KHR_materials_volume", "KHR_materials_unlit", "KHR_materials_emissive_strength", + "KHR_texture_transform", "extras", "extensions", "names", diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index ff1aced2f680b..072076030c42e 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -8,7 +8,7 @@ use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{entity::Entity, world::World}; use bevy_hierarchy::{BuildWorldChildren, WorldChildBuilder}; use bevy_log::{error, warn}; -use bevy_math::{Mat4, Vec3}; +use bevy_math::{Mat3, Mat4, Vec3}; use bevy_pbr::{ AlphaMode, DirectionalLight, DirectionalLightBundle, PbrBundle, PointLight, PointLightBundle, SpotLight, SpotLightBundle, StandardMaterial, MAX_JOINTS, @@ -38,7 +38,7 @@ use bevy_utils::{HashMap, HashSet}; use gltf::{ accessor::Iter, mesh::{util::ReadIndices, Mode}, - texture::{MagFilter, MinFilter, WrappingMode}, + texture::{MagFilter, MinFilter, TextureTransform, WrappingMode}, Material, Node, Primitive, Semantic, }; use serde::{Deserialize, Serialize}; @@ -791,6 +791,15 @@ async fn load_image<'a, 'b>( } } +/// Converts [`TextureTransform`] to [`Mat3`]. +fn texture_transform_mat3(texture_transform: TextureTransform) -> Mat3 { + Mat3::from_scale_angle_translation( + texture_transform.scale().into(), + -texture_transform.rotation(), + texture_transform.offset().into(), + ) +} + /// Loads a glTF material as a bevy [`StandardMaterial`] and returns it. fn load_material( material: &Material, @@ -803,10 +812,16 @@ fn load_material( // TODO: handle missing label handle errors here? let color = pbr.base_color_factor(); - let base_color_texture = pbr.base_color_texture().map(|info| { - // TODO: handle info.tex_coord() (the *set* index for the right texcoords) - texture_handle(load_context, &info.texture()) - }); + let (base_color_texture, uv_transform) = pbr + .base_color_texture() + .map(|info| { + // TODO: handle info.tex_coord() (the *set* index for the right texcoords) + let texture = texture_handle(load_context, &info.texture()); + let uv_transform = info.texture_transform().map(texture_transform_mat3); + (texture, uv_transform) + }) + .unzip(); + let uv_transform = uv_transform.flatten().unwrap_or_default(); let normal_map_texture: Option> = material.normal_texture().map(|normal_texture| { @@ -817,6 +832,9 @@ fn load_material( let metallic_roughness_texture = pbr.metallic_roughness_texture().map(|info| { // TODO: handle info.tex_coord() (the *set* index for the right texcoords) + if info.texture_transform().map(texture_transform_mat3) != Some(uv_transform) { + warn!("Only one texture transform is supported"); + } texture_handle(load_context, &info.texture()) }); @@ -830,6 +848,9 @@ fn load_material( let emissive_texture = material.emissive_texture().map(|info| { // TODO: handle occlusion_texture.tex_coord() (the *set* index for the right texcoords) // TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength) + if info.texture_transform().map(texture_transform_mat3) != Some(uv_transform) { + warn!("Only one texture transform is supported"); + } texture_handle(load_context, &info.texture()) }); @@ -917,6 +938,7 @@ fn load_material( ), unlit: material.unlit(), alpha_mode: alpha_mode(material), + uv_transform, ..Default::default() } }) diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 25cb24fc14447..574468cc00053 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -1,5 +1,5 @@ use bevy_asset::{Asset, Handle}; -use bevy_math::Vec4; +use bevy_math::{Mat3, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ color::Color, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_resource::*, @@ -472,6 +472,9 @@ pub struct StandardMaterial { /// Default is [`DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID`] for default /// PBR deferred lighting pass. Ignored in the case of forward materials. pub deferred_lighting_pass_id: u8, + + /// Texture UV transform. + pub uv_transform: Mat3, } impl Default for StandardMaterial { @@ -520,6 +523,7 @@ impl Default for StandardMaterial { parallax_mapping_method: ParallaxMappingMethod::Occlusion, opaque_render_method: OpaqueRendererMethod::Auto, deferred_lighting_pass_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID, + uv_transform: Mat3::IDENTITY, } } } @@ -632,6 +636,8 @@ pub struct StandardMaterialUniform { pub max_relief_mapping_search_steps: u32, /// ID for specifying which deferred lighting pass should be used for rendering this material, if any. pub deferred_lighting_pass_id: u32, + /// Texture UV transform. + pub uv_transform: Mat3, } impl AsBindGroupShaderType for StandardMaterial { @@ -729,6 +735,7 @@ impl AsBindGroupShaderType for StandardMaterial { lightmap_exposure: self.lightmap_exposure, max_relief_mapping_search_steps: self.parallax_mapping_method.max_steps(), deferred_lighting_pass_id: self.deferred_lighting_pass_id as u32, + uv_transform: self.uv_transform, } } } diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index be759d38b573e..1b43d4a912cba 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -73,7 +73,7 @@ fn pbr_input_from_standard_material( let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); #ifdef VERTEX_UVS - var uv = in.uv; + var uv = (pbr_bindings::material.uv_transform * vec3(in.uv, 1.0)).xy; #ifdef VERTEX_TANGENTS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { diff --git a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl index 176c56aa1aba7..ee1bb45f3ebc0 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl @@ -18,8 +18,9 @@ fn prepass_alpha_discard(in: VertexOutput) { var output_color: vec4 = pbr_bindings::material.base_color; #ifdef VERTEX_UVS + let uv = (pbr_bindings::material.uv_transform * vec3(in.uv, 1.0)).xy; if (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u { - output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, in.uv, view.mip_bias); + output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); } #endif // VERTEX_UVS diff --git a/crates/bevy_pbr/src/render/pbr_types.wgsl b/crates/bevy_pbr/src/render/pbr_types.wgsl index 72a70e45c840c..b90631380cefc 100644 --- a/crates/bevy_pbr/src/render/pbr_types.wgsl +++ b/crates/bevy_pbr/src/render/pbr_types.wgsl @@ -21,6 +21,7 @@ struct StandardMaterial { max_relief_mapping_search_steps: u32, /// ID for specifying which deferred lighting pass should be used for rendering this material, if any. deferred_lighting_pass_id: u32, + uv_transform: mat3x3, }; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -74,6 +75,7 @@ fn standard_material_new() -> StandardMaterial { material.max_parallax_layer_count = 16.0; material.max_relief_mapping_search_steps = 5u; material.deferred_lighting_pass_id = 1u; + material.uv_transform = mat3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0); return material; } From edbc37b6e9c84a2103ded146107163e6d7187f73 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Fri, 16 Feb 2024 18:36:33 +0100 Subject: [PATCH 02/10] Refactor uv transform retrieval --- crates/bevy_gltf/src/loader.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 072076030c42e..f3845a026e6be 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -812,16 +812,16 @@ fn load_material( // TODO: handle missing label handle errors here? let color = pbr.base_color_factor(); - let (base_color_texture, uv_transform) = pbr + let base_color_texture = pbr.base_color_texture().map(|info| { + // TODO: handle info.tex_coord() (the *set* index for the right texcoords) + texture_handle(load_context, &info.texture()) + }); + + let uv_transform = pbr .base_color_texture() - .map(|info| { - // TODO: handle info.tex_coord() (the *set* index for the right texcoords) - let texture = texture_handle(load_context, &info.texture()); - let uv_transform = info.texture_transform().map(texture_transform_mat3); - (texture, uv_transform) - }) - .unzip(); - let uv_transform = uv_transform.flatten().unwrap_or_default(); + .map(|info| info.texture_transform().map(texture_transform_mat3)) + .flatten() + .unwrap_or_default(); let normal_map_texture: Option> = material.normal_texture().map(|normal_texture| { From b9deafdd9e7b99b3dc7ec9f956f822edc0a4777c Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Fri, 16 Feb 2024 18:46:13 +0100 Subject: [PATCH 03/10] Improve error messages --- crates/bevy_gltf/src/loader.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index f3845a026e6be..c410cc3e4b648 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -833,7 +833,7 @@ fn load_material( let metallic_roughness_texture = pbr.metallic_roughness_texture().map(|info| { // TODO: handle info.tex_coord() (the *set* index for the right texcoords) if info.texture_transform().map(texture_transform_mat3) != Some(uv_transform) { - warn!("Only one texture transform is supported"); + warn!("Only the texture transform on the base color texture is supported, ignoring the texture transform on the metallic/roughness texture"); } texture_handle(load_context, &info.texture()) }); @@ -849,7 +849,7 @@ fn load_material( // TODO: handle occlusion_texture.tex_coord() (the *set* index for the right texcoords) // TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength) if info.texture_transform().map(texture_transform_mat3) != Some(uv_transform) { - warn!("Only one texture transform is supported"); + warn!("Only the texture transform on the base color texture is supported, ignoring the texture transform on the emissive texture"); } texture_handle(load_context, &info.texture()) }); From 51642f5b04843c32937ad7dad865bd29fe15bba2 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Fri, 16 Feb 2024 18:50:43 +0100 Subject: [PATCH 04/10] Fix lint --- crates/bevy_gltf/src/loader.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index c410cc3e4b648..c67eca2151137 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -819,8 +819,7 @@ fn load_material( let uv_transform = pbr .base_color_texture() - .map(|info| info.texture_transform().map(texture_transform_mat3)) - .flatten() + .and_then(|info| info.texture_transform().map(texture_transform_mat3)) .unwrap_or_default(); let normal_map_texture: Option> = From 7298cdbe62891229c8e99edff358a51757ccc941 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Sat, 17 Feb 2024 15:03:05 +0100 Subject: [PATCH 05/10] Use Affine2 instead of Mat3 and improve error messages --- crates/bevy_gltf/src/loader.rs | 64 +++++++++++++++++++++-------- crates/bevy_pbr/src/pbr_material.rs | 12 +++--- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index c67eca2151137..e0d32f8c0c39f 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -8,7 +8,7 @@ use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{entity::Entity, world::World}; use bevy_hierarchy::{BuildWorldChildren, WorldChildBuilder}; use bevy_log::{error, warn}; -use bevy_math::{Mat3, Mat4, Vec3}; +use bevy_math::{Affine2, Mat4, Vec3}; use bevy_pbr::{ AlphaMode, DirectionalLight, DirectionalLightBundle, PbrBundle, PointLight, PointLightBundle, SpotLight, SpotLightBundle, StandardMaterial, MAX_JOINTS, @@ -35,6 +35,7 @@ use bevy_scene::Scene; use bevy_tasks::IoTaskPool; use bevy_transform::components::Transform; use bevy_utils::{HashMap, HashSet}; +use gltf::texture::Info; use gltf::{ accessor::Iter, mesh::{util::ReadIndices, Mode}, @@ -791,15 +792,6 @@ async fn load_image<'a, 'b>( } } -/// Converts [`TextureTransform`] to [`Mat3`]. -fn texture_transform_mat3(texture_transform: TextureTransform) -> Mat3 { - Mat3::from_scale_angle_translation( - texture_transform.scale().into(), - -texture_transform.rotation(), - texture_transform.offset().into(), - ) -} - /// Loads a glTF material as a bevy [`StandardMaterial`] and returns it. fn load_material( material: &Material, @@ -819,7 +811,10 @@ fn load_material( let uv_transform = pbr .base_color_texture() - .and_then(|info| info.texture_transform().map(texture_transform_mat3)) + .and_then(|info| { + info.texture_transform() + .map(convert_texture_transform_to_affine2) + }) .unwrap_or_default(); let normal_map_texture: Option> = @@ -831,9 +826,12 @@ fn load_material( let metallic_roughness_texture = pbr.metallic_roughness_texture().map(|info| { // TODO: handle info.tex_coord() (the *set* index for the right texcoords) - if info.texture_transform().map(texture_transform_mat3) != Some(uv_transform) { - warn!("Only the texture transform on the base color texture is supported, ignoring the texture transform on the metallic/roughness texture"); - } + warn_on_differing_texture_transforms( + material, + &info, + uv_transform, + "metallic/roughness", + ); texture_handle(load_context, &info.texture()) }); @@ -847,9 +845,7 @@ fn load_material( let emissive_texture = material.emissive_texture().map(|info| { // TODO: handle occlusion_texture.tex_coord() (the *set* index for the right texcoords) // TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength) - if info.texture_transform().map(texture_transform_mat3) != Some(uv_transform) { - warn!("Only the texture transform on the base color texture is supported, ignoring the texture transform on the emissive texture"); - } + warn_on_differing_texture_transforms(material, &info, uv_transform, "emissive"); texture_handle(load_context, &info.texture()) }); @@ -943,6 +939,40 @@ fn load_material( }) } +fn convert_texture_transform_to_affine2(texture_transform: TextureTransform) -> Affine2 { + Affine2::from_scale_angle_translation( + texture_transform.scale().into(), + -texture_transform.rotation(), + texture_transform.offset().into(), + ) +} + +fn warn_on_differing_texture_transforms( + material: &Material, + info: &Info, + texture_transform: Affine2, + texture_kind: &str, +) { + let has_same_texture_transform = info + .texture_transform() + .map(convert_texture_transform_to_affine2) + == Some(texture_transform); + if !has_same_texture_transform { + let material_name = material + .name() + .map(|n| format!("the material \"{n}\"")) + .unwrap_or_else(|| "an unnamed material".to_string()); + let texture_name = info + .texture() + .name() + .map(|n| format!("its {texture_kind} texture \"{n}\"")) + .unwrap_or_else(|| format!("its unnamed {texture_kind} texture")); + warn!( + "Only texture transforms on base color textures are supported, but {material_name} has a texture transform on {texture_name}, which will be ignored.", + ); + } +} + /// Loads a glTF node. #[allow(clippy::too_many_arguments, clippy::result_large_err)] fn load_node( diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 574468cc00053..5534ab3aef7d6 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -1,5 +1,5 @@ use bevy_asset::{Asset, Handle}; -use bevy_math::{Mat3, Vec4}; +use bevy_math::{Affine2, Mat3, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ color::Color, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_resource::*, @@ -473,8 +473,8 @@ pub struct StandardMaterial { /// PBR deferred lighting pass. Ignored in the case of forward materials. pub deferred_lighting_pass_id: u8, - /// Texture UV transform. - pub uv_transform: Mat3, + /// The transform applied to the UVs of the material before sampling. Default is identity. + pub uv_transform: Affine2, } impl Default for StandardMaterial { @@ -523,7 +523,7 @@ impl Default for StandardMaterial { parallax_mapping_method: ParallaxMappingMethod::Occlusion, opaque_render_method: OpaqueRendererMethod::Auto, deferred_lighting_pass_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID, - uv_transform: Mat3::IDENTITY, + uv_transform: Affine2::IDENTITY, } } } @@ -636,7 +636,7 @@ pub struct StandardMaterialUniform { pub max_relief_mapping_search_steps: u32, /// ID for specifying which deferred lighting pass should be used for rendering this material, if any. pub deferred_lighting_pass_id: u32, - /// Texture UV transform. + /// The transform applied to the UVs of the material before sampling. Default is identity. pub uv_transform: Mat3, } @@ -735,7 +735,7 @@ impl AsBindGroupShaderType for StandardMaterial { lightmap_exposure: self.lightmap_exposure, max_relief_mapping_search_steps: self.parallax_mapping_method.max_steps(), deferred_lighting_pass_id: self.deferred_lighting_pass_id as u32, - uv_transform: self.uv_transform, + uv_transform: self.uv_transform.into(), } } } From 598452a55400c241a9e88c15e4e04dbce65c2516 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Sat, 17 Feb 2024 15:54:48 +0100 Subject: [PATCH 06/10] Allow having no texture transform on non base color textures --- crates/bevy_gltf/src/loader.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index e0d32f8c0c39f..74d15b7f9e125 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -953,11 +953,11 @@ fn warn_on_differing_texture_transforms( texture_transform: Affine2, texture_kind: &str, ) { - let has_same_texture_transform = info + let has_differing_texture_transform = info .texture_transform() .map(convert_texture_transform_to_affine2) - == Some(texture_transform); - if !has_same_texture_transform { + .is_some_and(|t| t != texture_transform); + if has_differing_texture_transform { let material_name = material .name() .map(|n| format!("the material \"{n}\"")) From 47567f6faea1bf631ca0ed52134239b916746aca Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Sun, 18 Feb 2024 00:05:13 +0100 Subject: [PATCH 07/10] Report index on texture transform warnings Co-authored-by: Kanabenki --- crates/bevy_gltf/src/loader.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 74d15b7f9e125..4b552591568c4 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -967,8 +967,13 @@ fn warn_on_differing_texture_transforms( .name() .map(|n| format!("its {texture_kind} texture \"{n}\"")) .unwrap_or_else(|| format!("its unnamed {texture_kind} texture")); + let material_index = material + .index() + .map(|i| format!("index {i}")) + .unwrap_or_else(|| "default".to_string()); warn!( - "Only texture transforms on base color textures are supported, but {material_name} has a texture transform on {texture_name}, which will be ignored.", + "Only texture transforms on base color textures are supported, but {material_name} ({material_index}) \ + has a texture transform on {texture_name} (index {}), which will be ignored.", info.texture().index() ); } } From ed54b875ea601a94e2cb47f7a99096413f504abb Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Sun, 18 Feb 2024 00:06:58 +0100 Subject: [PATCH 08/10] Improve docs --- crates/bevy_pbr/src/pbr_material.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 5534ab3aef7d6..0387feb942b2a 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -473,7 +473,7 @@ pub struct StandardMaterial { /// PBR deferred lighting pass. Ignored in the case of forward materials. pub deferred_lighting_pass_id: u8, - /// The transform applied to the UVs of the material before sampling. Default is identity. + /// The transform applied to the UVs corresponding to ATTRIBUTE_UV_0 on the mesh before sampling. Default is identity. pub uv_transform: Affine2, } @@ -636,7 +636,7 @@ pub struct StandardMaterialUniform { pub max_relief_mapping_search_steps: u32, /// ID for specifying which deferred lighting pass should be used for rendering this material, if any. pub deferred_lighting_pass_id: u32, - /// The transform applied to the UVs of the material before sampling. Default is identity. + /// The transform applied to the UVs corresponding to ATTRIBUTE_UV_0 on the mesh before sampling. Default is identity. pub uv_transform: Mat3, } From 2d40a37776b7636750eb3803f316252fc1eeb109 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 20 Feb 2024 17:25:22 +0100 Subject: [PATCH 09/10] Optimize packing of standard material --- crates/bevy_pbr/src/pbr_material.rs | 20 ++++++--- .../bevy_pbr/src/render/mesh_functions.wgsl | 6 +-- crates/bevy_pbr/src/render/mesh_types.wgsl | 2 +- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 4 +- .../src/render/pbr_prepass_functions.wgsl | 4 +- crates/bevy_pbr/src/render/pbr_types.wgsl | 43 ++++++++++--------- crates/bevy_render/src/maths.wgsl | 10 ++++- .../src/mesh2d/mesh2d_functions.wgsl | 4 +- .../bevy_sprite/src/mesh2d/mesh2d_types.wgsl | 2 +- crates/bevy_sprite/src/render/sprite.wgsl | 4 +- 10 files changed, 60 insertions(+), 39 deletions(-) diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 0387feb942b2a..3c017fbc4be79 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -1,5 +1,5 @@ use bevy_asset::{Asset, Handle}; -use bevy_math::{Affine2, Mat3, Vec4}; +use bevy_math::{Affine2, Vec2, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ color::Color, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_resource::*, @@ -594,9 +594,17 @@ pub struct StandardMaterialUniform { /// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything /// in between. pub base_color: Vec4, - // Use a color for user friendliness even though we technically don't use the alpha channel + // Use a color for user-friendliness even though we technically don't use the alpha channel // Might be used in the future for exposure correction in HDR pub emissive: Vec4, + /// Color white light takes after travelling through the attenuation distance underneath the material surface + pub attenuation_color: Vec4, + /// The x-axis of the mat2 of the transform applied to the UVs corresponding to ATTRIBUTE_UV_0 on the mesh before sampling. Default is [1, 0]. + pub uv_transform_x_axis: Vec2, + /// The y-axis of the mat2 of the transform applied to the UVs corresponding to ATTRIBUTE_UV_0 on the mesh before sampling. Default is [0, 1]. + pub uv_transform_y_axis: Vec2, + /// The translation of the transform applied to the UVs corresponding to ATTRIBUTE_UV_0 on the mesh before sampling. Default is [0, 0]. + pub uv_transform_translation: Vec2, /// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader /// Defaults to minimum of 0.089 pub roughness: f32, @@ -615,8 +623,6 @@ pub struct StandardMaterialUniform { pub ior: f32, /// How far light travels through the volume underneath the material surface before being absorbed pub attenuation_distance: f32, - /// Color white light takes after travelling through the attenuation distance underneath the material surface - pub attenuation_color: Vec4, /// The [`StandardMaterialFlags`] accessible in the `wgsl` shader. pub flags: u32, /// When the alpha mode mask flag is set, any base color alpha above this cutoff means fully opaque, @@ -636,8 +642,6 @@ pub struct StandardMaterialUniform { pub max_relief_mapping_search_steps: u32, /// ID for specifying which deferred lighting pass should be used for rendering this material, if any. pub deferred_lighting_pass_id: u32, - /// The transform applied to the UVs corresponding to ATTRIBUTE_UV_0 on the mesh before sampling. Default is identity. - pub uv_transform: Mat3, } impl AsBindGroupShaderType for StandardMaterial { @@ -735,7 +739,9 @@ impl AsBindGroupShaderType for StandardMaterial { lightmap_exposure: self.lightmap_exposure, max_relief_mapping_search_steps: self.parallax_mapping_method.max_steps(), deferred_lighting_pass_id: self.deferred_lighting_pass_id as u32, - uv_transform: self.uv_transform.into(), + uv_transform_x_axis: self.uv_transform.matrix2.x_axis, + uv_transform_y_axis: self.uv_transform.matrix2.y_axis, + uv_transform_translation: self.uv_transform.translation, } } } diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index 170c2f916ae26..e94b4f763306d 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -6,15 +6,15 @@ mesh_types::MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT, view_transformations::position_world_to_clip, } -#import bevy_render::maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack} +#import bevy_render::maths::{affine3_to_square, mat2x4_f32_to_mat3x3_unpack} fn get_model_matrix(instance_index: u32) -> mat4x4 { - return affine_to_square(mesh[instance_index].model); + return affine3_to_square(mesh[instance_index].model); } fn get_previous_model_matrix(instance_index: u32) -> mat4x4 { - return affine_to_square(mesh[instance_index].previous_model); + return affine3_to_square(mesh[instance_index].previous_model); } fn mesh_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { diff --git a/crates/bevy_pbr/src/render/mesh_types.wgsl b/crates/bevy_pbr/src/render/mesh_types.wgsl index 89b73be2bd6f1..6580941c96acd 100644 --- a/crates/bevy_pbr/src/render/mesh_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_types.wgsl @@ -2,7 +2,7 @@ struct Mesh { // Affine 4x3 matrices transposed to 3x4 - // Use bevy_render::maths::affine_to_square to unpack + // Use bevy_render::maths::affine3_to_square to unpack model: mat3x4, previous_model: mat3x4, lightmap_uv_rect: vec2, diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index 1b43d4a912cba..b484e9691a879 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -11,6 +11,7 @@ parallax_mapping::parallaxed_uv, lightmap::lightmap, } +#import bevy_render::maths::affine2_to_square #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION #import bevy_pbr::mesh_view_bindings::screen_space_ambient_occlusion_texture @@ -73,7 +74,8 @@ fn pbr_input_from_standard_material( let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); #ifdef VERTEX_UVS - var uv = (pbr_bindings::material.uv_transform * vec3(in.uv, 1.0)).xy; + let uv_transform = affine2_to_square(pbr_bindings::material.uv_transform); + var uv = (uv_transform * vec3(in.uv, 1.0)).xy; #ifdef VERTEX_TANGENTS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { diff --git a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl index ee1bb45f3ebc0..7a8dbb8a19d7e 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass_functions.wgsl @@ -7,6 +7,7 @@ pbr_bindings, pbr_types, } +#import bevy_render::maths::affine2_to_square // Cutoff used for the premultiplied alpha modes BLEND and ADD. const PREMULTIPLIED_ALPHA_CUTOFF = 0.05; @@ -18,7 +19,8 @@ fn prepass_alpha_discard(in: VertexOutput) { var output_color: vec4 = pbr_bindings::material.base_color; #ifdef VERTEX_UVS - let uv = (pbr_bindings::material.uv_transform * vec3(in.uv, 1.0)).xy; + let uv_transform = affine2_to_square(pbr_bindings::material.uv_transform); + let uv = (uv_transform * vec3(in.uv, 1.0)).xy; if (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u { output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); } diff --git a/crates/bevy_pbr/src/render/pbr_types.wgsl b/crates/bevy_pbr/src/render/pbr_types.wgsl index b90631380cefc..f3fc79a30c835 100644 --- a/crates/bevy_pbr/src/render/pbr_types.wgsl +++ b/crates/bevy_pbr/src/render/pbr_types.wgsl @@ -1,27 +1,29 @@ #define_import_path bevy_pbr::pbr_types +// Alignments and sizes taken from . +// Since this is a hot path, try to keep the alignment and size of the struct members in mind. struct StandardMaterial { - base_color: vec4, - emissive: vec4, - perceptual_roughness: f32, - metallic: f32, - reflectance: f32, - diffuse_transmission: f32, - specular_transmission: f32, - thickness: f32, - ior: f32, - attenuation_distance: f32, - attenuation_color: vec4, + base_color: vec4, // alignment 16, size 16 + emissive: vec4, // alignment 16, size 16 + attenuation_color: vec4, // alignment 16, size 16 + uv_transform: mat3x2, // alignment 8, size 24 + perceptual_roughness: f32, // alignment 4, size 4 + metallic: f32, // alignment 4, size 4 + reflectance: f32, // alignment 4, size 4 + diffuse_transmission: f32, // alignment 4, size 4 + specular_transmission: f32, // alignment 4, size 4 + thickness: f32, // alignment 4, size 4 + ior: f32, // alignment 4, size 4 + attenuation_distance: f32, // alignment 4, size 4 // 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options. - flags: u32, - alpha_cutoff: f32, - parallax_depth_scale: f32, - max_parallax_layer_count: f32, - lightmap_exposure: f32, - max_relief_mapping_search_steps: u32, + flags: u32, // alignment 4, size 4 + alpha_cutoff: f32, // alignment 4, size 4 + parallax_depth_scale: f32, // alignment 4, size 4 + max_parallax_layer_count: f32, // alignment 4, size 4 + lightmap_exposure: f32, // alignment 4, size 4 + max_relief_mapping_search_steps: u32, // alignment 4, size 4 /// ID for specifying which deferred lighting pass should be used for rendering this material, if any. - deferred_lighting_pass_id: u32, - uv_transform: mat3x3, + deferred_lighting_pass_id: u32, // alignment 4, size 4 }; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -75,7 +77,8 @@ fn standard_material_new() -> StandardMaterial { material.max_parallax_layer_count = 16.0; material.max_relief_mapping_search_steps = 5u; material.deferred_lighting_pass_id = 1u; - material.uv_transform = mat3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0); + // scale 1, translation 0, rotation 0 + material.uv_transform = mat3x2(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); return material; } diff --git a/crates/bevy_render/src/maths.wgsl b/crates/bevy_render/src/maths.wgsl index af79c88c9a150..17d045154a317 100644 --- a/crates/bevy_render/src/maths.wgsl +++ b/crates/bevy_render/src/maths.wgsl @@ -1,6 +1,14 @@ #define_import_path bevy_render::maths -fn affine_to_square(affine: mat3x4) -> mat4x4 { +fn affine2_to_square(affine: mat3x2) -> mat3x3 { + return mat3x3( + vec3(affine[0].xy, 0.0), + vec3(affine[1].xy, 0.0), + vec3(affine[2].xy, 1.0), + ); +} + +fn affine3_to_square(affine: mat3x4) -> mat4x4 { return transpose(mat4x4( affine[0], affine[1], diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl index b66b34e88896d..d76a88ab03832 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_functions.wgsl @@ -4,10 +4,10 @@ mesh2d_view_bindings::view, mesh2d_bindings::mesh, } -#import bevy_render::maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack} +#import bevy_render::maths::{affine3_to_square, mat2x4_f32_to_mat3x3_unpack} fn get_model_matrix(instance_index: u32) -> mat4x4 { - return affine_to_square(mesh[instance_index].model); + return affine3_to_square(mesh[instance_index].model); } fn mesh2d_position_local_to_world(model: mat4x4, vertex_position: vec4) -> vec4 { diff --git a/crates/bevy_sprite/src/mesh2d/mesh2d_types.wgsl b/crates/bevy_sprite/src/mesh2d/mesh2d_types.wgsl index f855707790001..4b14b919f3a26 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh2d_types.wgsl +++ b/crates/bevy_sprite/src/mesh2d/mesh2d_types.wgsl @@ -2,7 +2,7 @@ struct Mesh2d { // Affine 4x3 matrix transposed to 3x4 - // Use bevy_render::maths::affine_to_square to unpack + // Use bevy_render::maths::affine3_to_square to unpack model: mat3x4, // 3x3 matrix packed in mat2x4 and f32 as: // [0].xyz, [1].x, diff --git a/crates/bevy_sprite/src/render/sprite.wgsl b/crates/bevy_sprite/src/render/sprite.wgsl index 1f5c0125e17ae..48f0235155856 100644 --- a/crates/bevy_sprite/src/render/sprite.wgsl +++ b/crates/bevy_sprite/src/render/sprite.wgsl @@ -3,7 +3,7 @@ #endif #import bevy_render::{ - maths::affine_to_square, + maths::affine3_to_square, view::View, } @@ -37,7 +37,7 @@ fn vertex(in: VertexInput) -> VertexOutput { 0.0 ); - out.clip_position = view.view_proj * affine_to_square(mat3x4( + out.clip_position = view.view_proj * affine3_to_square(mat3x4( in.i_model_transpose_col0, in.i_model_transpose_col1, in.i_model_transpose_col2, From 6c4297ba4ff3c00865f5d3a67116f9eca4a152a1 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 20 Feb 2024 17:33:14 +0100 Subject: [PATCH 10/10] Remove comment noise --- crates/bevy_pbr/src/render/pbr_types.wgsl | 40 +++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/bevy_pbr/src/render/pbr_types.wgsl b/crates/bevy_pbr/src/render/pbr_types.wgsl index f3fc79a30c835..c08245034c222 100644 --- a/crates/bevy_pbr/src/render/pbr_types.wgsl +++ b/crates/bevy_pbr/src/render/pbr_types.wgsl @@ -1,29 +1,29 @@ #define_import_path bevy_pbr::pbr_types -// Alignments and sizes taken from . // Since this is a hot path, try to keep the alignment and size of the struct members in mind. +// You can find the alignment and sizes at . struct StandardMaterial { - base_color: vec4, // alignment 16, size 16 - emissive: vec4, // alignment 16, size 16 - attenuation_color: vec4, // alignment 16, size 16 - uv_transform: mat3x2, // alignment 8, size 24 - perceptual_roughness: f32, // alignment 4, size 4 - metallic: f32, // alignment 4, size 4 - reflectance: f32, // alignment 4, size 4 - diffuse_transmission: f32, // alignment 4, size 4 - specular_transmission: f32, // alignment 4, size 4 - thickness: f32, // alignment 4, size 4 - ior: f32, // alignment 4, size 4 - attenuation_distance: f32, // alignment 4, size 4 + base_color: vec4, + emissive: vec4, + attenuation_color: vec4, + uv_transform: mat3x2, + perceptual_roughness: f32, + metallic: f32, + reflectance: f32, + diffuse_transmission: f32, + specular_transmission: f32, + thickness: f32, + ior: f32, + attenuation_distance: f32, // 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options. - flags: u32, // alignment 4, size 4 - alpha_cutoff: f32, // alignment 4, size 4 - parallax_depth_scale: f32, // alignment 4, size 4 - max_parallax_layer_count: f32, // alignment 4, size 4 - lightmap_exposure: f32, // alignment 4, size 4 - max_relief_mapping_search_steps: u32, // alignment 4, size 4 + flags: u32, + alpha_cutoff: f32, + parallax_depth_scale: f32, + max_parallax_layer_count: f32, + lightmap_exposure: f32, + max_relief_mapping_search_steps: u32, /// ID for specifying which deferred lighting pass should be used for rendering this material, if any. - deferred_lighting_pass_id: u32, // alignment 4, size 4 + deferred_lighting_pass_id: u32, }; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!