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