
# Objective Add a [parallax mapping] shader to bevy. Please note that this is a 3d technique, NOT a 2d sidescroller feature. ## Solution - Add related fields to `StandardMaterial` - update the pbr shader - Add an example taking advantage of parallax mapping A pre-existing implementation exists at: https://github.com/nicopap/bevy_mod_paramap/ The implementation is derived from: https://web.archive.org/web/20150419215321/http://sunandblackcat.com/tipFullView.php?l=eng&topicid=28 Further discussion on literature is found in the `bevy_mod_paramap` README. ### Limitations - The mesh silhouette isn't affected by the depth map. - The depth of the pixel does not reflect its visual position, resulting in artifacts for depth-dependent features such as fog or SSAO - GLTF does not define a height map texture, so somehow the user will always need to work around this limitation, though [an extension is in the works][gltf] ### Future work - It's possible to update the depth in the depth buffer to follow the parallaxed texture. This would enable interop with depth-based visual effects, it also allows `discard`ing pixels of materials when computed depth is higher than the one in depth buffer - Cheap lower quality single-sample method using [offset limiting] - Add distance fading, to disable parallaxing (relatively expensive) on distant objects - GLTF extension to allow defining height maps. Or a workaround implemented through a blender plugin to the GLTF exporter that uses the `extras` field to add height map. - [Quadratic surface vertex attributes][oliveira_3] to enable parallax mapping on bending surfaces and allow clean silhouetting. - noise based sampling, to limit the pancake artifacts. - Cone mapping ([GPU gems], [Simcity (2013)][simcity]). Requires preprocessing, increase depth map size, reduces sample count greatly. - [Quadtree parallax mapping][qpm] (also requires preprocessing) - Self-shadowing of parallax-mapped surfaces by modifying the shadow map - Generate depth map from normal map [link to slides], [blender question] https://user-images.githubusercontent.com/26321040/223563792-dffcc6ab-70e8-4ff9-90d1-b36c338695ad.mp4 [blender question]: https://blender.stackexchange.com/questions/89278/how-to-get-a-smooth-curvature-map-from-a-normal-map [link to slides]: https://developer.download.nvidia.com/assets/gamedev/docs/nmap2displacement.pdf [oliveira_3]: https://www.inf.ufrgs.br/~oliveira/pubs_files/Oliveira_Policarpo_RP-351_Jan_2005.pdf [GPU gems]: https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-18-relaxed-cone-stepping-relief-mapping [simcity]: https://community.simtropolis.com/omnibus/other-games/building-and-rendering-simcity-2013-r247/ [offset limiting]: https://raw.githubusercontent.com/marcusstenbeck/tncg14-parallax-mapping/master/documents/Parallax%20Mapping%20with%20Offset%20Limiting%20-%20A%20Per-Pixel%20Approximation%20of%20Uneven%20Surfaces.pdf [gltf]: https://github.com/KhronosGroup/glTF/pull/2196 [qpm]: https://www.gamedevs.org/uploads/quadtree-displacement-mapping-with-height-blending.pdf --- ## Changelog - Add a `depth_map` field to the `StandardMaterial`, it is a grayscale image where white represents bottom and black the top. If `depth_map` is set, bevy's pbr shader will use it to do [parallax mapping] to give an increased feel of depth to the material. This is similar to a displacement map, but with infinite precision at fairly low cost. - The fields `parallax_mapping_method`, `parallax_depth_scale` and `max_parallax_layer_count` allow finer grained control over the behavior of the parallax shader. - Add the `parallax_mapping` example to show off the effect. [parallax mapping]: https://en.wikipedia.org/wiki/Parallax_mapping --------- Co-authored-by: Robert Swain <robert.swain@gmail.com>
46 lines
1.7 KiB
Rust
46 lines
1.7 KiB
Rust
use bevy_reflect::{FromReflect, Reflect};
|
|
|
|
/// The [parallax mapping] method to use to compute depth based on the
|
|
/// material's [`depth_map`].
|
|
///
|
|
/// Parallax Mapping uses a depth map texture to give the illusion of depth
|
|
/// variation on a mesh surface that is geometrically flat.
|
|
///
|
|
/// See the `parallax_mapping.wgsl` shader code for implementation details
|
|
/// and explanation of the methods used.
|
|
///
|
|
/// [`depth_map`]: crate::StandardMaterial::depth_map
|
|
/// [parallax mapping]: https://en.wikipedia.org/wiki/Parallax_mapping
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Reflect, FromReflect)]
|
|
pub enum ParallaxMappingMethod {
|
|
/// A simple linear interpolation, using a single texture sample.
|
|
///
|
|
/// This method is named "Parallax Occlusion Mapping".
|
|
///
|
|
/// Unlike [`ParallaxMappingMethod::Relief`], only requires a single lookup,
|
|
/// but may skip small details and result in writhing material artifacts.
|
|
#[default]
|
|
Occlusion,
|
|
/// Discovers the best depth value based on binary search.
|
|
///
|
|
/// Each iteration incurs a texture sample.
|
|
/// The result has fewer visual artifacts than [`ParallaxMappingMethod::Occlusion`].
|
|
///
|
|
/// This method is named "Relief Mapping".
|
|
Relief {
|
|
/// How many additional steps to use at most to find the depth value.
|
|
max_steps: u32,
|
|
},
|
|
}
|
|
impl ParallaxMappingMethod {
|
|
/// [`ParallaxMappingMethod::Relief`] with a 5 steps, a reasonable default.
|
|
pub const DEFAULT_RELIEF_MAPPING: Self = ParallaxMappingMethod::Relief { max_steps: 5 };
|
|
|
|
pub(crate) fn max_steps(&self) -> u32 {
|
|
match self {
|
|
ParallaxMappingMethod::Occlusion => 0,
|
|
ParallaxMappingMethod::Relief { max_steps } => *max_steps,
|
|
}
|
|
}
|
|
}
|