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