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