11use crate :: {
2- AlphaMode , Material , MaterialPipeline , MaterialPipelineKey , PBR_PREPASS_SHADER_HANDLE ,
3- PBR_SHADER_HANDLE ,
2+ AlphaMode , Material , MaterialPipeline , MaterialPipelineKey , ParallaxMappingMethod ,
3+ PBR_PREPASS_SHADER_HANDLE , PBR_SHADER_HANDLE ,
44} ;
55use bevy_asset:: Handle ;
66use bevy_math:: Vec4 ;
@@ -231,6 +231,84 @@ pub struct StandardMaterial {
231231 ///
232232 /// [z-fighting]: https://en.wikipedia.org/wiki/Z-fighting
233233 pub depth_bias : f32 ,
234+
235+ /// The depth map used for [parallax mapping].
236+ ///
237+ /// It is a greyscale image where white represents bottom and black the top.
238+ /// If this field is set, bevy will apply [parallax mapping].
239+ /// Parallax mapping, unlike simple normal maps, will move the texture
240+ /// coordinate according to the current perspective,
241+ /// giving actual depth to the texture.
242+ ///
243+ /// The visual result is similar to a displacement map,
244+ /// but does not require additional geometry.
245+ ///
246+ /// Use the [`parallax_depth_scale`] field to control the depth of the parallax.
247+ ///
248+ /// ## Limitations
249+ ///
250+ /// - It will look weird on bent/non-planar surfaces.
251+ /// - The depth of the pixel does not reflect its visual position, resulting
252+ /// in artifacts for depth-dependent features such as fog or SSAO.
253+ /// - For the same reason, the the geometry silhouette will always be
254+ /// the one of the actual geometry, not the parallaxed version, resulting
255+ /// in awkward looks on intersecting parallaxed surfaces.
256+ ///
257+ /// ## Performance
258+ ///
259+ /// Parallax mapping requires multiple texture lookups, proportional to
260+ /// [`max_parallax_layer_count`], which might be costly.
261+ ///
262+ /// Use the [`parallax_mapping_method`] and [`max_parallax_layer_count`] fields
263+ /// to tweak the shader, trading graphical quality for performance.
264+ ///
265+ /// To improve performance, set your `depth_map`'s [`Image::sampler_descriptor`]
266+ /// filter mode to `FilterMode::Nearest`, as [this paper] indicates, it improves
267+ /// performance a bit.
268+ ///
269+ /// To reduce artifacts, avoid steep changes in depth, blurring the depth
270+ /// map helps with this.
271+ ///
272+ /// Larger depth maps haves a disproportionate performance impact.
273+ ///
274+ /// [this paper]: https://www.diva-portal.org/smash/get/diva2:831762/FULLTEXT01.pdf
275+ /// [parallax mapping]: https://en.wikipedia.org/wiki/Parallax_mapping
276+ /// [`parallax_depth_scale`]: StandardMaterial::parallax_depth_scale
277+ /// [`parallax_mapping_method`]: StandardMaterial::parallax_mapping_method
278+ /// [`max_parallax_layer_count`]: StandardMaterial::max_parallax_layer_count
279+ #[ texture( 11 ) ]
280+ #[ sampler( 12 ) ]
281+ pub depth_map : Option < Handle < Image > > ,
282+
283+ /// How deep the offset introduced by the depth map should be.
284+ ///
285+ /// Default is `0.1`, anything over that value may look distorted.
286+ /// Lower values lessen the effect.
287+ ///
288+ /// The depth is relative to texture size. This means that if your texture
289+ /// occupies a surface of `1` world unit, and `parallax_depth_scale` is `0.1`, then
290+ /// the in-world depth will be of `0.1` world units.
291+ /// If the texture stretches for `10` world units, then the final depth
292+ /// will be of `1` world unit.
293+ pub parallax_depth_scale : f32 ,
294+
295+ /// Which parallax mapping method to use.
296+ ///
297+ /// We recommend that all objects use the same [`ParallaxMappingMethod`], to avoid
298+ /// duplicating and running two shaders.
299+ pub parallax_mapping_method : ParallaxMappingMethod ,
300+
301+ /// In how many layers to split the depth maps for parallax mapping.
302+ ///
303+ /// If you are seeing jaggy edges, increase this value.
304+ /// However, this incurs a performance cost.
305+ ///
306+ /// Dependent on the situation, switching to [`ParallaxMappingMethod::Relief`]
307+ /// and keeping this value low might have better performance than increasing the
308+ /// layer count while using [`ParallaxMappingMethod::Occlusion`].
309+ ///
310+ /// Default is `16.0`.
311+ pub max_parallax_layer_count : f32 ,
234312}
235313
236314impl Default for StandardMaterial {
@@ -260,6 +338,10 @@ impl Default for StandardMaterial {
260338 fog_enabled : true ,
261339 alpha_mode : AlphaMode :: Opaque ,
262340 depth_bias : 0.0 ,
341+ depth_map : None ,
342+ parallax_depth_scale : 0.1 ,
343+ max_parallax_layer_count : 16.0 ,
344+ parallax_mapping_method : ParallaxMappingMethod :: Occlusion ,
263345 }
264346 }
265347}
@@ -302,6 +384,7 @@ bitflags::bitflags! {
302384 const TWO_COMPONENT_NORMAL_MAP = ( 1 << 6 ) ;
303385 const FLIP_NORMAL_MAP_Y = ( 1 << 7 ) ;
304386 const FOG_ENABLED = ( 1 << 8 ) ;
387+ const DEPTH_MAP = ( 1 << 9 ) ; // Used for parallax mapping
305388 const ALPHA_MODE_RESERVED_BITS = ( Self :: ALPHA_MODE_MASK_BITS << Self :: ALPHA_MODE_SHIFT_BITS ) ; // ← Bitmask reserving bits for the `AlphaMode`
306389 const ALPHA_MODE_OPAQUE = ( 0 << Self :: ALPHA_MODE_SHIFT_BITS ) ; // ← Values are just sequential values bitshifted into
307390 const ALPHA_MODE_MASK = ( 1 << Self :: ALPHA_MODE_SHIFT_BITS ) ; // the bitmask, and can range from 0 to 7.
@@ -341,6 +424,16 @@ pub struct StandardMaterialUniform {
341424 /// When the alpha mode mask flag is set, any base color alpha above this cutoff means fully opaque,
342425 /// and any below means fully transparent.
343426 pub alpha_cutoff : f32 ,
427+ /// The depth of the [`StandardMaterial::depth_map`] to apply.
428+ pub parallax_depth_scale : f32 ,
429+ /// In how many layers to split the depth maps for Steep parallax mapping.
430+ ///
431+ /// If your `parallax_depth_scale` is >0.1 and you are seeing jaggy edges,
432+ /// increase this value. However, this incurs a performance cost.
433+ pub max_parallax_layer_count : f32 ,
434+ /// Using [`ParallaxMappingMethod::Relief`], how many additional
435+ /// steps to use at most to find the depth value.
436+ pub max_relief_mapping_search_steps : u32 ,
344437}
345438
346439impl AsBindGroupShaderType < StandardMaterialUniform > for StandardMaterial {
@@ -367,6 +460,9 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
367460 if self . fog_enabled {
368461 flags |= StandardMaterialFlags :: FOG_ENABLED ;
369462 }
463+ if self . depth_map . is_some ( ) {
464+ flags |= StandardMaterialFlags :: DEPTH_MAP ;
465+ }
370466 let has_normal_map = self . normal_map_texture . is_some ( ) ;
371467 if has_normal_map {
372468 if let Some ( texture) = images. get ( self . normal_map_texture . as_ref ( ) . unwrap ( ) ) {
@@ -407,15 +503,20 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
407503 reflectance : self . reflectance ,
408504 flags : flags. bits ( ) ,
409505 alpha_cutoff,
506+ parallax_depth_scale : self . parallax_depth_scale ,
507+ max_parallax_layer_count : self . max_parallax_layer_count ,
508+ max_relief_mapping_search_steps : self . parallax_mapping_method . max_steps ( ) ,
410509 }
411510 }
412511}
413512
513+ /// The pipeline key for [`StandardMaterial`].
414514#[ derive( Clone , PartialEq , Eq , Hash ) ]
415515pub struct StandardMaterialKey {
416516 normal_map : bool ,
417517 cull_mode : Option < Face > ,
418518 depth_bias : i32 ,
519+ relief_mapping : bool ,
419520}
420521
421522impl From < & StandardMaterial > for StandardMaterialKey {
@@ -424,6 +525,10 @@ impl From<&StandardMaterial> for StandardMaterialKey {
424525 normal_map : material. normal_map_texture . is_some ( ) ,
425526 cull_mode : material. cull_mode ,
426527 depth_bias : material. depth_bias as i32 ,
528+ relief_mapping : matches ! (
529+ material. parallax_mapping_method,
530+ ParallaxMappingMethod :: Relief { .. }
531+ ) ,
427532 }
428533 }
429534}
@@ -435,11 +540,14 @@ impl Material for StandardMaterial {
435540 _layout : & MeshVertexBufferLayout ,
436541 key : MaterialPipelineKey < Self > ,
437542 ) -> Result < ( ) , SpecializedMeshPipelineError > {
438- if key. bind_group_data . normal_map {
439- if let Some ( fragment) = descriptor. fragment . as_mut ( ) {
440- fragment
441- . shader_defs
442- . push ( "STANDARDMATERIAL_NORMAL_MAP" . into ( ) ) ;
543+ if let Some ( fragment) = descriptor. fragment . as_mut ( ) {
544+ let shader_defs = & mut fragment. shader_defs ;
545+
546+ if key. bind_group_data . normal_map {
547+ shader_defs. push ( "STANDARDMATERIAL_NORMAL_MAP" . into ( ) ) ;
548+ }
549+ if key. bind_group_data . relief_mapping {
550+ shader_defs. push ( "RELIEF_MAPPING" . into ( ) ) ;
443551 }
444552 }
445553 descriptor. primitive . cull_mode = key. bind_group_data . cull_mode ;
0 commit comments