1#define_import_path bevy_pbr::parallax_mapping3#import bevy_pbr::pbr_bindings::{depth_map_texture, depth_map_sampler}5fn sample_depth_map(uv: vec2<f32>) -> f32 {6 // We use `textureSampleLevel` over `textureSample` because the wgpu DX127 // backend (Fxc) panics when using "gradient instructions" inside a loop.8 // It results in the whole loop being unrolled by the shader compiler,9 // which it can't do because the upper limit of the loop in steep parallax10 // mapping is a variable set by the user.11 // The "gradient instructions" comes from `textureSample` computing MIP level12 // based on UV derivative. With `textureSampleLevel`, we provide ourselves13 // the MIP level, so no gradient instructions are used, and we can use14 // sample_depth_map in our loop.15 // See https://stackoverflow.com/questions/56581141/direct3d11-gradient-instruction-used-in-a-loop-with-varying-iteration-forcing16 return textureSampleLevel(depth_map_texture, depth_map_sampler, uv, 0.0).r;17}19// An implementation of parallax mapping, see https://en.wikipedia.org/wiki/Parallax_mapping20// Code derived from: https://web.archive.org/web/20150419215321/http://sunandblackcat.com/tipFullView.php?l=eng&topicid=2821fn parallaxed_uv(22 depth_scale: f32,23 max_layer_count: f32,24 max_steps: u32,25 // The original interpolated uv26 original_uv: vec2<f32>,27 // The vector from the camera to the fragment at the surface in tangent space28 Vt: vec3<f32>,29) -> vec2<f32> {30 if max_layer_count < 1.0 {31 return original_uv;32 }33 var uv = original_uv;35 // Steep Parallax Mapping36 // ======================37 // Split the depth map into `layer_count` layers.38 // When Vt hits the surface of the mesh (excluding depth displacement),39 // if the depth is not below or on surface including depth displacement (textureSample), then40 // look forward (+= delta_uv) on depth texture according to41 // Vt and distance between hit surface and depth map surface,42 // repeat until below the surface.43 //44 // Where `layer_count` is interpolated between `1.0` and45 // `max_layer_count` according to the steepness of Vt.47 let view_steepness = abs(Vt.z);48 // We mix with minimum value 1.0 because otherwise,49 // with 0.0, we get a division by zero in surfaces parallel to viewport,50 // resulting in a singularity.51 let layer_count = mix(max_layer_count, 1.0, view_steepness);52 let layer_depth = 1.0 / layer_count;53 var delta_uv = depth_scale * layer_depth * Vt.xy * vec2(1.0, -1.0) / view_steepness;55 var current_layer_depth = 0.0;56 var texture_depth = sample_depth_map(uv);58 // texture_depth > current_layer_depth means the depth map depth is deeper59 // than the depth the ray would be at this UV offset so the ray has not60 // intersected the surface61 for (var i: i32 = 0; texture_depth > current_layer_depth && i <= i32(layer_count); i++) {62 current_layer_depth += layer_depth;63 uv += delta_uv;64 texture_depth = sample_depth_map(uv);65 }67#ifdef RELIEF_MAPPING68 // Relief Mapping69 // ==============70 // "Refine" the rough result from Steep Parallax Mapping71 // with a **binary search** between the layer selected by steep parallax72 // and the next one to find a point closer to the depth map surface.73 // This reduces the jaggy step artifacts from steep parallax mapping.75 delta_uv *= 0.5;76 var delta_depth = 0.5 * layer_depth;78 uv -= delta_uv;79 current_layer_depth -= delta_depth;81 for (var i: u32 = 0u; i < max_steps; i++) {82 texture_depth = sample_depth_map(uv);84 // Halve the deltas for the next step85 delta_uv *= 0.5;86 delta_depth *= 0.5;88 // Step based on whether the current depth is above or below the depth map89 if (texture_depth > current_layer_depth) {90 uv += delta_uv;91 current_layer_depth += delta_depth;92 } else {93 uv -= delta_uv;94 current_layer_depth -= delta_depth;95 }96 }97#else98 // Parallax Occlusion mapping99 // ==========================100 // "Refine" Steep Parallax Mapping by interpolating between the101 // previous layer's depth and the computed layer depth.102 // Only requires a single lookup, unlike Relief Mapping, but103 // may skip small details and result in writhing material artifacts.104 let previous_uv = uv - delta_uv;105 let next_depth = texture_depth - current_layer_depth;106 let previous_depth = sample_depth_map(previous_uv) - current_layer_depth + layer_depth;108 let weight = next_depth / (next_depth - previous_depth);110 uv = mix(uv, previous_uv, weight);112 current_layer_depth += mix(next_depth, previous_depth, weight);113#endif115 // Note: `current_layer_depth` is not returned, but may be useful116 // for light computation later on in future improvements of the pbr shader.117 return uv;118}