diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index f5d003aadb2eca..662271a13764ce 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -331,6 +331,13 @@ fn load_material(material: &Material, load_context: &mut LoadContext) -> Handle< (occlusion_texture, false) }; + let emissive = material.emissive_factor(); + let emissive_texture = load_texture( + material.emissive_texture().map(|info| info.texture()), + load_context, + &mut dependencies, + ); + let color = pbr.base_color_factor(); load_context.set_labeled_asset( &material_label, @@ -344,6 +351,8 @@ fn load_material(material: &Material, load_context: &mut LoadContext) -> Handle< double_sided: material.double_sided(), occlusion_shares_metallic_roughness_texture, occlusion_texture, + emissive: Color::rgba(emissive[0], emissive[1], emissive[2], 1.0), + emissive_texture, unlit: material.unlit(), ..Default::default() }) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 7cc2d2be8a55b9..dc6a52e62a3813 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -37,6 +37,11 @@ pub struct StandardMaterial { pub occlusion_shares_metallic_roughness_texture: bool, #[shader_def] pub occlusion_texture: Option>, + // Use a color for user friendliness even though we technically don't use .a + // Might be used in the future for exposure correction in HDR + pub emissive: Color, + #[shader_def] + pub emissive_texture: Option>, #[shader_def] #[render_resources(ignore)] #[shader_def] @@ -64,6 +69,8 @@ impl Default for StandardMaterial { double_sided: false, occlusion_shares_metallic_roughness_texture: false, occlusion_texture: None, + emissive: Color::BLACK, + emissive_texture: None, unlit: false, } } diff --git a/crates/bevy_pbr/src/render_graph/pipeline/pbr.frag b/crates/bevy_pbr/src/render_graph/pipeline/pbr.frag index 4b78d3ee1b3350..0c5bf383fc4ff4 100644 --- a/crates/bevy_pbr/src/render_graph/pipeline/pbr.frag +++ b/crates/bevy_pbr/src/render_graph/pipeline/pbr.frag @@ -107,6 +107,16 @@ layout(set = 3, binding = 11) uniform sampler StandardMaterial_occlusion_texture_sampler; # endif +layout(set = 3, binding = 12) uniform StandardMaterial_emissive { + vec4 emissive; +}; + +# if defined(STANDARDMATERIAL_EMISSIVE_TEXTURE) +layout(set = 3, binding = 13) uniform texture2D StandardMaterial_emissive_texture; +layout(set = 3, + binding = 14) uniform sampler StandardMaterial_emissive_texture_sampler; +# endif + # define saturate(x) clamp(x, 0.0, 1.0) const float PI = 3.141592653589793; @@ -315,6 +325,11 @@ void main() { float occlusion = 1.0; # endif +# ifdef STANDARDMATERIAL_EMISSIVE_TEXTURE + // TODO use .a for exposure compensation in HDR + vec3 emissive.rgb *= texture(sampler2D(StandardMaterial_emissive_texture, StandardMaterial_emissive_texture_sampler), v_Uv).rgb; +# endif + vec3 V = normalize(CameraPos.xyz - v_WorldPosition.xyz); // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" float NdotV = max(dot(N, V), 1e-4); @@ -363,7 +378,9 @@ void main() { vec3 diffuse_ambient = EnvBRDFApprox(diffuseColor, 1.0, NdotV); vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV); - output_color.rgb = light_accum + (diffuse_ambient + specular_ambient) * AmbientColor * occlusion; + output_color.rgb = light_accum; + output_color.rgb += (diffuse_ambient + specular_ambient) * AmbientColor * occlusion; + output_color.rgb += emissive.rgb * output_color.a; // tone_mapping output_color.rgb = reinhard_luminance(output_color.rgb); diff --git a/examples/3d/pbr_textures.rs b/examples/3d/pbr_textures.rs index a5ac37f844b9cf..a7abe81db084c4 100644 --- a/examples/3d/pbr_textures.rs +++ b/examples/3d/pbr_textures.rs @@ -38,6 +38,7 @@ fn setup( })), material: materials.add(StandardMaterial { base_color: Color::YELLOW, + emissive: Color::WHITE * 10.0f32, ..Default::default() }), transform: Transform::default(),