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