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