bevy_pbr/pbr_material.rs
1use bevy_asset::Asset;
2use bevy_color::{Alpha, ColorToComponents};
3use bevy_material::OpaqueRendererMethod;
4use bevy_math::{Affine2, Affine3, Mat2, Mat3, Vec2, Vec3, Vec4};
5use bevy_mesh::{MeshVertexBufferLayoutRef, UvChannel};
6use bevy_reflect::{std_traits::ReflectDefault, Reflect};
7use bevy_render::{render_asset::RenderAssets, render_resource::*, texture::GpuImage};
8use bevy_shader::ShaderDefVal;
9use bitflags::bitflags;
10
11use crate::{deferred::DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID, *};
12
13/// A material with "standard" properties used in PBR lighting.
14/// Standard property values with pictures here:
15/// <https://google.github.io/filament/notes/material_properties.html>.
16///
17/// May be created directly from a [`Color`] or an [`Image`].
18///
19/// The `StandardMaterial` can be extended with more data and custom
20/// shaders using [`ExtendedMaterial`]. Examples of how to do this can
21/// be found in the Bevy examples.
22#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
23#[bind_group_data(StandardMaterialKey)]
24#[data(0, StandardMaterialUniform, binding_array(10))]
25#[bindless(index_table(range(0..31)))]
26#[reflect(Default, Debug, Clone)]
27pub struct StandardMaterial {
28 /// The color of the surface of the material before lighting.
29 ///
30 /// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
31 /// in between. If used together with a `base_color_texture`, this is factored into the final
32 /// base color as `base_color * base_color_texture_value`.
33 ///
34 /// Defaults to [`Color::WHITE`].
35 pub base_color: Color,
36
37 /// The UV channel to use for the [`StandardMaterial::base_color_texture`].
38 ///
39 /// Defaults to [`UvChannel::Uv0`].
40 pub base_color_channel: UvChannel,
41
42 /// The texture component of the material's color before lighting.
43 /// The actual pre-lighting color is `base_color * this_texture`.
44 ///
45 /// See [`base_color`] for details.
46 ///
47 /// You should set `base_color` to [`Color::WHITE`] (the default)
48 /// if you want the texture to show as-is.
49 ///
50 /// Setting `base_color` to something else than white will tint
51 /// the texture. For example, setting `base_color` to pure red will
52 /// tint the texture red.
53 ///
54 /// [`base_color`]: StandardMaterial::base_color
55 #[texture(1)]
56 #[sampler(2)]
57 #[dependency]
58 pub base_color_texture: Option<Handle<Image>>,
59
60 // Use a color for user friendliness even though we technically don't use the alpha channel
61 // Might be used in the future for exposure correction in HDR
62 /// Color the material "emits" to the camera.
63 ///
64 /// This is typically used for monitor screens or LED lights.
65 /// Anything that can be visible even in darkness.
66 ///
67 /// The emissive color is added to what would otherwise be the material's visible color.
68 /// This means that for a light emissive value, in darkness,
69 /// you will mostly see the emissive component.
70 ///
71 /// The default emissive color is [`LinearRgba::BLACK`], which doesn't add anything to the material color.
72 ///
73 /// Emissive strength is controlled by the value of the color channels,
74 /// while the hue is controlled by their relative values.
75 ///
76 /// As a result, channel values for `emissive`
77 /// colors can exceed `1.0`. For instance, a `base_color` of
78 /// `LinearRgba::rgb(1.0, 0.0, 0.0)` represents the brightest
79 /// red for objects that reflect light, but an emissive color
80 /// like `LinearRgba::rgb(1000.0, 0.0, 0.0)` can be used to create
81 /// intensely bright red emissive effects.
82 ///
83 /// This results in a final luminance value when multiplied
84 /// by the value of the greyscale emissive texture (which ranges from 0 for black to 1 for white).
85 /// Luminance is a measure of the amount of light emitted per unit area,
86 /// and can be thought of as the "brightness" of the effect.
87 /// In Bevy, we treat these luminance values as the physical units of cd/m², aka nits.
88 ///
89 /// Increasing the emissive strength of the color will impact visual effects
90 /// like bloom, but it's important to note that **an emissive material won't
91 /// typically light up surrounding areas like a light source**,
92 /// it just adds a value to the color seen on screen.
93 pub emissive: LinearRgba,
94
95 /// The weight in which the camera exposure influences the emissive color.
96 /// A value of `0.0` means the emissive color is not affected by the camera exposure.
97 /// In opposition, a value of `1.0` means the emissive color is multiplied by the camera exposure.
98 ///
99 /// Defaults to `0.0`
100 pub emissive_exposure_weight: f32,
101
102 /// The UV channel to use for the [`StandardMaterial::emissive_texture`].
103 ///
104 /// Defaults to [`UvChannel::Uv0`].
105 pub emissive_channel: UvChannel,
106
107 /// The emissive map, multiplies pixels with [`emissive`]
108 /// to get the final "emitting" color of a surface.
109 ///
110 /// This color is multiplied by [`emissive`] to get the final emitted color.
111 /// Meaning that you should set [`emissive`] to [`Color::WHITE`]
112 /// if you want to use the full range of color of the emissive texture.
113 ///
114 /// [`emissive`]: StandardMaterial::emissive
115 #[texture(3)]
116 #[sampler(4)]
117 #[dependency]
118 pub emissive_texture: Option<Handle<Image>>,
119
120 /// Linear perceptual roughness, clamped to `[0.089, 1.0]` in the shader.
121 ///
122 /// Defaults to `0.5`.
123 ///
124 /// Low values result in a "glossy" material with specular highlights,
125 /// while values close to `1` result in rough materials.
126 ///
127 /// If used together with a roughness/metallic texture, this is factored into the final base
128 /// color as `roughness * roughness_texture_value`.
129 ///
130 /// 0.089 is the minimum floating point value that won't be rounded down to 0 in the
131 /// calculations used.
132 // Technically for 32-bit floats, 0.045 could be used.
133 // See <https://google.github.io/filament/Filament.md.html#materialsystem/parameterization>
134 pub perceptual_roughness: f32,
135
136 /// How "metallic" the material appears, within `[0.0, 1.0]`.
137 ///
138 /// This should be set to 0.0 for dielectric materials or 1.0 for metallic materials.
139 /// For a hybrid surface such as corroded metal, you may need to use in-between values.
140 ///
141 /// Defaults to `0.00`, for dielectric.
142 ///
143 /// If used together with a roughness/metallic texture, this is factored into the final base
144 /// color as `metallic * metallic_texture_value`.
145 pub metallic: f32,
146
147 /// The UV channel to use for the [`StandardMaterial::metallic_roughness_texture`].
148 ///
149 /// Defaults to [`UvChannel::Uv0`].
150 pub metallic_roughness_channel: UvChannel,
151
152 /// Metallic and roughness maps, stored as a single texture.
153 ///
154 /// The blue channel contains metallic values,
155 /// and the green channel contains the roughness values.
156 /// Other channels are unused.
157 ///
158 /// A two-channel image has no blue channel, so it is read as red = roughness,
159 /// green = metallic instead.
160 ///
161 /// Those values are multiplied by the scalar ones of the material,
162 /// see [`metallic`] and [`perceptual_roughness`] for details.
163 ///
164 /// Note that with the default values of [`metallic`] and [`perceptual_roughness`],
165 /// setting this texture has no effect. If you want to exclusively use the
166 /// `metallic_roughness_texture` values for your material, make sure to set [`metallic`]
167 /// and [`perceptual_roughness`] to `1.0`.
168 ///
169 /// [`metallic`]: StandardMaterial::metallic
170 /// [`perceptual_roughness`]: StandardMaterial::perceptual_roughness
171 #[texture(5)]
172 #[sampler(6)]
173 #[dependency]
174 pub metallic_roughness_texture: Option<Handle<Image>>,
175
176 /// Specular intensity for non-metals on a linear scale of `[0.0, 1.0]`.
177 ///
178 /// Use the value as a way to control the intensity of the
179 /// specular highlight of the material, i.e. how reflective is the material,
180 /// rather than the physical property "reflectance."
181 ///
182 /// Set to `0.0`, no specular highlight is visible, the highlight is strongest
183 /// when `reflectance` is set to `1.0`.
184 ///
185 /// Defaults to `0.5` which is mapped to 4% reflectance in the shader.
186 #[doc(alias = "specular_intensity")]
187 pub reflectance: f32,
188
189 /// A color with which to modulate the [`StandardMaterial::reflectance`] for
190 /// non-metals.
191 ///
192 /// The specular highlights and reflection are tinted with this color. Note
193 /// that it has no effect for non-metals.
194 ///
195 /// This feature is currently unsupported in the deferred rendering path, in
196 /// order to reduce the size of the geometry buffers.
197 ///
198 /// Defaults to [`Color::WHITE`].
199 #[doc(alias = "specular_color")]
200 pub specular_tint: Color,
201
202 /// The amount of light transmitted _diffusely_ through the material (i.e. “translucency”).
203 ///
204 /// Implemented as a second, flipped [Lambertian diffuse](https://en.wikipedia.org/wiki/Lambertian_reflectance) lobe,
205 /// which provides an inexpensive but plausible approximation of translucency for thin dielectric objects (e.g. paper,
206 /// leaves, some fabrics) or thicker volumetric materials with short scattering distances (e.g. porcelain, wax).
207 ///
208 /// For specular transmission usecases with refraction (e.g. glass) use the [`StandardMaterial::specular_transmission`] and
209 /// [`StandardMaterial::ior`] properties instead.
210 ///
211 /// - When set to `0.0` (the default) no diffuse light is transmitted;
212 /// - When set to `1.0` all diffuse light is transmitted through the material;
213 /// - Values higher than `0.5` will cause more diffuse light to be transmitted than reflected, resulting in a “darker”
214 /// appearance on the side facing the light than the opposite side. (e.g. plant leaves)
215 ///
216 /// ## Notes
217 ///
218 /// - The material's [`StandardMaterial::base_color`] also modulates the transmitted light;
219 /// - To receive transmitted shadows on the diffuse transmission lobe (i.e. the “backside”) of the material,
220 /// use the [`TransmittedShadowReceiver`](bevy_light::TransmittedShadowReceiver) component.
221 #[doc(alias = "translucency")]
222 pub diffuse_transmission: f32,
223
224 /// The UV channel to use for the [`StandardMaterial::diffuse_transmission_texture`].
225 ///
226 /// Defaults to [`UvChannel::Uv0`].
227 #[cfg(feature = "pbr_transmission_textures")]
228 pub diffuse_transmission_channel: UvChannel,
229
230 /// A map that modulates diffuse transmission via its alpha channel. Multiplied by [`StandardMaterial::diffuse_transmission`]
231 /// to obtain the final result.
232 ///
233 /// **Important:** The [`StandardMaterial::diffuse_transmission`] property must be set to a value higher than 0.0,
234 /// or this texture won't have any effect.
235 #[cfg_attr(feature = "pbr_transmission_textures", texture(19))]
236 #[cfg_attr(feature = "pbr_transmission_textures", sampler(20))]
237 #[cfg(feature = "pbr_transmission_textures")]
238 #[dependency]
239 pub diffuse_transmission_texture: Option<Handle<Image>>,
240
241 /// The amount of light transmitted _specularly_ through the material (i.e. via refraction).
242 ///
243 /// - When set to `0.0` (the default) no light is transmitted.
244 /// - When set to `1.0` all light is transmitted through the material.
245 ///
246 /// [`ScreenSpaceTransmission`] must be added to the camera to make specular transmission work.
247 ///
248 /// The material's [`StandardMaterial::base_color`] also modulates the transmitted light.
249 ///
250 /// **Note:** Typically used in conjunction with [`StandardMaterial::thickness`], [`StandardMaterial::ior`] and [`StandardMaterial::perceptual_roughness`].
251 ///
252 /// ## Performance
253 ///
254 /// Specular transmission is implemented as a relatively expensive screen-space effect that allows occluded objects to be seen through the material,
255 /// with distortion and blur effects.
256 ///
257 /// - [`crate::ScreenSpaceTransmission::steps`] can be used to enable transmissive objects
258 /// to be seen through other transmissive objects, at the cost of additional draw calls and texture copies; (Use with caution!)
259 /// - If a simplified approximation of specular transmission using only environment map lighting is sufficient, consider setting
260 /// [`crate::ScreenSpaceTransmission::steps`] to `0`.
261 /// - If purely diffuse light transmission is needed, (i.e. “translucency”) consider using [`StandardMaterial::diffuse_transmission`] instead,
262 /// for a much less expensive effect.
263 /// - Screen-space specular transmission is rendered in the [`Transmissive3d`]
264 /// pass. Materials using this effect are rendered there, even if they use [`AlphaMode::Blend`],
265 /// [`AlphaMode::Premultiplied`], [`AlphaMode::Add`], or [`AlphaMode::Multiply`].
266 #[doc(alias = "refraction")]
267 pub specular_transmission: f32,
268
269 /// The UV channel to use for the [`StandardMaterial::specular_transmission_texture`].
270 ///
271 /// Defaults to [`UvChannel::Uv0`].
272 #[cfg(feature = "pbr_transmission_textures")]
273 pub specular_transmission_channel: UvChannel,
274
275 /// A map that modulates specular transmission via its red channel. Multiplied by [`StandardMaterial::specular_transmission`]
276 /// to obtain the final result.
277 ///
278 /// **Important:** The [`StandardMaterial::specular_transmission`] property must be set to a value higher than 0.0,
279 /// or this texture won't have any effect.
280 #[cfg_attr(feature = "pbr_transmission_textures", texture(15))]
281 #[cfg_attr(feature = "pbr_transmission_textures", sampler(16))]
282 #[cfg(feature = "pbr_transmission_textures")]
283 #[dependency]
284 pub specular_transmission_texture: Option<Handle<Image>>,
285
286 /// Thickness of the volume beneath the material surface.
287 ///
288 /// When set to `0.0` (the default) the material appears as an infinitely-thin film,
289 /// transmitting light without distorting it.
290 ///
291 /// When set to any other value, the material distorts light like a thick lens.
292 ///
293 /// **Note:** Typically used in conjunction with [`StandardMaterial::specular_transmission`] and [`StandardMaterial::ior`], or with
294 /// [`StandardMaterial::diffuse_transmission`].
295 #[doc(alias = "volume")]
296 #[doc(alias = "thin_walled")]
297 pub thickness: f32,
298
299 /// The UV channel to use for the [`StandardMaterial::thickness_texture`].
300 ///
301 /// Defaults to [`UvChannel::Uv0`].
302 #[cfg(feature = "pbr_transmission_textures")]
303 pub thickness_channel: UvChannel,
304
305 /// A map that modulates thickness via its green channel. Multiplied by [`StandardMaterial::thickness`]
306 /// to obtain the final result.
307 ///
308 /// **Important:** The [`StandardMaterial::thickness`] property must be set to a value higher than 0.0,
309 /// or this texture won't have any effect.
310 #[cfg_attr(feature = "pbr_transmission_textures", texture(17))]
311 #[cfg_attr(feature = "pbr_transmission_textures", sampler(18))]
312 #[cfg(feature = "pbr_transmission_textures")]
313 #[dependency]
314 pub thickness_texture: Option<Handle<Image>>,
315
316 /// The [index of refraction](https://en.wikipedia.org/wiki/Refractive_index) of the material.
317 ///
318 /// Defaults to 1.5.
319 ///
320 /// | Material | Index of Refraction |
321 /// |:----------------|:---------------------|
322 /// | Vacuum | 1 |
323 /// | Air | 1.00 |
324 /// | Ice | 1.31 |
325 /// | Water | 1.33 |
326 /// | Eyes | 1.38 |
327 /// | Quartz | 1.46 |
328 /// | Olive Oil | 1.47 |
329 /// | Honey | 1.49 |
330 /// | Acrylic | 1.49 |
331 /// | Window Glass | 1.52 |
332 /// | Polycarbonate | 1.58 |
333 /// | Flint Glass | 1.69 |
334 /// | Ruby | 1.71 |
335 /// | Glycerine | 1.74 |
336 /// | Sapphire | 1.77 |
337 /// | Cubic Zirconia | 2.15 |
338 /// | Diamond | 2.42 |
339 /// | Moissanite | 2.65 |
340 ///
341 /// **Note:** Typically used in conjunction with [`StandardMaterial::specular_transmission`] and [`StandardMaterial::thickness`].
342 #[doc(alias = "index_of_refraction")]
343 #[doc(alias = "refraction_index")]
344 #[doc(alias = "refractive_index")]
345 pub ior: f32,
346
347 /// How far, on average, light travels through the volume beneath the material's
348 /// surface before being absorbed.
349 ///
350 /// Defaults to [`f32::INFINITY`], i.e. light is never absorbed.
351 ///
352 /// **Note:** To have any effect, must be used in conjunction with:
353 /// - [`StandardMaterial::attenuation_color`];
354 /// - [`StandardMaterial::thickness`];
355 /// - [`StandardMaterial::diffuse_transmission`] or [`StandardMaterial::specular_transmission`].
356 #[doc(alias = "absorption_distance")]
357 #[doc(alias = "extinction_distance")]
358 pub attenuation_distance: f32,
359
360 /// The resulting (non-absorbed) color after white light travels through the attenuation distance.
361 ///
362 /// Defaults to [`Color::WHITE`], i.e. no change.
363 ///
364 /// **Note:** To have any effect, must be used in conjunction with:
365 /// - [`StandardMaterial::attenuation_distance`];
366 /// - [`StandardMaterial::thickness`];
367 /// - [`StandardMaterial::diffuse_transmission`] or [`StandardMaterial::specular_transmission`].
368 #[doc(alias = "absorption_color")]
369 #[doc(alias = "extinction_color")]
370 pub attenuation_color: Color,
371
372 /// The UV channel to use for the [`StandardMaterial::normal_map_texture`].
373 ///
374 /// Defaults to [`UvChannel::Uv0`].
375 pub normal_map_channel: UvChannel,
376
377 /// Used to fake the lighting of bumps and dents on a material.
378 ///
379 /// A typical usage would be faking cobblestones on a flat plane mesh in 3D.
380 ///
381 /// # Notes
382 ///
383 /// Normal mapping with `StandardMaterial` and the core bevy PBR shaders requires:
384 /// - A normal map texture
385 /// - Vertex UVs
386 /// - Vertex tangents
387 /// - Vertex normals
388 ///
389 /// Tangents do not have to be stored in your model,
390 /// they can be generated using the [`Mesh::generate_tangents`] or
391 /// [`Mesh::with_generated_tangents`] methods.
392 /// If your material has a normal map, but still renders as a flat surface,
393 /// make sure your meshes have their tangents set.
394 ///
395 /// [`Mesh::generate_tangents`]: bevy_mesh::Mesh::generate_tangents
396 /// [`Mesh::with_generated_tangents`]: bevy_mesh::Mesh::with_generated_tangents
397 ///
398 /// # Usage
399 ///
400 /// ```
401 /// # use bevy_asset::{AssetServer, Handle};
402 /// # use bevy_ecs::change_detection::Res;
403 /// # use bevy_image::{Image, ImageLoaderSettings};
404 /// #
405 /// fn load_normal_map(asset_server: Res<AssetServer>) {
406 /// let normal_handle: Handle<Image> = asset_server.load_builder().with_settings(
407 /// // The normal map texture is in linear color space. Lighting won't look correct
408 /// // if `is_srgb` is `true`, which is the default.
409 /// |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
410 /// )
411 /// .load("textures/parallax_example/cube_normal.png");
412 /// }
413 /// ```
414 #[texture(9)]
415 #[sampler(10)]
416 #[dependency]
417 pub normal_map_texture: Option<Handle<Image>>,
418
419 /// Normal map textures authored for DirectX have their y-component flipped. Set this to flip
420 /// it to right-handed conventions.
421 pub flip_normal_map_y: bool,
422
423 /// The UV channel to use for the [`StandardMaterial::occlusion_texture`].
424 ///
425 /// Defaults to [`UvChannel::Uv0`].
426 pub occlusion_channel: UvChannel,
427
428 /// Specifies the level of exposure to ambient light.
429 ///
430 /// This is usually generated and stored automatically ("baked") by 3D-modeling software.
431 ///
432 /// Typically, steep concave parts of a model (such as the armpit of a shirt) are darker,
433 /// because they have little exposure to light.
434 /// An occlusion map specifies those parts of the model that light doesn't reach well.
435 ///
436 /// The material will be less lit in places where this texture is dark.
437 /// This is similar to ambient occlusion, but built into the model.
438 ///
439 /// It is very common to use an RGB texture that uses the red channel for the [`StandardMaterial::occlusion_texture`],
440 /// and the B and G channels for [`StandardMaterial::metallic_roughness_texture`].
441 /// In such cases, use the same image handle for both fields.
442 /// Notably, this is the setup used by [glTF](https://docs.blender.org/manual/en/latest/addons/import_export/scene_gltf2.html#baked-ambient-occlusion).
443 #[texture(7)]
444 #[sampler(8)]
445 #[dependency]
446 pub occlusion_texture: Option<Handle<Image>>,
447
448 /// The UV channel to use for the [`StandardMaterial::specular_texture`].
449 ///
450 /// Defaults to [`UvChannel::Uv0`].
451 #[cfg(feature = "pbr_specular_textures")]
452 pub specular_channel: UvChannel,
453
454 /// A map that specifies reflectance for non-metallic materials.
455 ///
456 /// Alpha values from [0.0, 1.0] in this texture are linearly mapped to
457 /// reflectance values of [0.0, 0.5] and multiplied by the constant
458 /// [`StandardMaterial::reflectance`] value. This follows the
459 /// `KHR_materials_specular` specification. The map will have no effect if
460 /// the material is fully metallic.
461 ///
462 /// When using this map, you may wish to set the
463 /// [`StandardMaterial::reflectance`] value to 2.0 so that this map can
464 /// express the full [0.0, 1.0] range of values.
465 ///
466 /// Note that, because the reflectance is stored in the alpha channel, and
467 /// the [`StandardMaterial::specular_tint_texture`] has no alpha value, it
468 /// may be desirable to pack the values together and supply the same
469 /// texture to both fields.
470 #[cfg_attr(feature = "pbr_specular_textures", texture(27))]
471 #[cfg_attr(feature = "pbr_specular_textures", sampler(28))]
472 #[cfg(feature = "pbr_specular_textures")]
473 #[dependency]
474 pub specular_texture: Option<Handle<Image>>,
475
476 /// The UV channel to use for the
477 /// [`StandardMaterial::specular_tint_texture`].
478 ///
479 /// Defaults to [`UvChannel::Uv0`].
480 #[cfg(feature = "pbr_specular_textures")]
481 pub specular_tint_channel: UvChannel,
482
483 /// A map that specifies color adjustment to be applied to the specular
484 /// reflection for non-metallic materials.
485 ///
486 /// The RGB values of this texture modulate the
487 /// [`StandardMaterial::specular_tint`] value. See the documentation for
488 /// that field for more information.
489 ///
490 /// Like the fixed specular tint value, this texture map isn't supported in
491 /// the deferred renderer.
492 #[cfg_attr(feature = "pbr_specular_textures", texture(29))]
493 #[cfg_attr(feature = "pbr_specular_textures", sampler(30))]
494 #[cfg(feature = "pbr_specular_textures")]
495 #[dependency]
496 pub specular_tint_texture: Option<Handle<Image>>,
497
498 /// An extra thin translucent layer on top of the main PBR layer. This is
499 /// typically used for painted surfaces.
500 ///
501 /// This value specifies the strength of the layer, which affects how
502 /// visible the clearcoat layer will be.
503 ///
504 /// Defaults to zero, specifying no clearcoat layer.
505 pub clearcoat: f32,
506
507 /// The UV channel to use for the [`StandardMaterial::clearcoat_texture`].
508 ///
509 /// Defaults to [`UvChannel::Uv0`].
510 #[cfg(feature = "pbr_multi_layer_material_textures")]
511 pub clearcoat_channel: UvChannel,
512
513 /// An image texture that specifies the strength of the clearcoat layer in
514 /// the red channel. Values sampled from this texture are multiplied by the
515 /// main [`StandardMaterial::clearcoat`] factor.
516 ///
517 /// As this is a non-color map, it must not be loaded as sRGB.
518 #[cfg_attr(feature = "pbr_multi_layer_material_textures", texture(21))]
519 #[cfg_attr(feature = "pbr_multi_layer_material_textures", sampler(22))]
520 #[cfg(feature = "pbr_multi_layer_material_textures")]
521 #[dependency]
522 pub clearcoat_texture: Option<Handle<Image>>,
523
524 /// The roughness of the clearcoat material. This is specified in exactly
525 /// the same way as the [`StandardMaterial::perceptual_roughness`].
526 ///
527 /// If the [`StandardMaterial::clearcoat`] value if zero, this has no
528 /// effect.
529 ///
530 /// Defaults to 0.5.
531 pub clearcoat_perceptual_roughness: f32,
532
533 /// The UV channel to use for the [`StandardMaterial::clearcoat_roughness_texture`].
534 ///
535 /// Defaults to [`UvChannel::Uv0`].
536 #[cfg(feature = "pbr_multi_layer_material_textures")]
537 pub clearcoat_roughness_channel: UvChannel,
538
539 /// An image texture that specifies the roughness of the clearcoat level in
540 /// the green channel. Values from this texture are multiplied by the main
541 /// [`StandardMaterial::clearcoat_perceptual_roughness`] factor.
542 ///
543 /// As this is a non-color map, it must not be loaded as sRGB.
544 #[cfg_attr(feature = "pbr_multi_layer_material_textures", texture(23))]
545 #[cfg_attr(feature = "pbr_multi_layer_material_textures", sampler(24))]
546 #[cfg(feature = "pbr_multi_layer_material_textures")]
547 #[dependency]
548 pub clearcoat_roughness_texture: Option<Handle<Image>>,
549
550 /// The UV channel to use for the [`StandardMaterial::clearcoat_normal_texture`].
551 ///
552 /// Defaults to [`UvChannel::Uv0`].
553 #[cfg(feature = "pbr_multi_layer_material_textures")]
554 pub clearcoat_normal_channel: UvChannel,
555
556 /// An image texture that specifies a normal map that is to be applied to
557 /// the clearcoat layer. This can be used to simulate, for example,
558 /// scratches on an outer layer of varnish. Normal maps are in the same
559 /// format as [`StandardMaterial::normal_map_texture`].
560 ///
561 /// Note that, if a clearcoat normal map isn't specified, the main normal
562 /// map, if any, won't be applied to the clearcoat. If you want a normal map
563 /// that applies to both the main material and to the clearcoat, specify it
564 /// in both [`StandardMaterial::normal_map_texture`] and this field.
565 ///
566 /// As this is a non-color map, it must not be loaded as sRGB.
567 #[cfg_attr(feature = "pbr_multi_layer_material_textures", texture(25))]
568 #[cfg_attr(feature = "pbr_multi_layer_material_textures", sampler(26))]
569 #[cfg(feature = "pbr_multi_layer_material_textures")]
570 #[dependency]
571 pub clearcoat_normal_texture: Option<Handle<Image>>,
572
573 /// Increases the roughness along a specific direction, so that the specular
574 /// highlight will be stretched instead of being a circular lobe.
575 ///
576 /// This value ranges from 0 (perfectly circular) to 1 (maximally
577 /// stretched). The default direction (corresponding to a
578 /// [`StandardMaterial::anisotropy_rotation`] of 0) aligns with the
579 /// *tangent* of the mesh; thus mesh tangents must be specified in order for
580 /// this parameter to have any meaning. The direction can be changed using
581 /// the [`StandardMaterial::anisotropy_rotation`] parameter.
582 ///
583 /// This is typically used for modeling surfaces such as brushed metal and
584 /// hair, in which one direction of the surface but not the other is smooth.
585 ///
586 /// See the [`KHR_materials_anisotropy` specification] for more details.
587 ///
588 /// [`KHR_materials_anisotropy` specification]:
589 /// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md
590 pub anisotropy_strength: f32,
591
592 /// The direction of increased roughness, in radians relative to the mesh
593 /// tangent.
594 ///
595 /// This parameter causes the roughness to vary according to the
596 /// [`StandardMaterial::anisotropy_strength`]. The rotation is applied in
597 /// tangent-bitangent space; thus, mesh tangents must be present for this
598 /// parameter to have any meaning.
599 ///
600 /// This parameter has no effect if
601 /// [`StandardMaterial::anisotropy_strength`] is zero. Its value can
602 /// optionally be adjusted across the mesh with the
603 /// [`StandardMaterial::anisotropy_texture`].
604 ///
605 /// See the [`KHR_materials_anisotropy` specification] for more details.
606 ///
607 /// [`KHR_materials_anisotropy` specification]:
608 /// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md
609 pub anisotropy_rotation: f32,
610
611 /// The UV channel to use for the [`StandardMaterial::anisotropy_texture`].
612 ///
613 /// Defaults to [`UvChannel::Uv0`].
614 #[cfg(feature = "pbr_anisotropy_texture")]
615 pub anisotropy_channel: UvChannel,
616
617 /// An image texture that allows the
618 /// [`StandardMaterial::anisotropy_strength`] and
619 /// [`StandardMaterial::anisotropy_rotation`] to vary across the mesh.
620 ///
621 /// The [`KHR_materials_anisotropy` specification] defines the format that
622 /// this texture must take. To summarize: the direction vector is encoded in
623 /// the red and green channels, while the strength is encoded in the blue
624 /// channels. For the direction vector, the red and green channels map the
625 /// color range [0, 1] to the vector range [-1, 1]. The direction vector
626 /// encoded in this texture modifies the default rotation direction in
627 /// tangent-bitangent space, before the
628 /// [`StandardMaterial::anisotropy_rotation`] parameter is applied. The
629 /// value in the blue channel is multiplied by the
630 /// [`StandardMaterial::anisotropy_strength`] value to produce the final
631 /// anisotropy strength.
632 ///
633 /// As the texel values don't represent colors, this texture must be in
634 /// linear color space, not sRGB.
635 ///
636 /// [`KHR_materials_anisotropy` specification]:
637 /// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md
638 #[cfg_attr(feature = "pbr_anisotropy_texture", texture(13))]
639 #[cfg_attr(feature = "pbr_anisotropy_texture", sampler(14))]
640 #[cfg(feature = "pbr_anisotropy_texture")]
641 #[dependency]
642 pub anisotropy_texture: Option<Handle<Image>>,
643
644 /// Support two-sided lighting by automatically flipping the normals for "back" faces
645 /// within the PBR lighting shader.
646 ///
647 /// Defaults to `false`.
648 /// This does not automatically configure backface culling,
649 /// which can be done via `cull_mode`.
650 pub double_sided: bool,
651
652 /// Whether to cull the "front", "back" or neither side of a mesh.
653 /// If set to `None`, the two sides of the mesh are visible.
654 ///
655 /// Defaults to `Some(Face::Back)`.
656 /// In bevy, the order of declaration of a triangle's vertices
657 /// in [`Mesh`] defines the triangle's front face.
658 ///
659 /// When a triangle is in a viewport,
660 /// if its vertices appear counter-clockwise from the viewport's perspective,
661 /// then the viewport is seeing the triangle's front face.
662 /// Conversely, if the vertices appear clockwise, you are seeing the back face.
663 ///
664 /// In short, in bevy, front faces winds counter-clockwise.
665 ///
666 /// Your 3D editing software should manage all of that.
667 ///
668 /// [`Mesh`]: bevy_mesh::Mesh
669 // TODO: include this in reflection somehow (maybe via remote types like serde https://serde.rs/remote-derive.html)
670 #[reflect(ignore, clone)]
671 pub cull_mode: Option<Face>,
672
673 /// Whether to apply only the base color to this material.
674 ///
675 /// Normals, occlusion textures, roughness, metallic, reflectance, emissive,
676 /// shadows, alpha mode and ambient light are ignored if this is set to `true`.
677 pub unlit: bool,
678
679 /// Whether to enable fog for this material.
680 pub fog_enabled: bool,
681
682 /// How to apply the alpha channel of the `base_color_texture`.
683 ///
684 /// See [`AlphaMode`] for details. Defaults to [`AlphaMode::Opaque`].
685 pub alpha_mode: AlphaMode,
686
687 /// Adjust rendered depth.
688 ///
689 /// A material with a positive depth bias will render closer to the
690 /// camera while negative values cause the material to render behind
691 /// other objects. This is independent of the viewport.
692 ///
693 /// `depth_bias` affects render ordering and depth write operations
694 /// using the `wgpu::DepthBiasState::Constant` field.
695 ///
696 /// [z-fighting]: https://en.wikipedia.org/wiki/Z-fighting
697 pub depth_bias: f32,
698
699 /// The depth map used for [parallax mapping].
700 ///
701 /// It is a grayscale image where white represents bottom and black the top.
702 /// If this field is set, bevy will apply [parallax mapping].
703 /// Parallax mapping, unlike simple normal maps, will move the texture
704 /// coordinate according to the current perspective,
705 /// giving actual depth to the texture.
706 ///
707 /// The visual result is similar to a displacement map,
708 /// but does not require additional geometry.
709 ///
710 /// Use the [`parallax_depth_scale`] field to control the depth of the parallax.
711 ///
712 /// ## Limitations
713 ///
714 /// - It will look weird on bent/non-planar surfaces.
715 /// - The depth of the pixel does not reflect its visual position, resulting
716 /// in artifacts for depth-dependent features such as fog or SSAO.
717 /// - For the same reason, the geometry silhouette will always be
718 /// the one of the actual geometry, not the parallaxed version, resulting
719 /// in awkward looks on intersecting parallaxed surfaces.
720 ///
721 /// ## Performance
722 ///
723 /// Parallax mapping requires multiple texture lookups, proportional to
724 /// [`max_parallax_layer_count`], which might be costly.
725 ///
726 /// Use the [`parallax_mapping_method`] and [`max_parallax_layer_count`] fields
727 /// to tweak the shader, trading graphical quality for performance.
728 ///
729 /// To improve performance, set your `depth_map`'s [`Image::sampler`]
730 /// filter mode to `FilterMode::Nearest`, as [this paper] indicates, it improves
731 /// performance a bit.
732 ///
733 /// To reduce artifacts, avoid steep changes in depth, blurring the depth
734 /// map helps with this.
735 ///
736 /// Larger depth maps haves a disproportionate performance impact.
737 ///
738 /// [this paper]: https://www.diva-portal.org/smash/get/diva2:831762/FULLTEXT01.pdf
739 /// [parallax mapping]: https://en.wikipedia.org/wiki/Parallax_mapping
740 /// [`parallax_depth_scale`]: StandardMaterial::parallax_depth_scale
741 /// [`parallax_mapping_method`]: StandardMaterial::parallax_mapping_method
742 /// [`max_parallax_layer_count`]: StandardMaterial::max_parallax_layer_count
743 #[texture(11)]
744 #[sampler(12)]
745 #[dependency]
746 pub depth_map: Option<Handle<Image>>,
747
748 /// How deep the offset introduced by the depth map should be.
749 ///
750 /// Default is `0.1`, anything over that value may look distorted.
751 /// Lower values lessen the effect.
752 ///
753 /// The depth is relative to texture size. This means that if your texture
754 /// occupies a surface of `1` world unit, and `parallax_depth_scale` is `0.1`, then
755 /// the in-world depth will be of `0.1` world units.
756 /// If the texture stretches for `10` world units, then the final depth
757 /// will be of `1` world unit.
758 pub parallax_depth_scale: f32,
759
760 /// Which parallax mapping method to use.
761 ///
762 /// We recommend that all objects use the same [`ParallaxMappingMethod`], to avoid
763 /// duplicating and running two shaders.
764 pub parallax_mapping_method: ParallaxMappingMethod,
765
766 /// In how many layers to split the depth maps for parallax mapping.
767 ///
768 /// If you are seeing jaggy edges, increase this value.
769 /// However, this incurs a performance cost.
770 ///
771 /// Dependent on the situation, switching to [`ParallaxMappingMethod::Relief`]
772 /// and keeping this value low might have better performance than increasing the
773 /// layer count while using [`ParallaxMappingMethod::Occlusion`].
774 ///
775 /// Default is `16.0`.
776 pub max_parallax_layer_count: f32,
777
778 /// The exposure (brightness) level of the lightmap, if present.
779 pub lightmap_exposure: f32,
780
781 /// Render method used for opaque materials. (Where `alpha_mode` is [`AlphaMode::Opaque`] or [`AlphaMode::Mask`])
782 pub opaque_render_method: OpaqueRendererMethod,
783
784 /// Used for selecting the deferred lighting pass for deferred materials.
785 /// Default is [`DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID`] for default
786 /// PBR deferred lighting pass. Ignored in the case of forward materials.
787 pub deferred_lighting_pass_id: u8,
788
789 /// The transform applied to the UVs corresponding to `ATTRIBUTE_UV_0` on the mesh before sampling. Default is identity.
790 pub uv_transform: Affine2,
791}
792
793impl StandardMaterial {
794 /// Horizontal flipping transform
795 ///
796 /// Multiplying this with another Affine2 returns transformation with horizontally flipped texture coords
797 pub const FLIP_HORIZONTAL: Affine2 = Affine2 {
798 matrix2: Mat2::from_cols(Vec2::new(-1.0, 0.0), Vec2::Y),
799 translation: Vec2::X,
800 };
801
802 /// Vertical flipping transform
803 ///
804 /// Multiplying this with another Affine2 returns transformation with vertically flipped texture coords
805 pub const FLIP_VERTICAL: Affine2 = Affine2 {
806 matrix2: Mat2::from_cols(Vec2::X, Vec2::new(0.0, -1.0)),
807 translation: Vec2::Y,
808 };
809
810 /// Flipping X 3D transform
811 ///
812 /// Multiplying this with another Affine3 returns transformation with flipped X coords
813 pub const FLIP_X: Affine3 = Affine3 {
814 matrix3: Mat3::from_cols(Vec3::new(-1.0, 0.0, 0.0), Vec3::Y, Vec3::Z),
815 translation: Vec3::X,
816 };
817
818 /// Flipping Y 3D transform
819 ///
820 /// Multiplying this with another Affine3 returns transformation with flipped Y coords
821 pub const FLIP_Y: Affine3 = Affine3 {
822 matrix3: Mat3::from_cols(Vec3::X, Vec3::new(0.0, -1.0, 0.0), Vec3::Z),
823 translation: Vec3::Y,
824 };
825
826 /// Flipping Z 3D transform
827 ///
828 /// Multiplying this with another Affine3 returns transformation with flipped Z coords
829 pub const FLIP_Z: Affine3 = Affine3 {
830 matrix3: Mat3::from_cols(Vec3::X, Vec3::Y, Vec3::new(0.0, 0.0, -1.0)),
831 translation: Vec3::Z,
832 };
833
834 /// Flip the texture coordinates of the material.
835 pub fn flip(&mut self, horizontal: bool, vertical: bool) {
836 if horizontal {
837 // Multiplication of `Affine2` is order dependent, which is why
838 // we do not use the `*=` operator.
839 self.uv_transform = Self::FLIP_HORIZONTAL * self.uv_transform;
840 }
841 if vertical {
842 self.uv_transform = Self::FLIP_VERTICAL * self.uv_transform;
843 }
844 }
845
846 /// Consumes the material and returns a material with flipped texture coordinates
847 pub fn flipped(mut self, horizontal: bool, vertical: bool) -> Self {
848 self.flip(horizontal, vertical);
849 self
850 }
851
852 /// Creates a new material from a given color
853 pub fn from_color(color: impl Into<Color>) -> Self {
854 Self::from(color.into())
855 }
856}
857
858impl Default for StandardMaterial {
859 fn default() -> Self {
860 StandardMaterial {
861 // White because it gets multiplied with texture values if someone uses
862 // a texture.
863 base_color: Color::WHITE,
864 base_color_channel: UvChannel::Uv0,
865 base_color_texture: None,
866 emissive: LinearRgba::BLACK,
867 emissive_exposure_weight: 0.0,
868 emissive_channel: UvChannel::Uv0,
869 emissive_texture: None,
870 // Matches Blender's default roughness.
871 perceptual_roughness: 0.5,
872 // Metallic should generally be set to 0.0 or 1.0.
873 metallic: 0.0,
874 metallic_roughness_channel: UvChannel::Uv0,
875 metallic_roughness_texture: None,
876 // Minimum real-world reflectance is 2%, most materials between 2-5%
877 // Expressed in a linear scale and equivalent to 4% reflectance see
878 // <https://google.github.io/filament/Material%20Properties.pdf>
879 reflectance: 0.5,
880 diffuse_transmission: 0.0,
881 #[cfg(feature = "pbr_transmission_textures")]
882 diffuse_transmission_channel: UvChannel::Uv0,
883 #[cfg(feature = "pbr_transmission_textures")]
884 diffuse_transmission_texture: None,
885 specular_transmission: 0.0,
886 #[cfg(feature = "pbr_transmission_textures")]
887 specular_transmission_channel: UvChannel::Uv0,
888 #[cfg(feature = "pbr_transmission_textures")]
889 specular_transmission_texture: None,
890 thickness: 0.0,
891 #[cfg(feature = "pbr_transmission_textures")]
892 thickness_channel: UvChannel::Uv0,
893 #[cfg(feature = "pbr_transmission_textures")]
894 thickness_texture: None,
895 ior: 1.5,
896 attenuation_color: Color::WHITE,
897 attenuation_distance: f32::INFINITY,
898 occlusion_channel: UvChannel::Uv0,
899 occlusion_texture: None,
900 normal_map_channel: UvChannel::Uv0,
901 normal_map_texture: None,
902 #[cfg(feature = "pbr_specular_textures")]
903 specular_channel: UvChannel::Uv0,
904 #[cfg(feature = "pbr_specular_textures")]
905 specular_texture: None,
906 specular_tint: Color::WHITE,
907 #[cfg(feature = "pbr_specular_textures")]
908 specular_tint_channel: UvChannel::Uv0,
909 #[cfg(feature = "pbr_specular_textures")]
910 specular_tint_texture: None,
911 clearcoat: 0.0,
912 clearcoat_perceptual_roughness: 0.5,
913 #[cfg(feature = "pbr_multi_layer_material_textures")]
914 clearcoat_channel: UvChannel::Uv0,
915 #[cfg(feature = "pbr_multi_layer_material_textures")]
916 clearcoat_texture: None,
917 #[cfg(feature = "pbr_multi_layer_material_textures")]
918 clearcoat_roughness_channel: UvChannel::Uv0,
919 #[cfg(feature = "pbr_multi_layer_material_textures")]
920 clearcoat_roughness_texture: None,
921 #[cfg(feature = "pbr_multi_layer_material_textures")]
922 clearcoat_normal_channel: UvChannel::Uv0,
923 #[cfg(feature = "pbr_multi_layer_material_textures")]
924 clearcoat_normal_texture: None,
925 anisotropy_strength: 0.0,
926 anisotropy_rotation: 0.0,
927 #[cfg(feature = "pbr_anisotropy_texture")]
928 anisotropy_channel: UvChannel::Uv0,
929 #[cfg(feature = "pbr_anisotropy_texture")]
930 anisotropy_texture: None,
931 flip_normal_map_y: false,
932 double_sided: false,
933 cull_mode: Some(Face::Back),
934 unlit: false,
935 fog_enabled: true,
936 alpha_mode: AlphaMode::Opaque,
937 depth_bias: 0.0,
938 depth_map: None,
939 parallax_depth_scale: 0.1,
940 max_parallax_layer_count: 16.0,
941 lightmap_exposure: 1.0,
942 parallax_mapping_method: ParallaxMappingMethod::Occlusion,
943 opaque_render_method: OpaqueRendererMethod::Auto,
944 deferred_lighting_pass_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID,
945 uv_transform: Affine2::IDENTITY,
946 }
947 }
948}
949
950impl From<Color> for StandardMaterial {
951 fn from(color: Color) -> Self {
952 StandardMaterial {
953 base_color: color,
954 alpha_mode: if color.alpha() < 1.0 {
955 AlphaMode::Blend
956 } else {
957 AlphaMode::Opaque
958 },
959 ..Default::default()
960 }
961 }
962}
963
964impl From<Handle<Image>> for StandardMaterial {
965 fn from(texture: Handle<Image>) -> Self {
966 StandardMaterial {
967 base_color_texture: Some(texture),
968 ..Default::default()
969 }
970 }
971}
972
973impl StandardMaterial {
974 #[inline]
975 fn uses_screen_space_specular_transmission(&self) -> bool {
976 self.specular_transmission > 0.0
977 }
978}
979
980// NOTE: These must match the bit flags in bevy_pbr/src/render/pbr_types.wesl!
981bitflags::bitflags! {
982 /// Bitflags info about the material a shader is currently rendering.
983 /// This is accessible in the shader in the [`StandardMaterialUniform`]
984 #[repr(transparent)]
985 pub struct StandardMaterialFlags: u32 {
986 const BASE_COLOR_TEXTURE = 1 << 0;
987 const EMISSIVE_TEXTURE = 1 << 1;
988 const METALLIC_ROUGHNESS_TEXTURE = 1 << 2;
989 const OCCLUSION_TEXTURE = 1 << 3;
990 const DOUBLE_SIDED = 1 << 4;
991 const UNLIT = 1 << 5;
992 const TWO_COMPONENT_NORMAL_MAP = 1 << 6;
993 const FLIP_NORMAL_MAP_Y = 1 << 7;
994 const FOG_ENABLED = 1 << 8;
995 const DEPTH_MAP = 1 << 9; // Used for parallax mapping
996 const SPECULAR_TRANSMISSION_TEXTURE = 1 << 10;
997 const THICKNESS_TEXTURE = 1 << 11;
998 const DIFFUSE_TRANSMISSION_TEXTURE = 1 << 12;
999 const ATTENUATION_ENABLED = 1 << 13;
1000 const CLEARCOAT_TEXTURE = 1 << 14;
1001 const CLEARCOAT_ROUGHNESS_TEXTURE = 1 << 15;
1002 const CLEARCOAT_NORMAL_TEXTURE = 1 << 16;
1003 const ANISOTROPY_TEXTURE = 1 << 17;
1004 const SPECULAR_TEXTURE = 1 << 18;
1005 const SPECULAR_TINT_TEXTURE = 1 << 19;
1006 const METALLIC_ROUGHNESS_RG = 1 << 20; // Roughness in red, metallic in green
1007 const ALPHA_MODE_RESERVED_BITS = Self::ALPHA_MODE_MASK_BITS << Self::ALPHA_MODE_SHIFT_BITS; // ← Bitmask reserving bits for the `AlphaMode`
1008 const ALPHA_MODE_OPAQUE = 0 << Self::ALPHA_MODE_SHIFT_BITS; // ← Values are just sequential values bitshifted into
1009 const ALPHA_MODE_MASK = 1 << Self::ALPHA_MODE_SHIFT_BITS; // the bitmask, and can range from 0 to 7.
1010 const ALPHA_MODE_BLEND = 2 << Self::ALPHA_MODE_SHIFT_BITS; //
1011 const ALPHA_MODE_PREMULTIPLIED = 3 << Self::ALPHA_MODE_SHIFT_BITS; //
1012 const ALPHA_MODE_ADD = 4 << Self::ALPHA_MODE_SHIFT_BITS; // Right now only values 0–5 are used, which still gives
1013 const ALPHA_MODE_MULTIPLY = 5 << Self::ALPHA_MODE_SHIFT_BITS; // ← us "room" for two more modes without adding more bits
1014 const ALPHA_MODE_ALPHA_TO_COVERAGE = 6 << Self::ALPHA_MODE_SHIFT_BITS;
1015 const NONE = 0;
1016 const UNINITIALIZED = 0xFFFF;
1017 }
1018}
1019
1020impl StandardMaterialFlags {
1021 const ALPHA_MODE_MASK_BITS: u32 = 0b111;
1022 const ALPHA_MODE_SHIFT_BITS: u32 = 32 - Self::ALPHA_MODE_MASK_BITS.count_ones();
1023}
1024
1025/// The GPU representation of the uniform data of a [`StandardMaterial`].
1026#[derive(Clone, Default, ShaderType)]
1027pub struct StandardMaterialUniform {
1028 /// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
1029 /// in between.
1030 pub base_color: Vec4,
1031 // Use a color for user-friendliness even though we technically don't use the alpha channel
1032 // Might be used in the future for exposure correction in HDR
1033 pub emissive: Vec4,
1034 /// Color white light takes after traveling through the attenuation distance underneath the material surface
1035 pub attenuation_color: Vec4,
1036 /// The transform applied to the UVs corresponding to `ATTRIBUTE_UV_0` on the mesh before sampling. Default is identity.
1037 pub uv_transform: Mat3,
1038 /// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
1039 /// defaults to 0.5 which is mapped to 4% reflectance in the shader
1040 pub reflectance: Vec3,
1041 /// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
1042 /// Defaults to minimum of 0.089
1043 pub roughness: f32,
1044 /// From [0.0, 1.0], dielectric to pure metallic
1045 pub metallic: f32,
1046 /// Amount of diffuse light transmitted through the material
1047 pub diffuse_transmission: f32,
1048 /// Amount of specular light transmitted through the material
1049 pub specular_transmission: f32,
1050 /// Thickness of the volume underneath the material surface
1051 pub thickness: f32,
1052 /// Index of Refraction
1053 pub ior: f32,
1054 /// How far light travels through the volume underneath the material surface before being absorbed
1055 pub attenuation_distance: f32,
1056 pub clearcoat: f32,
1057 pub clearcoat_perceptual_roughness: f32,
1058 pub anisotropy_strength: f32,
1059 pub anisotropy_rotation: Vec2,
1060 /// The [`StandardMaterialFlags`] accessible in the `wgsl` shader.
1061 pub flags: u32,
1062 /// When the alpha mode mask flag is set, any base color alpha above this cutoff means fully opaque,
1063 /// and any below means fully transparent.
1064 pub alpha_cutoff: f32,
1065 /// The depth of the [`StandardMaterial::depth_map`] to apply.
1066 pub parallax_depth_scale: f32,
1067 /// In how many layers to split the depth maps for Steep parallax mapping.
1068 ///
1069 /// If your `parallax_depth_scale` is >0.1 and you are seeing jaggy edges,
1070 /// increase this value. However, this incurs a performance cost.
1071 pub max_parallax_layer_count: f32,
1072 /// The exposure (brightness) level of the lightmap, if present.
1073 pub lightmap_exposure: f32,
1074 /// Using [`ParallaxMappingMethod::Relief`], how many additional
1075 /// steps to use at most to find the depth value.
1076 pub max_relief_mapping_search_steps: u32,
1077 /// ID for specifying which deferred lighting pass should be used for rendering this material, if any.
1078 pub deferred_lighting_pass_id: u32,
1079}
1080
1081impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
1082 fn as_bind_group_shader_type(
1083 &self,
1084 images: &RenderAssets<GpuImage>,
1085 ) -> StandardMaterialUniform {
1086 let uses_screen_space_specular_transmission =
1087 self.uses_screen_space_specular_transmission();
1088 let mut flags = StandardMaterialFlags::NONE;
1089 if self.base_color_texture.is_some() {
1090 flags |= StandardMaterialFlags::BASE_COLOR_TEXTURE;
1091 }
1092 if self.emissive_texture.is_some() {
1093 flags |= StandardMaterialFlags::EMISSIVE_TEXTURE;
1094 }
1095 if let Some(handle) = &self.metallic_roughness_texture {
1096 flags |= StandardMaterialFlags::METALLIC_ROUGHNESS_TEXTURE;
1097 if images.get(handle.id()).is_some_and(|texture| {
1098 texture.texture_descriptor.format.channels() == TextureChannel::RG
1099 }) {
1100 flags |= StandardMaterialFlags::METALLIC_ROUGHNESS_RG;
1101 }
1102 }
1103 if self.occlusion_texture.is_some() {
1104 flags |= StandardMaterialFlags::OCCLUSION_TEXTURE;
1105 }
1106 if self.double_sided {
1107 flags |= StandardMaterialFlags::DOUBLE_SIDED;
1108 }
1109 if self.unlit {
1110 flags |= StandardMaterialFlags::UNLIT;
1111 }
1112 if self.fog_enabled {
1113 flags |= StandardMaterialFlags::FOG_ENABLED;
1114 }
1115 if self.depth_map.is_some() {
1116 flags |= StandardMaterialFlags::DEPTH_MAP;
1117 }
1118 #[cfg(feature = "pbr_transmission_textures")]
1119 {
1120 if uses_screen_space_specular_transmission
1121 && self.specular_transmission_texture.is_some()
1122 {
1123 flags |= StandardMaterialFlags::SPECULAR_TRANSMISSION_TEXTURE;
1124 }
1125 if self.thickness_texture.is_some() {
1126 flags |= StandardMaterialFlags::THICKNESS_TEXTURE;
1127 }
1128 if self.diffuse_transmission_texture.is_some() {
1129 flags |= StandardMaterialFlags::DIFFUSE_TRANSMISSION_TEXTURE;
1130 }
1131 }
1132
1133 #[cfg(feature = "pbr_anisotropy_texture")]
1134 {
1135 if self.anisotropy_texture.is_some() {
1136 flags |= StandardMaterialFlags::ANISOTROPY_TEXTURE;
1137 }
1138 }
1139
1140 #[cfg(feature = "pbr_specular_textures")]
1141 {
1142 if self.specular_texture.is_some() {
1143 flags |= StandardMaterialFlags::SPECULAR_TEXTURE;
1144 }
1145 if self.specular_tint_texture.is_some() {
1146 flags |= StandardMaterialFlags::SPECULAR_TINT_TEXTURE;
1147 }
1148 }
1149
1150 #[cfg(feature = "pbr_multi_layer_material_textures")]
1151 {
1152 if self.clearcoat_texture.is_some() {
1153 flags |= StandardMaterialFlags::CLEARCOAT_TEXTURE;
1154 }
1155 if self.clearcoat_roughness_texture.is_some() {
1156 flags |= StandardMaterialFlags::CLEARCOAT_ROUGHNESS_TEXTURE;
1157 }
1158 if self.clearcoat_normal_texture.is_some() {
1159 flags |= StandardMaterialFlags::CLEARCOAT_NORMAL_TEXTURE;
1160 }
1161 }
1162
1163 let has_normal_map = self.normal_map_texture.is_some();
1164 if has_normal_map {
1165 let normal_map_id = self.normal_map_texture.as_ref().map(Handle::id).unwrap();
1166 if let Some(texture) = images.get(normal_map_id) {
1167 match texture.texture_descriptor.format {
1168 // Dedicated 2-component unorm formats.
1169 TextureFormat::Rg8Unorm
1170 | TextureFormat::Rg16Unorm
1171 | TextureFormat::Bc5RgUnorm
1172 | TextureFormat::EacRg11Unorm
1173 // ASTC has no dedicated 2-channel block format; all ASTC blocks are
1174 // RGBA-shaped. Since `compressed_image_saver` writes 2-component normal maps
1175 // when using the ASTC backend, assume ASTC normal maps are 2-component.
1176 | TextureFormat::Astc {
1177 channel: AstcChannel::Unorm,
1178 ..
1179 } => {
1180 flags |= StandardMaterialFlags::TWO_COMPONENT_NORMAL_MAP;
1181 }
1182 _ => {}
1183 }
1184 }
1185 if self.flip_normal_map_y {
1186 flags |= StandardMaterialFlags::FLIP_NORMAL_MAP_Y;
1187 }
1188 }
1189 // NOTE: 0.5 is from the glTF default - do we want this?
1190 let mut alpha_cutoff = 0.5;
1191 match self.alpha_mode {
1192 AlphaMode::Opaque => flags |= StandardMaterialFlags::ALPHA_MODE_OPAQUE,
1193 AlphaMode::Mask(c) => {
1194 alpha_cutoff = c;
1195 flags |= StandardMaterialFlags::ALPHA_MODE_MASK;
1196 }
1197 AlphaMode::Blend => flags |= StandardMaterialFlags::ALPHA_MODE_BLEND,
1198 AlphaMode::Premultiplied => flags |= StandardMaterialFlags::ALPHA_MODE_PREMULTIPLIED,
1199 AlphaMode::Add => flags |= StandardMaterialFlags::ALPHA_MODE_ADD,
1200 AlphaMode::Multiply => flags |= StandardMaterialFlags::ALPHA_MODE_MULTIPLY,
1201 AlphaMode::AlphaToCoverage => {
1202 flags |= StandardMaterialFlags::ALPHA_MODE_ALPHA_TO_COVERAGE;
1203 }
1204 };
1205
1206 if self.attenuation_distance.is_finite() {
1207 flags |= StandardMaterialFlags::ATTENUATION_ENABLED;
1208 }
1209
1210 let mut emissive = self.emissive.to_vec4();
1211 emissive[3] = self.emissive_exposure_weight;
1212
1213 // Doing this up front saves having to do this repeatedly in the fragment shader.
1214 let anisotropy_rotation = Vec2::from_angle(self.anisotropy_rotation);
1215
1216 StandardMaterialUniform {
1217 base_color: LinearRgba::from(self.base_color).to_vec4(),
1218 emissive,
1219 roughness: self.perceptual_roughness,
1220 metallic: self.metallic,
1221 reflectance: LinearRgba::from(self.specular_tint).to_vec3() * self.reflectance,
1222 clearcoat: self.clearcoat,
1223 clearcoat_perceptual_roughness: self.clearcoat_perceptual_roughness,
1224 anisotropy_strength: self.anisotropy_strength,
1225 anisotropy_rotation,
1226 diffuse_transmission: self.diffuse_transmission,
1227 specular_transmission: if uses_screen_space_specular_transmission {
1228 self.specular_transmission
1229 } else {
1230 0.0
1231 },
1232 thickness: self.thickness,
1233 ior: self.ior,
1234 attenuation_distance: self.attenuation_distance,
1235 attenuation_color: LinearRgba::from(self.attenuation_color)
1236 .to_f32_array()
1237 .into(),
1238 flags: flags.bits(),
1239 alpha_cutoff,
1240 parallax_depth_scale: self.parallax_depth_scale,
1241 max_parallax_layer_count: self.max_parallax_layer_count,
1242 lightmap_exposure: self.lightmap_exposure,
1243 max_relief_mapping_search_steps: self.parallax_mapping_method.max_steps(),
1244 deferred_lighting_pass_id: self.deferred_lighting_pass_id as u32,
1245 uv_transform: self.uv_transform.into(),
1246 }
1247 }
1248}
1249
1250bitflags! {
1251 /// The pipeline key for `StandardMaterial`, packed into 64 bits.
1252 #[repr(C)]
1253 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
1254 pub struct StandardMaterialKey: u64 {
1255 const CULL_FRONT = 0x000001;
1256 const CULL_BACK = 0x000002;
1257 const NORMAL_MAP = 0x000004;
1258 const RELIEF_MAPPING = 0x000008;
1259 const DIFFUSE_TRANSMISSION = 0x000010;
1260 const SPECULAR_TRANSMISSION = 0x000020;
1261 const CLEARCOAT = 0x000040;
1262 const CLEARCOAT_NORMAL_MAP = 0x000080;
1263 const ANISOTROPY = 0x000100;
1264 const BASE_COLOR_UV = 0x000200;
1265 const EMISSIVE_UV = 0x000400;
1266 const METALLIC_ROUGHNESS_UV = 0x000800;
1267 const OCCLUSION_UV = 0x001000;
1268 const SPECULAR_TRANSMISSION_UV = 0x002000;
1269 const THICKNESS_UV = 0x004000;
1270 const DIFFUSE_TRANSMISSION_UV = 0x008000;
1271 const NORMAL_MAP_UV = 0x010000;
1272 const ANISOTROPY_UV = 0x020000;
1273 const CLEARCOAT_UV = 0x040000;
1274 const CLEARCOAT_ROUGHNESS_UV = 0x080000;
1275 const CLEARCOAT_NORMAL_UV = 0x100000;
1276 const SPECULAR_UV = 0x200000;
1277 const SPECULAR_TINT_UV = 0x400000;
1278 const DEPTH_BIAS = 0xffffffff_00000000;
1279 }
1280}
1281
1282const STANDARD_MATERIAL_KEY_DEPTH_BIAS_SHIFT: u64 = 32;
1283
1284impl From<&StandardMaterial> for StandardMaterialKey {
1285 fn from(material: &StandardMaterial) -> Self {
1286 let uses_screen_space_specular_transmission =
1287 material.uses_screen_space_specular_transmission();
1288 let mut key = StandardMaterialKey::empty();
1289 key.set(
1290 StandardMaterialKey::CULL_FRONT,
1291 material.cull_mode == Some(Face::Front),
1292 );
1293 key.set(
1294 StandardMaterialKey::CULL_BACK,
1295 material.cull_mode == Some(Face::Back),
1296 );
1297 key.set(
1298 StandardMaterialKey::NORMAL_MAP,
1299 material.normal_map_texture.is_some(),
1300 );
1301 key.set(
1302 StandardMaterialKey::RELIEF_MAPPING,
1303 matches!(
1304 material.parallax_mapping_method,
1305 ParallaxMappingMethod::Relief { .. }
1306 ),
1307 );
1308 key.set(
1309 StandardMaterialKey::DIFFUSE_TRANSMISSION,
1310 material.diffuse_transmission > 0.0,
1311 );
1312 key.set(
1313 StandardMaterialKey::SPECULAR_TRANSMISSION,
1314 uses_screen_space_specular_transmission,
1315 );
1316
1317 key.set(StandardMaterialKey::CLEARCOAT, material.clearcoat > 0.0);
1318
1319 #[cfg(feature = "pbr_multi_layer_material_textures")]
1320 key.set(
1321 StandardMaterialKey::CLEARCOAT_NORMAL_MAP,
1322 material.clearcoat > 0.0 && material.clearcoat_normal_texture.is_some(),
1323 );
1324
1325 key.set(
1326 StandardMaterialKey::ANISOTROPY,
1327 material.anisotropy_strength > 0.0,
1328 );
1329
1330 key.set(
1331 StandardMaterialKey::BASE_COLOR_UV,
1332 material.base_color_channel != UvChannel::Uv0,
1333 );
1334
1335 key.set(
1336 StandardMaterialKey::EMISSIVE_UV,
1337 material.emissive_channel != UvChannel::Uv0,
1338 );
1339 key.set(
1340 StandardMaterialKey::METALLIC_ROUGHNESS_UV,
1341 material.metallic_roughness_channel != UvChannel::Uv0,
1342 );
1343 key.set(
1344 StandardMaterialKey::OCCLUSION_UV,
1345 material.occlusion_channel != UvChannel::Uv0,
1346 );
1347 #[cfg(feature = "pbr_transmission_textures")]
1348 {
1349 key.set(
1350 StandardMaterialKey::SPECULAR_TRANSMISSION_UV,
1351 uses_screen_space_specular_transmission
1352 && material.specular_transmission_channel != UvChannel::Uv0,
1353 );
1354 key.set(
1355 StandardMaterialKey::THICKNESS_UV,
1356 material.thickness_channel != UvChannel::Uv0,
1357 );
1358 key.set(
1359 StandardMaterialKey::DIFFUSE_TRANSMISSION_UV,
1360 material.diffuse_transmission_channel != UvChannel::Uv0,
1361 );
1362 }
1363
1364 key.set(
1365 StandardMaterialKey::NORMAL_MAP_UV,
1366 material.normal_map_channel != UvChannel::Uv0,
1367 );
1368
1369 #[cfg(feature = "pbr_anisotropy_texture")]
1370 {
1371 key.set(
1372 StandardMaterialKey::ANISOTROPY_UV,
1373 material.anisotropy_channel != UvChannel::Uv0,
1374 );
1375 }
1376
1377 #[cfg(feature = "pbr_specular_textures")]
1378 {
1379 key.set(
1380 StandardMaterialKey::SPECULAR_UV,
1381 material.specular_channel != UvChannel::Uv0,
1382 );
1383 key.set(
1384 StandardMaterialKey::SPECULAR_TINT_UV,
1385 material.specular_tint_channel != UvChannel::Uv0,
1386 );
1387 }
1388
1389 #[cfg(feature = "pbr_multi_layer_material_textures")]
1390 {
1391 key.set(
1392 StandardMaterialKey::CLEARCOAT_UV,
1393 material.clearcoat_channel != UvChannel::Uv0,
1394 );
1395 key.set(
1396 StandardMaterialKey::CLEARCOAT_ROUGHNESS_UV,
1397 material.clearcoat_roughness_channel != UvChannel::Uv0,
1398 );
1399 key.set(
1400 StandardMaterialKey::CLEARCOAT_NORMAL_UV,
1401 material.clearcoat_normal_channel != UvChannel::Uv0,
1402 );
1403 }
1404
1405 key.insert(StandardMaterialKey::from_bits_retain(
1406 // Casting to i32 first to ensure the full i32 range is preserved.
1407 // (wgpu expects the depth_bias as an i32 when this is extracted in a later step)
1408 (material.depth_bias as i32 as u64) << STANDARD_MATERIAL_KEY_DEPTH_BIAS_SHIFT,
1409 ));
1410 key
1411 }
1412}
1413
1414impl Material for StandardMaterial {
1415 fn fragment_shader() -> ShaderRef {
1416 shader_ref(bevy_asset::embedded_path!("render/pbr.wesl"))
1417 }
1418
1419 #[inline]
1420 fn alpha_mode(&self) -> AlphaMode {
1421 self.alpha_mode
1422 }
1423
1424 #[inline]
1425 fn opaque_render_method(&self) -> OpaqueRendererMethod {
1426 match self.opaque_render_method {
1427 // For now, diffuse transmission doesn't work under deferred rendering as we don't pack
1428 // the required data into the GBuffer. If this material is set to `Auto`, we report it as
1429 // `Forward` so that it's rendered correctly, even when the `DefaultOpaqueRendererMethod`
1430 // is set to `Deferred`.
1431 //
1432 // If the developer explicitly sets the `OpaqueRendererMethod` to `Deferred`, we assume
1433 // they know what they're doing and don't override it.
1434 OpaqueRendererMethod::Auto if self.diffuse_transmission > 0.0 => {
1435 OpaqueRendererMethod::Forward
1436 }
1437 other => other,
1438 }
1439 }
1440
1441 #[inline]
1442 fn depth_bias(&self) -> f32 {
1443 self.depth_bias
1444 }
1445
1446 #[inline]
1447 fn reads_view_transmission_texture(&self) -> bool {
1448 self.uses_screen_space_specular_transmission()
1449 }
1450
1451 fn prepass_fragment_shader() -> ShaderRef {
1452 shader_ref(bevy_asset::embedded_path!("render/pbr_prepass.wesl"))
1453 }
1454
1455 fn deferred_fragment_shader() -> ShaderRef {
1456 shader_ref(bevy_asset::embedded_path!("render/pbr.wesl"))
1457 }
1458
1459 #[cfg(feature = "meshlet")]
1460 fn meshlet_mesh_fragment_shader() -> ShaderRef {
1461 Self::fragment_shader()
1462 }
1463
1464 #[cfg(feature = "meshlet")]
1465 fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
1466 Self::prepass_fragment_shader()
1467 }
1468
1469 #[cfg(feature = "meshlet")]
1470 fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
1471 Self::deferred_fragment_shader()
1472 }
1473
1474 fn specialize(
1475 _pipeline: &MaterialPipeline,
1476 descriptor: &mut RenderPipelineDescriptor,
1477 _layout: &MeshVertexBufferLayoutRef,
1478 key: MaterialPipelineKey<Self>,
1479 ) -> Result<(), SpecializedMeshPipelineError> {
1480 if let Some(fragment) = descriptor.fragment.as_mut() {
1481 let shader_defs = &mut fragment.shader_defs;
1482
1483 for (flags, shader_def) in [
1484 (
1485 StandardMaterialKey::NORMAL_MAP,
1486 "STANDARD_MATERIAL_NORMAL_MAP",
1487 ),
1488 (StandardMaterialKey::RELIEF_MAPPING, "RELIEF_MAPPING"),
1489 (
1490 StandardMaterialKey::DIFFUSE_TRANSMISSION,
1491 "STANDARD_MATERIAL_DIFFUSE_TRANSMISSION",
1492 ),
1493 (
1494 StandardMaterialKey::SPECULAR_TRANSMISSION,
1495 "STANDARD_MATERIAL_SPECULAR_TRANSMISSION",
1496 ),
1497 (
1498 StandardMaterialKey::DIFFUSE_TRANSMISSION
1499 | StandardMaterialKey::SPECULAR_TRANSMISSION,
1500 "STANDARD_MATERIAL_DIFFUSE_OR_SPECULAR_TRANSMISSION",
1501 ),
1502 (
1503 StandardMaterialKey::CLEARCOAT,
1504 "STANDARD_MATERIAL_CLEARCOAT",
1505 ),
1506 (
1507 StandardMaterialKey::CLEARCOAT_NORMAL_MAP,
1508 "STANDARD_MATERIAL_CLEARCOAT_NORMAL_MAP",
1509 ),
1510 (
1511 StandardMaterialKey::ANISOTROPY,
1512 "STANDARD_MATERIAL_ANISOTROPY",
1513 ),
1514 (
1515 StandardMaterialKey::BASE_COLOR_UV,
1516 "STANDARD_MATERIAL_BASE_COLOR_UV_B",
1517 ),
1518 (
1519 StandardMaterialKey::EMISSIVE_UV,
1520 "STANDARD_MATERIAL_EMISSIVE_UV_B",
1521 ),
1522 (
1523 StandardMaterialKey::METALLIC_ROUGHNESS_UV,
1524 "STANDARD_MATERIAL_METALLIC_ROUGHNESS_UV_B",
1525 ),
1526 (
1527 StandardMaterialKey::OCCLUSION_UV,
1528 "STANDARD_MATERIAL_OCCLUSION_UV_B",
1529 ),
1530 (
1531 StandardMaterialKey::SPECULAR_TRANSMISSION_UV,
1532 "STANDARD_MATERIAL_SPECULAR_TRANSMISSION_UV_B",
1533 ),
1534 (
1535 StandardMaterialKey::THICKNESS_UV,
1536 "STANDARD_MATERIAL_THICKNESS_UV_B",
1537 ),
1538 (
1539 StandardMaterialKey::DIFFUSE_TRANSMISSION_UV,
1540 "STANDARD_MATERIAL_DIFFUSE_TRANSMISSION_UV_B",
1541 ),
1542 (
1543 StandardMaterialKey::NORMAL_MAP_UV,
1544 "STANDARD_MATERIAL_NORMAL_MAP_UV_B",
1545 ),
1546 (
1547 StandardMaterialKey::CLEARCOAT_UV,
1548 "STANDARD_MATERIAL_CLEARCOAT_UV_B",
1549 ),
1550 (
1551 StandardMaterialKey::CLEARCOAT_ROUGHNESS_UV,
1552 "STANDARD_MATERIAL_CLEARCOAT_ROUGHNESS_UV_B",
1553 ),
1554 (
1555 StandardMaterialKey::CLEARCOAT_NORMAL_UV,
1556 "STANDARD_MATERIAL_CLEARCOAT_NORMAL_UV_B",
1557 ),
1558 (
1559 StandardMaterialKey::ANISOTROPY_UV,
1560 "STANDARD_MATERIAL_ANISOTROPY_UV_B",
1561 ),
1562 (
1563 StandardMaterialKey::SPECULAR_UV,
1564 "STANDARD_MATERIAL_SPECULAR_UV_B",
1565 ),
1566 (
1567 StandardMaterialKey::SPECULAR_TINT_UV,
1568 "STANDARD_MATERIAL_SPECULAR_TINT_UV_B",
1569 ),
1570 ] {
1571 if [
1572 StandardMaterialKey::SPECULAR_TRANSMISSION,
1573 StandardMaterialKey::SPECULAR_TRANSMISSION_UV,
1574 ]
1575 .contains(&flags)
1576 && !key
1577 .mesh_key
1578 .contains(MeshPipelineKey::VIEW_TRANSMISSION_TEXTURE)
1579 {
1580 continue;
1581 }
1582 if key.bind_group_data.intersects(flags) {
1583 shader_defs.push(shader_def.into());
1584 }
1585 }
1586
1587 if key
1588 .bind_group_data
1589 .intersects(StandardMaterialKey::SPECULAR_TRANSMISSION)
1590 && key
1591 .mesh_key
1592 .contains(MeshPipelineKey::VIEW_TRANSMISSION_TEXTURE)
1593 {
1594 // Do not load prepass normals for transmissive materials
1595 // https://github.com/bevyengine/bevy/pull/11140
1596 shader_defs.retain(|def| def != &ShaderDefVal::from("LOAD_PREPASS_NORMALS"));
1597 }
1598 }
1599
1600 // Generally, we want to cull front faces if `CULL_FRONT` is present and
1601 // backfaces if `CULL_BACK` is present. However, if the view has
1602 // `INVERT_CULLING` on (usually used for mirrors and the like), we do
1603 // the opposite.
1604 descriptor.primitive.cull_mode = match (
1605 key.bind_group_data
1606 .contains(StandardMaterialKey::CULL_FRONT),
1607 key.bind_group_data.contains(StandardMaterialKey::CULL_BACK),
1608 key.mesh_key.contains(MeshPipelineKey::INVERT_CULLING),
1609 ) {
1610 (true, false, false) | (false, true, true) => Some(Face::Front),
1611 (false, true, false) | (true, false, true) => Some(Face::Back),
1612 _ => None,
1613 };
1614
1615 if let Some(label) = &mut descriptor.label {
1616 *label = format!("pbr_{}", *label).into();
1617 }
1618 if let Some(depth_stencil) = descriptor.depth_stencil.as_mut() {
1619 depth_stencil.bias.constant =
1620 (key.bind_group_data.bits() >> STANDARD_MATERIAL_KEY_DEPTH_BIAS_SHIFT) as i32;
1621 }
1622 Ok(())
1623 }
1624}