Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1#define_import_path bevy_pbr::pbr_functions
3#import bevy_pbr::{
4 pbr_types,
5 pbr_bindings,
6 mesh_view_bindings as view_bindings,
7 mesh_view_types,
8 lighting,
9 lighting::{LAYER_BASE, LAYER_CLEARCOAT},
10 transmission,
11 clustered_forward as clustering,
12 shadows,
13 ambient,
14 irradiance_volume,
15 mesh_types::{MESH_FLAGS_SHADOW_RECEIVER_BIT, MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT},
17#import bevy_render::maths::{E, powsafe}
19#ifdef MESHLET_MESH_MATERIAL_PASS
20#import bevy_pbr::meshlet_visibility_buffer_resolve::VertexOutput
21#else ifdef PREPASS_PIPELINE
22#import bevy_pbr::prepass_io::VertexOutput
23#else // PREPASS_PIPELINE
24#import bevy_pbr::forward_io::VertexOutput
25#endif // PREPASS_PIPELINE
27#ifdef ENVIRONMENT_MAP
28#import bevy_pbr::environment_map
29#endif
31#ifdef TONEMAP_IN_SHADER
32#import bevy_core_pipeline::tonemapping::{tone_mapping, screen_space_dither}
33#endif
36// Biasing info needed to sample from a texture. How this is done depends on
37// whether we're rendering meshlets or regular meshes.
38struct SampleBias {
39#ifdef MESHLET_MESH_MATERIAL_PASS
40 ddx_uv: vec2<f32>,
41 ddy_uv: vec2<f32>,
42#else // MESHLET_MESH_MATERIAL_PASS
43 mip_bias: f32,
44#endif // MESHLET_MESH_MATERIAL_PASS
47// This is the standard 4x4 ordered dithering pattern from [1].
48//
49// We can't use `array<vec4<u32>, 4>` because they can't be indexed dynamically
50// due to Naga limitations. So instead we pack into a single `vec4` and extract
51// individual bytes.
52//
53// [1]: https://en.wikipedia.org/wiki/Ordered_dithering#Threshold_map
54const DITHER_THRESHOLD_MAP: vec4<u32> = vec4(
55 0x0a020800,
56 0x060e040c,
57 0x09010b03,
58 0x050d070f
59);
61// Processes a visibility range dither value and discards the fragment if
62// needed.
63//
64// Visibility ranges, also known as HLODs, are crossfades between different
65// levels of detail.
66//
67// The `dither` value ranges from [-16, 16]. When zooming out, positive values
68// are used for meshes that are in the process of disappearing, while negative
69// values are used for meshes that are in the process of appearing. In other
70// words, when the camera is moving backwards, the `dither` value counts up from
71// -16 to 0 when the object is fading in, stays at 0 while the object is
72// visible, and then counts up to 16 while the object is fading out.
73// Distinguishing between negative and positive values allows the dither
74// patterns for different LOD levels of a single mesh to mesh together properly.
75#ifdef VISIBILITY_RANGE_DITHER
76fn visibility_range_dither(frag_coord: vec4<f32>, dither: i32) {
77 // If `dither` is 0, the object is visible.
78 if (dither == 0) {
79 return;
80 }
82 // If `dither` is less than -15 or greater than 15, the object is culled.
83 if (dither <= -16 || dither >= 16) {
84 discard;
85 }
87 // Otherwise, check the dither pattern.
88 let coords = vec2<u32>(floor(frag_coord.xy)) % 4u;
89 let threshold = i32((DITHER_THRESHOLD_MAP[coords.y] >> (coords.x * 8)) & 0xff);
90 if ((dither >= 0 && dither + threshold >= 16) || (dither < 0 && 1 + dither + threshold <= 0)) {
91 discard;
92 }
94#endif
96fn alpha_discard(material: pbr_types::StandardMaterial, output_color: vec4<f32>) -> vec4<f32> {
97 var color = output_color;
98 let alpha_mode = material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
99 if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
100 // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
101 color.a = 1.0;
102 }
104#ifdef MAY_DISCARD
105 // NOTE: `MAY_DISCARD` is only defined in the alpha to coverage case if MSAA
106 // was off. This special situation causes alpha to coverage to fall back to
107 // alpha mask.
108 else if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK ||
109 alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ALPHA_TO_COVERAGE {
110 if color.a >= material.alpha_cutoff {
111 // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
112 color.a = 1.0;
113 } else {
114 // NOTE: output_color.a < in.material.alpha_cutoff should not be rendered
115 discard;
116 }
117 }
118#endif
120 return color;
123fn prepare_world_normal(
124 world_normal: vec3<f32>,
125 double_sided: bool,
126 is_front: bool,
127) -> vec3<f32> {
128 var output: vec3<f32> = world_normal;
129#ifndef VERTEX_TANGENTS
130#ifndef STANDARD_MATERIAL_NORMAL_MAP
131 // NOTE: When NOT using normal-mapping, if looking at the back face of a double-sided
132 // material, the normal needs to be inverted. This is a branchless version of that.
133 output = (f32(!double_sided || is_front) * 2.0 - 1.0) * output;
134#endif
135#endif
136 return output;
139// Calculates the three TBN vectors according to [mikktspace]. Returns a matrix
140// with T, B, N columns in that order.
141//
142// [mikktspace]: http://www.mikktspace.com/
143fn calculate_tbn_mikktspace(world_normal: vec3<f32>, world_tangent: vec4<f32>) -> mat3x3<f32> {
144 // NOTE: The mikktspace method of normal mapping explicitly requires that the world normal NOT
145 // be re-normalized in the fragment shader. This is primarily to match the way mikktspace
146 // bakes vertex tangents and normal maps so that this is the exact inverse. Blender, Unity,
147 // Unreal Engine, Godot, and more all use the mikktspace method. Do not change this code
148 // unless you really know what you are doing.
149 // http://www.mikktspace.com/
150 var N: vec3<f32> = world_normal;
152 // NOTE: The mikktspace method of normal mapping explicitly requires that these NOT be
153 // normalized nor any Gram-Schmidt applied to ensure the vertex normal is orthogonal to the
154 // vertex tangent! Do not change this code unless you really know what you are doing.
155 // http://www.mikktspace.com/
156 var T: vec3<f32> = world_tangent.xyz;
157 var B: vec3<f32> = world_tangent.w * cross(N, T);
159#ifdef MESHLET_MESH_MATERIAL_PASS
160 // https://www.jeremyong.com/graphics/2023/12/16/surface-gradient-bump-mapping/#a-note-on-mikktspace-usage
161 let inverse_length_n = 1.0 / length(N);
162 T *= inverse_length_n;
163 B *= inverse_length_n;
164 N *= inverse_length_n;
165#endif
167 return mat3x3(T, B, N);
170fn apply_normal_mapping(
171 standard_material_flags: u32,
172 TBN: mat3x3<f32>,
173 double_sided: bool,
174 is_front: bool,
175 in_Nt: vec3<f32>,
176) -> vec3<f32> {
177 // Unpack the TBN vectors.
178 var T = TBN[0];
179 var B = TBN[1];
180 var N = TBN[2];
182 // Nt is the tangent-space normal.
183 var Nt = in_Nt;
184 if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
185 // Only use the xy components and derive z for 2-component normal maps.
186 Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
187 Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y);
188 } else {
189 Nt = Nt * 2.0 - 1.0;
190 }
191 // Normal maps authored for DirectX require flipping the y component
192 if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u {
193 Nt.y = -Nt.y;
194 }
196 if double_sided && !is_front {
197 Nt = -Nt;
198 }
200 // NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from
201 // the normal map texture in this way to be an EXACT inverse of how the normal map baker
202 // calculates the normal maps so there is no error introduced. Do not change this code
203 // unless you really know what you are doing.
204 // http://www.mikktspace.com/
205 N = Nt.x * T + Nt.y * B + Nt.z * N;
207 return normalize(N);
210#ifdef STANDARD_MATERIAL_ANISOTROPY
212// Modifies the normal to achieve a better approximate direction from the
213// environment map when using anisotropy.
214//
215// This follows the suggested implementation in the `KHR_materials_anisotropy` specification:
216// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md#image-based-lighting
217fn bend_normal_for_anisotropy(lighting_input: ptr<function, lighting::LightingInput>) {
218 // Unpack.
219 let N = (*lighting_input).layers[LAYER_BASE].N;
220 let roughness = (*lighting_input).layers[LAYER_BASE].roughness;
221 let V = (*lighting_input).V;
222 let anisotropy = (*lighting_input).anisotropy;
223 let Ba = (*lighting_input).Ba;
225 var bent_normal = normalize(cross(cross(Ba, V), Ba));
227 // The `KHR_materials_anisotropy` spec states:
228 //
229 // > This heuristic can probably be improved upon
230 let a = pow(2.0, pow(2.0, 1.0 - anisotropy * (1.0 - roughness)));
231 bent_normal = normalize(mix(bent_normal, N, a));
233 // The `KHR_materials_anisotropy` spec states:
234 //
235 // > Mixing the reflection with the normal is more accurate both with and
236 // > without anisotropy and keeps rough objects from gathering light from
237 // > behind their tangent plane.
238 let R = normalize(mix(reflect(-V, bent_normal), bent_normal, roughness * roughness));
240 (*lighting_input).layers[LAYER_BASE].N = bent_normal;
241 (*lighting_input).layers[LAYER_BASE].R = R;
244#endif // STANDARD_MATERIAL_ANISOTROPY
246// NOTE: Correctly calculates the view vector depending on whether
247// the projection is orthographic or perspective.
248fn calculate_view(
249 world_position: vec4<f32>,
250 is_orthographic: bool,
251) -> vec3<f32> {
252 var V: vec3<f32>;
253 if is_orthographic {
254 // Orthographic view vector
255 V = normalize(vec3<f32>(view_bindings::view.clip_from_world[0].z, view_bindings::view.clip_from_world[1].z, view_bindings::view.clip_from_world[2].z));
256 } else {
257 // Only valid for a perspective projection
258 V = normalize(view_bindings::view.world_position.xyz - world_position.xyz);
259 }
260 return V;
263// Diffuse strength is inversely related to metallicity, specular and diffuse transmission
264fn calculate_diffuse_color(
265 base_color: vec3<f32>,
266 metallic: f32,
267 specular_transmission: f32,
268 diffuse_transmission: f32
269) -> vec3<f32> {
270 return base_color * (1.0 - metallic) * (1.0 - specular_transmission) *
271 (1.0 - diffuse_transmission);
274// Remapping [0,1] reflectance to F0
275// See https://google.github.io/filament/Filament.html#materialsystem/parameterization/remapping
276fn calculate_F0(base_color: vec3<f32>, metallic: f32, reflectance: vec3<f32>) -> vec3<f32> {
277 return 0.16 * reflectance * reflectance * (1.0 - metallic) + base_color * metallic;
280#ifndef PREPASS_FRAGMENT
281fn apply_pbr_lighting(
282 in: pbr_types::PbrInput,
283) -> vec4<f32> {
284 var output_color: vec4<f32> = in.material.base_color;
286 let emissive = in.material.emissive;
288 // calculate non-linear roughness from linear perceptualRoughness
289 let metallic = in.material.metallic;
290 let perceptual_roughness = in.material.perceptual_roughness;
291 let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);
292 let ior = in.material.ior;
293 let thickness = in.material.thickness;
294 let reflectance = in.material.reflectance;
295 let diffuse_transmission = in.material.diffuse_transmission;
296 let specular_transmission = in.material.specular_transmission;
298 let specular_transmissive_color = specular_transmission * in.material.base_color.rgb;
300 let diffuse_occlusion = in.diffuse_occlusion;
301 let specular_occlusion = in.specular_occlusion;
303 // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
304 let NdotV = max(dot(in.N, in.V), 0.0001);
305 let R = reflect(-in.V, in.N);
307#ifdef STANDARD_MATERIAL_CLEARCOAT
308 // Do the above calculations again for the clearcoat layer. Remember that
309 // the clearcoat can have its own roughness and its own normal.
310 let clearcoat = in.material.clearcoat;
311 let clearcoat_perceptual_roughness = in.material.clearcoat_perceptual_roughness;
312 let clearcoat_roughness = lighting::perceptualRoughnessToRoughness(clearcoat_perceptual_roughness);
313 let clearcoat_N = in.clearcoat_N;
314 let clearcoat_NdotV = max(dot(clearcoat_N, in.V), 0.0001);
315 let clearcoat_R = reflect(-in.V, clearcoat_N);
316#endif // STANDARD_MATERIAL_CLEARCOAT
318 let diffuse_color = calculate_diffuse_color(
319 output_color.rgb,
320 metallic,
321 specular_transmission,
322 diffuse_transmission
323 );
325 // Diffuse transmissive strength is inversely related to metallicity and specular transmission, but directly related to diffuse transmission
326 let diffuse_transmissive_color = output_color.rgb * (1.0 - metallic) * (1.0 - specular_transmission) * diffuse_transmission;
328 // Calculate the world position of the second Lambertian lobe used for diffuse transmission, by subtracting material thickness
329 let diffuse_transmissive_lobe_world_position = in.world_position - vec4<f32>(in.world_normal, 0.0) * thickness;
331 let F0 = calculate_F0(output_color.rgb, metallic, reflectance);
332 let F_ab = lighting::F_AB(perceptual_roughness, NdotV);
334 var direct_light: vec3<f32> = vec3<f32>(0.0);
336 // Transmitted Light (Specular and Diffuse)
337 var transmitted_light: vec3<f32> = vec3<f32>(0.0);
339 // Pack all the values into a structure.
340 var lighting_input: lighting::LightingInput;
341 lighting_input.layers[LAYER_BASE].NdotV = NdotV;
342 lighting_input.layers[LAYER_BASE].N = in.N;
343 lighting_input.layers[LAYER_BASE].R = R;
344 lighting_input.layers[LAYER_BASE].perceptual_roughness = perceptual_roughness;
345 lighting_input.layers[LAYER_BASE].roughness = roughness;
346 lighting_input.P = in.world_position.xyz;
347 lighting_input.V = in.V;
348 lighting_input.diffuse_color = diffuse_color;
349 lighting_input.F0_ = F0;
350 lighting_input.F_ab = F_ab;
351#ifdef STANDARD_MATERIAL_CLEARCOAT
352 lighting_input.layers[LAYER_CLEARCOAT].NdotV = clearcoat_NdotV;
353 lighting_input.layers[LAYER_CLEARCOAT].N = clearcoat_N;
354 lighting_input.layers[LAYER_CLEARCOAT].R = clearcoat_R;
355 lighting_input.layers[LAYER_CLEARCOAT].perceptual_roughness = clearcoat_perceptual_roughness;
356 lighting_input.layers[LAYER_CLEARCOAT].roughness = clearcoat_roughness;
357 lighting_input.clearcoat_strength = clearcoat;
358#endif // STANDARD_MATERIAL_CLEARCOAT
359#ifdef STANDARD_MATERIAL_ANISOTROPY
360 lighting_input.anisotropy = in.anisotropy_strength;
361 lighting_input.Ta = in.anisotropy_T;
362 lighting_input.Ba = in.anisotropy_B;
363#endif // STANDARD_MATERIAL_ANISOTROPY
365 // And do the same for transmissive if we need to.
366#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
367 var transmissive_lighting_input: lighting::LightingInput;
368 transmissive_lighting_input.layers[LAYER_BASE].NdotV = 1.0;
369 transmissive_lighting_input.layers[LAYER_BASE].N = -in.N;
370 transmissive_lighting_input.layers[LAYER_BASE].R = vec3(0.0);
371 transmissive_lighting_input.layers[LAYER_BASE].perceptual_roughness = 1.0;
372 transmissive_lighting_input.layers[LAYER_BASE].roughness = 1.0;
373 transmissive_lighting_input.P = diffuse_transmissive_lobe_world_position.xyz;
374 transmissive_lighting_input.V = -in.V;
375 transmissive_lighting_input.diffuse_color = diffuse_transmissive_color;
376 transmissive_lighting_input.F0_ = vec3(0.0);
377 transmissive_lighting_input.F_ab = vec2(0.1);
378#ifdef STANDARD_MATERIAL_CLEARCOAT
379 transmissive_lighting_input.layers[LAYER_CLEARCOAT].NdotV = 0.0;
380 transmissive_lighting_input.layers[LAYER_CLEARCOAT].N = vec3(0.0);
381 transmissive_lighting_input.layers[LAYER_CLEARCOAT].R = vec3(0.0);
382 transmissive_lighting_input.layers[LAYER_CLEARCOAT].perceptual_roughness = 0.0;
383 transmissive_lighting_input.layers[LAYER_CLEARCOAT].roughness = 0.0;
384 transmissive_lighting_input.clearcoat_strength = 0.0;
385#endif // STANDARD_MATERIAL_CLEARCOAT
386#ifdef STANDARD_MATERIAL_ANISOTROPY
387 transmissive_lighting_input.anisotropy = in.anisotropy_strength;
388 transmissive_lighting_input.Ta = in.anisotropy_T;
389 transmissive_lighting_input.Ba = in.anisotropy_B;
390#endif // STANDARD_MATERIAL_ANISOTROPY
391#endif // STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
393 let view_z = dot(vec4<f32>(
394 view_bindings::view.view_from_world[0].z,
395 view_bindings::view.view_from_world[1].z,
396 view_bindings::view.view_from_world[2].z,
397 view_bindings::view.view_from_world[3].z
398 ), in.world_position);
399 let cluster_index = clustering::fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic);
400 var clusterable_object_index_ranges =
401 clustering::unpack_clusterable_object_index_ranges(cluster_index);
403 // Point lights (direct)
404 for (var i: u32 = clusterable_object_index_ranges.first_point_light_index_offset;
405 i < clusterable_object_index_ranges.first_spot_light_index_offset;
406 i = i + 1u) {
407 let light_id = clustering::get_clusterable_object_id(i);
409 // If we're lightmapped, disable diffuse contribution from the light if
410 // requested, to avoid double-counting light.
411#ifdef LIGHTMAP
412 let enable_diffuse =
413 (view_bindings::clusterable_objects.data[light_id].flags &
414 mesh_view_types::POINT_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT) != 0u;
415#else // LIGHTMAP
416 let enable_diffuse = true;
417#endif // LIGHTMAP
419 var shadow: f32 = 1.0;
420 if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
421 && (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
422 shadow = shadows::fetch_point_shadow(light_id, in.world_position, in.world_normal);
423 }
425 let light_contrib = lighting::point_light(light_id, &lighting_input, enable_diffuse);
426 direct_light += light_contrib * shadow;
428#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
429 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
430 // world position, inverted normal and view vectors, and the following simplified
431 // values for a fully diffuse transmitted light contribution approximation:
432 //
433 // roughness = 1.0;
434 // NdotV = 1.0;
435 // R = vec3<f32>(0.0) // doesn't really matter
436 // F_ab = vec2<f32>(0.1)
437 // F0 = vec3<f32>(0.0)
438 var transmitted_shadow: f32 = 1.0;
439 if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)
440 && (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
441 transmitted_shadow = shadows::fetch_point_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal);
442 }
444 let transmitted_light_contrib =
445 lighting::point_light(light_id, &transmissive_lighting_input, enable_diffuse);
446 transmitted_light += transmitted_light_contrib * transmitted_shadow;
447#endif
448 }
450 // Spot lights (direct)
451 for (var i: u32 = clusterable_object_index_ranges.first_spot_light_index_offset;
452 i < clusterable_object_index_ranges.first_reflection_probe_index_offset;
453 i = i + 1u) {
454 let light_id = clustering::get_clusterable_object_id(i);
456 // If we're lightmapped, disable diffuse contribution from the light if
457 // requested, to avoid double-counting light.
458#ifdef LIGHTMAP
459 let enable_diffuse =
460 (view_bindings::clusterable_objects.data[light_id].flags &
461 mesh_view_types::POINT_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT) != 0u;
462#else // LIGHTMAP
463 let enable_diffuse = true;
464#endif // LIGHTMAP
466 var shadow: f32 = 1.0;
467 if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
468 && (view_bindings::clusterable_objects.data[light_id].flags &
469 mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
470 shadow = shadows::fetch_spot_shadow(
471 light_id,
472 in.world_position,
473 in.world_normal,
474 view_bindings::clusterable_objects.data[light_id].shadow_map_near_z,
475 );
476 }
478 let light_contrib = lighting::spot_light(light_id, &lighting_input, enable_diffuse);
479 direct_light += light_contrib * shadow;
481#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
482 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
483 // world position, inverted normal and view vectors, and the following simplified
484 // values for a fully diffuse transmitted light contribution approximation:
485 //
486 // roughness = 1.0;
487 // NdotV = 1.0;
488 // R = vec3<f32>(0.0) // doesn't really matter
489 // F_ab = vec2<f32>(0.1)
490 // F0 = vec3<f32>(0.0)
491 var transmitted_shadow: f32 = 1.0;
492 if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)
493 && (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
494 transmitted_shadow = shadows::fetch_spot_shadow(
495 light_id,
496 diffuse_transmissive_lobe_world_position,
497 -in.world_normal,
498 view_bindings::clusterable_objects.data[light_id].shadow_map_near_z,
499 );
500 }
502 let transmitted_light_contrib =
503 lighting::spot_light(light_id, &transmissive_lighting_input, enable_diffuse);
504 transmitted_light += transmitted_light_contrib * transmitted_shadow;
505#endif
506 }
508 // directional lights (direct)
509 let n_directional_lights = view_bindings::lights.n_directional_lights;
510 for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
511 // check if this light should be skipped, which occurs if this light does not intersect with the view
512 // note point and spot lights aren't skippable, as the relevant lights are filtered in `assign_lights_to_clusters`
513 let light = &view_bindings::lights.directional_lights[i];
514 if (*light).skip != 0u {
515 continue;
516 }
518 // If we're lightmapped, disable diffuse contribution from the light if
519 // requested, to avoid double-counting light.
520#ifdef LIGHTMAP
521 let enable_diffuse =
522 ((*light).flags &
523 mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT) !=
524 0u;
525#else // LIGHTMAP
526 let enable_diffuse = true;
527#endif // LIGHTMAP
529 var shadow: f32 = 1.0;
530 if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
531 && (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
532 shadow = shadows::fetch_directional_shadow(i, in.world_position, in.world_normal, view_z);
533 }
535 var light_contrib = lighting::directional_light(i, &lighting_input, enable_diffuse);
537#ifdef DIRECTIONAL_LIGHT_SHADOW_MAP_DEBUG_CASCADES
538 light_contrib = shadows::cascade_debug_visualization(light_contrib, i, view_z);
539#endif
540 direct_light += light_contrib * shadow;
542#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
543 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
544 // world position, inverted normal and view vectors, and the following simplified
545 // values for a fully diffuse transmitted light contribution approximation:
546 //
547 // roughness = 1.0;
548 // NdotV = 1.0;
549 // R = vec3<f32>(0.0) // doesn't really matter
550 // F_ab = vec2<f32>(0.1)
551 // F0 = vec3<f32>(0.0)
552 var transmitted_shadow: f32 = 1.0;
553 if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)
554 && (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
555 transmitted_shadow = shadows::fetch_directional_shadow(i, diffuse_transmissive_lobe_world_position, -in.world_normal, view_z);
556 }
558 let transmitted_light_contrib =
559 lighting::directional_light(i, &transmissive_lighting_input, enable_diffuse);
560 transmitted_light += transmitted_light_contrib * transmitted_shadow;
561#endif
562 }
564#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
565 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
566 // world position, inverted normal and view vectors, and the following simplified
567 // values for a fully diffuse transmitted light contribution approximation:
568 //
569 // perceptual_roughness = 1.0;
570 // NdotV = 1.0;
571 // F0 = vec3<f32>(0.0)
572 // diffuse_occlusion = vec3<f32>(1.0)
573 transmitted_light += ambient::ambient_light(diffuse_transmissive_lobe_world_position, -in.N, -in.V, 1.0, diffuse_transmissive_color, vec3<f32>(0.0), 1.0, vec3<f32>(1.0));
574#endif
576 // Diffuse indirect lighting can come from a variety of sources. The
577 // priority goes like this:
578 //
579 // 1. Lightmap (highest)
580 // 2. Irradiance volume
581 // 3. Environment map (lowest)
582 //
583 // When we find a source of diffuse indirect lighting, we stop accumulating
584 // any more diffuse indirect light. This avoids double-counting if, for
585 // example, both lightmaps and irradiance volumes are present.
587 var indirect_light = vec3(0.0f);
588 var found_diffuse_indirect = false;
590#ifdef LIGHTMAP
591 indirect_light += in.lightmap_light * diffuse_color;
592 found_diffuse_indirect = true;
593#endif
595#ifdef IRRADIANCE_VOLUME
596 // Irradiance volume light (indirect)
597 if (!found_diffuse_indirect) {
598 let irradiance_volume_light = irradiance_volume::irradiance_volume_light(
599 in.world_position.xyz,
600 in.N,
601 &clusterable_object_index_ranges,
602 );
603 indirect_light += irradiance_volume_light * diffuse_color * diffuse_occlusion;
604 found_diffuse_indirect = true;
605 }
606#endif
608 // Environment map light (indirect)
609#ifdef ENVIRONMENT_MAP
611#ifdef STANDARD_MATERIAL_ANISOTROPY
612 var bent_normal_lighting_input = lighting_input;
613 bend_normal_for_anisotropy(&bent_normal_lighting_input);
614 let environment_map_lighting_input = &bent_normal_lighting_input;
615#else // STANDARD_MATERIAL_ANISOTROPY
616 let environment_map_lighting_input = &lighting_input;
617#endif // STANDARD_MATERIAL_ANISOTROPY
619 let environment_light = environment_map::environment_map_light(
620 environment_map_lighting_input,
621 &clusterable_object_index_ranges,
622 found_diffuse_indirect,
623 );
625 // If screen space reflections are going to be used for this material, don't
626 // accumulate environment map light yet. The SSR shader will do it.
627#ifdef SCREEN_SPACE_REFLECTIONS
628 let use_ssr = perceptual_roughness <=
629 view_bindings::ssr_settings.perceptual_roughness_threshold;
630#else // SCREEN_SPACE_REFLECTIONS
631 let use_ssr = false;
632#endif // SCREEN_SPACE_REFLECTIONS
634 if (!use_ssr) {
635 let environment_light = environment_map::environment_map_light(
636 &lighting_input,
637 &clusterable_object_index_ranges,
638 found_diffuse_indirect
639 );
641 indirect_light += environment_light.diffuse * diffuse_occlusion +
642 environment_light.specular * specular_occlusion;
643 }
645#endif // ENVIRONMENT_MAP
647 // Ambient light (indirect)
648 indirect_light += ambient::ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, diffuse_occlusion);
650 // we'll use the specular component of the transmitted environment
651 // light in the call to `specular_transmissive_light()` below
652 var specular_transmitted_environment_light = vec3<f32>(0.0);
654#ifdef ENVIRONMENT_MAP
656#ifdef STANDARD_MATERIAL_DIFFUSE_OR_SPECULAR_TRANSMISSION
657 // NOTE: We use the diffuse transmissive color, inverted normal and view vectors,
658 // and the following simplified values for the transmitted environment light contribution
659 // approximation:
660 //
661 // diffuse_color = vec3<f32>(1.0) // later we use `diffuse_transmissive_color` and `specular_transmissive_color`
662 // NdotV = 1.0;
663 // R = T // see definition below
664 // F0 = vec3<f32>(1.0)
665 // diffuse_occlusion = 1.0
666 //
667 // (This one is slightly different from the other light types above, because the environment
668 // map light returns both diffuse and specular components separately, and we want to use both)
670 let T = -normalize(
671 in.V + // start with view vector at entry point
672 refract(in.V, -in.N, 1.0 / ior) * thickness // add refracted vector scaled by thickness, towards exit point
673 ); // normalize to find exit point view vector
675 var transmissive_environment_light_input: lighting::LightingInput;
676 transmissive_environment_light_input.diffuse_color = vec3(1.0);
677 transmissive_environment_light_input.layers[LAYER_BASE].NdotV = 1.0;
678 transmissive_environment_light_input.P = in.world_position.xyz;
679 transmissive_environment_light_input.layers[LAYER_BASE].N = -in.N;
680 transmissive_environment_light_input.V = in.V;
681 transmissive_environment_light_input.layers[LAYER_BASE].R = T;
682 transmissive_environment_light_input.layers[LAYER_BASE].perceptual_roughness = perceptual_roughness;
683 transmissive_environment_light_input.layers[LAYER_BASE].roughness = roughness;
684 transmissive_environment_light_input.F0_ = vec3<f32>(1.0);
685 transmissive_environment_light_input.F_ab = vec2(0.1);
686#ifdef STANDARD_MATERIAL_CLEARCOAT
687 // No clearcoat.
688 transmissive_environment_light_input.clearcoat_strength = 0.0;
689 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].NdotV = 0.0;
690 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].N = in.N;
691 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].R = vec3(0.0);
692 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].perceptual_roughness = 0.0;
693 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].roughness = 0.0;
694#endif // STANDARD_MATERIAL_CLEARCOAT
696 let transmitted_environment_light = environment_map::environment_map_light(
697 &transmissive_environment_light_input,
698 &clusterable_object_index_ranges,
699 false,
700 );
702#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
703 transmitted_light += transmitted_environment_light.diffuse * diffuse_transmissive_color;
704#endif // STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
705#ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION
706 specular_transmitted_environment_light = transmitted_environment_light.specular * specular_transmissive_color;
707#endif // STANDARD_MATERIAL_SPECULAR_TRANSMISSION
709#endif // STANDARD_MATERIAL_SPECULAR_OR_DIFFUSE_TRANSMISSION
711#endif // ENVIRONMENT_MAP
713 var emissive_light = emissive.rgb * output_color.a;
715 // "The clearcoat layer is on top of emission in the layering stack.
716 // Consequently, the emission is darkened by the Fresnel term."
717 //
718 // <https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md#emission>
719#ifdef STANDARD_MATERIAL_CLEARCOAT
720 emissive_light = emissive_light * (0.04 + (1.0 - 0.04) * pow(1.0 - clearcoat_NdotV, 5.0));
721#endif
723 emissive_light = emissive_light * mix(1.0, view_bindings::view.exposure, emissive.a);
725#ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION
726 transmitted_light += transmission::specular_transmissive_light(in.world_position, in.frag_coord.xyz, view_z, in.N, in.V, F0, ior, thickness, perceptual_roughness, specular_transmissive_color, specular_transmitted_environment_light).rgb;
728 if (in.material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ATTENUATION_ENABLED_BIT) != 0u {
729 // We reuse the `atmospheric_fog()` function here, as it's fundamentally
730 // equivalent to the attenuation that takes place inside the material volume,
731 // and will allow us to eventually hook up subsurface scattering more easily
732 var attenuation_fog: mesh_view_types::Fog;
733 attenuation_fog.base_color.a = 1.0;
734 attenuation_fog.be = pow(1.0 - in.material.attenuation_color.rgb, vec3<f32>(E)) / in.material.attenuation_distance;
735 // TODO: Add the subsurface scattering factor below
736 // attenuation_fog.bi = /* ... */
737 transmitted_light = bevy_pbr::fog::atmospheric_fog(
738 attenuation_fog, vec4<f32>(transmitted_light, 1.0), thickness,
739 vec3<f32>(0.0) // TODO: Pass in (pre-attenuated) scattered light contribution here
740 ).rgb;
741 }
742#endif
744 // Total light
745 output_color = vec4<f32>(
746 (view_bindings::view.exposure * (transmitted_light + direct_light + indirect_light)) + emissive_light,
747 output_color.a
748 );
750 output_color = clustering::cluster_debug_visualization(
751 output_color,
752 view_z,
753 in.is_orthographic,
754 clusterable_object_index_ranges,
755 cluster_index,
756 );
758 return output_color;
760#endif // PREPASS_FRAGMENT
762#ifdef DISTANCE_FOG
763fn apply_fog(fog_params: mesh_view_types::Fog, input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
764 let view_to_world = fragment_world_position.xyz - view_world_position.xyz;
766 // `length()` is used here instead of just `view_to_world.z` since that produces more
767 // high quality results, especially for denser/smaller fogs. we get a "curved"
768 // fog shape that remains consistent with camera rotation, instead of a "linear"
769 // fog shape that looks a bit fake
770 let distance = length(view_to_world);
772 var scattering = vec3<f32>(0.0);
773 if fog_params.directional_light_color.a > 0.0 {
774 let view_to_world_normalized = view_to_world / distance;
775 let n_directional_lights = view_bindings::lights.n_directional_lights;
776 for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
777 let light = view_bindings::lights.directional_lights[i];
778 scattering += pow(
779 max(
780 dot(view_to_world_normalized, light.direction_to_light),
781 0.0
782 ),
783 fog_params.directional_light_exponent
784 ) * light.color.rgb * view_bindings::view.exposure;
785 }
786 }
788 if fog_params.mode == mesh_view_types::FOG_MODE_LINEAR {
789 return bevy_pbr::fog::linear_fog(fog_params, input_color, distance, scattering);
790 } else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL {
791 return bevy_pbr::fog::exponential_fog(fog_params, input_color, distance, scattering);
792 } else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL_SQUARED {
793 return bevy_pbr::fog::exponential_squared_fog(fog_params, input_color, distance, scattering);
794 } else if fog_params.mode == mesh_view_types::FOG_MODE_ATMOSPHERIC {
795 return bevy_pbr::fog::atmospheric_fog(fog_params, input_color, distance, scattering);
796 } else {
797 return input_color;
798 }
800#endif // DISTANCE_FOG
802#ifdef PREMULTIPLY_ALPHA
803fn premultiply_alpha(standard_material_flags: u32, color: vec4<f32>) -> vec4<f32> {
804// `Blend`, `Premultiplied` and `Alpha` all share the same `BlendState`. Depending
805// on the alpha mode, we premultiply the color channels by the alpha channel value,
806// (and also optionally replace the alpha value with 0.0) so that the result produces
807// the desired blend mode when sent to the blending operation.
808#ifdef BLEND_PREMULTIPLIED_ALPHA
809 // For `BlendState::PREMULTIPLIED_ALPHA_BLENDING` the blend function is:
810 //
811 // result = 1 * src_color + (1 - src_alpha) * dst_color
812 let alpha_mode = standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
813 if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD {
814 // Here, we premultiply `src_color` by `src_alpha`, and replace `src_alpha` with 0.0:
815 //
816 // src_color *= src_alpha
817 // src_alpha = 0.0
818 //
819 // We end up with:
820 //
821 // result = 1 * (src_alpha * src_color) + (1 - 0) * dst_color
822 // result = src_alpha * src_color + 1 * dst_color
823 //
824 // Which is the blend operation for additive blending
825 return vec4<f32>(color.rgb * color.a, 0.0);
826 } else {
827 // Here, we don't do anything, so that we get premultiplied alpha blending. (As expected)
828 return color.rgba;
829 }
830#endif
831// `Multiply` uses its own `BlendState`, but we still need to premultiply here in the
832// shader so that we get correct results as we tweak the alpha channel
833#ifdef BLEND_MULTIPLY
834 // The blend function is:
835 //
836 // result = dst_color * src_color + (1 - src_alpha) * dst_color
837 //
838 // We premultiply `src_color` by `src_alpha`:
839 //
840 // src_color *= src_alpha
841 //
842 // We end up with:
843 //
844 // result = dst_color * (src_color * src_alpha) + (1 - src_alpha) * dst_color
845 // result = src_alpha * (src_color * dst_color) + (1 - src_alpha) * dst_color
846 //
847 // Which is the blend operation for multiplicative blending with arbitrary mixing
848 // controlled by the source alpha channel
849 return vec4<f32>(color.rgb * color.a, color.a);
850#endif
852#endif
854// fog, alpha premultiply
855// for non-hdr cameras, tonemapping and debanding
856fn main_pass_post_lighting_processing(
857 pbr_input: pbr_types::PbrInput,
858 input_color: vec4<f32>,
859) -> vec4<f32> {
860 var output_color = input_color;
862#ifdef DISTANCE_FOG
863 // fog
864 if ((pbr_input.material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
865 output_color = apply_fog(view_bindings::fog, output_color, pbr_input.world_position.xyz, view_bindings::view.world_position.xyz);
866 }
867#endif // DISTANCE_FOG
869#ifdef TONEMAP_IN_SHADER
870 output_color = tone_mapping(output_color, view_bindings::view.color_grading);
871#ifdef DEBAND_DITHER
872 var output_rgb = output_color.rgb;
873 output_rgb = powsafe(output_rgb, 1.0 / 2.2);
874 output_rgb += screen_space_dither(pbr_input.frag_coord.xy);
875 // This conversion back to linear space is required because our output texture format is
876 // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
877 output_rgb = powsafe(output_rgb, 2.2);
878 output_color = vec4(output_rgb, output_color.a);
879#endif
880#endif
881#ifdef PREMULTIPLY_ALPHA
882 output_color = premultiply_alpha(pbr_input.material.flags, output_color);
883#endif
884 return output_color;
Contents