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 clustered_forward as clustering,
11 shadows,
12 ambient,
13 view_transformations,
14 raymarch,
15 utils,
16 mesh_types::{MESH_FLAGS_SHADOW_RECEIVER_BIT, MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT},
18#import bevy_pbr::mesh_view_bindings::globals
19#import bevy_pbr::view_transformations::{position_world_to_ndc}
20#import bevy_render::maths::{E, powsafe}
22#ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION
23#import bevy_pbr::transmission
24#endif
26#ifdef IRRADIANCE_VOLUME
27#import bevy_pbr::irradiance_volume
28#endif
30#ifdef MESHLET_MESH_MATERIAL_PASS
31#import bevy_pbr::meshlet_visibility_buffer_resolve::VertexOutput
32#else ifdef PREPASS_PIPELINE
33#import bevy_pbr::prepass_io::VertexOutput
34#else // PREPASS_PIPELINE
35#import bevy_pbr::forward_io::VertexOutput
36#endif // PREPASS_PIPELINE
38#ifdef ENVIRONMENT_MAP
39#import bevy_pbr::environment_map
40#endif
42#ifdef TONEMAP_IN_SHADER
43#import bevy_core_pipeline::tonemapping::{tone_mapping, screen_space_dither}
44#endif
47// Biasing info needed to sample from a texture. How this is done depends on
48// whether we're rendering meshlets or regular meshes.
49struct SampleBias {
50#ifdef MESHLET_MESH_MATERIAL_PASS
51 ddx_uv: vec2<f32>,
52 ddy_uv: vec2<f32>,
53#else // MESHLET_MESH_MATERIAL_PASS
54 mip_bias: f32,
55#endif // MESHLET_MESH_MATERIAL_PASS
58// This is the standard 4x4 ordered dithering pattern from [1].
59//
60// We can't use `array<vec4<u32>, 4>` because they can't be indexed dynamically
61// due to Naga limitations. So instead we pack into a single `vec4` and extract
62// individual bytes.
63//
64// [1]: https://en.wikipedia.org/wiki/Ordered_dithering#Threshold_map
65const DITHER_THRESHOLD_MAP: vec4<u32> = vec4(
66 0x0a020800,
67 0x060e040c,
68 0x09010b03,
69 0x050d070f
70);
72// Processes a visibility range dither value and discards the fragment if
73// needed.
74//
75// Visibility ranges, also known as HLODs, are crossfades between different
76// levels of detail.
77//
78// The `dither` value ranges from [-16, 16]. When zooming out, positive values
79// are used for meshes that are in the process of disappearing, while negative
80// values are used for meshes that are in the process of appearing. In other
81// words, when the camera is moving backwards, the `dither` value counts up from
82// -16 to 0 when the object is fading in, stays at 0 while the object is
83// visible, and then counts up to 16 while the object is fading out.
84// Distinguishing between negative and positive values allows the dither
85// patterns for different LOD levels of a single mesh to mesh together properly.
86#ifdef VISIBILITY_RANGE_DITHER
87fn visibility_range_dither(frag_coord: vec4<f32>, dither: i32) {
88 // If `dither` is 0, the object is visible.
89 if (dither == 0) {
90 return;
91 }
93 // If `dither` is less than -15 or greater than 15, the object is culled.
94 if (dither <= -16 || dither >= 16) {
95 discard;
96 }
98 // Otherwise, check the dither pattern.
99 let coords = vec2<u32>(floor(frag_coord.xy)) % 4u;
100 let threshold = i32((DITHER_THRESHOLD_MAP[coords.y] >> (coords.x * 8)) & 0xff);
101 if ((dither >= 0 && dither + threshold >= 16) || (dither < 0 && 1 + dither + threshold <= 0)) {
102 discard;
103 }
105#endif
107fn alpha_discard(material: pbr_types::StandardMaterial, output_color: vec4<f32>) -> vec4<f32> {
108 var color = output_color;
109 let alpha_mode = material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
110 if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
111 // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
112 color.a = 1.0;
113 }
115#ifdef MAY_DISCARD
116 // NOTE: `MAY_DISCARD` is only defined in the alpha to coverage case if MSAA
117 // was off. This special situation causes alpha to coverage to fall back to
118 // alpha mask.
119 else if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK ||
120 alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ALPHA_TO_COVERAGE {
121 if color.a >= material.alpha_cutoff {
122 // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
123 color.a = 1.0;
124 } else {
125 // NOTE: output_color.a < in.material.alpha_cutoff should not be rendered
126 discard;
127 }
128 }
129#endif
131 return color;
134fn prepare_world_normal(
135 world_normal: vec3<f32>,
136 double_sided: bool,
137 is_front: bool,
138) -> vec3<f32> {
139 var output: vec3<f32> = world_normal;
140#ifndef VERTEX_TANGENTS
141#ifndef STANDARD_MATERIAL_NORMAL_MAP
142 // NOTE: When NOT using normal-mapping, if looking at the back face of a double-sided
143 // material, the normal needs to be inverted. This is a branchless version of that.
144 output = (f32(!double_sided || is_front) * 2.0 - 1.0) * output;
145#endif
146#endif
147 return output;
150// Calculates the three TBN vectors according to [mikktspace]. Returns a matrix
151// with T, B, N columns in that order.
152//
153// [mikktspace]: http://www.mikktspace.com/
154fn calculate_tbn_mikktspace(world_normal: vec3<f32>, world_tangent: vec4<f32>) -> mat3x3<f32> {
155 // NOTE: The mikktspace method of normal mapping explicitly requires that the world normal NOT
156 // be re-normalized in the fragment shader. This is primarily to match the way mikktspace
157 // bakes vertex tangents and normal maps so that this is the exact inverse. Blender, Unity,
158 // Unreal Engine, Godot, and more all use the mikktspace method. Do not change this code
159 // unless you really know what you are doing.
160 // http://www.mikktspace.com/
161 var N: vec3<f32> = world_normal;
163 // NOTE: The mikktspace method of normal mapping explicitly requires that these NOT be
164 // normalized nor any Gram-Schmidt applied to ensure the vertex normal is orthogonal to the
165 // vertex tangent! Do not change this code unless you really know what you are doing.
166 // http://www.mikktspace.com/
167 var T: vec3<f32> = world_tangent.xyz;
168 var B: vec3<f32> = world_tangent.w * cross(N, T);
170#ifdef MESHLET_MESH_MATERIAL_PASS
171 // https://www.jeremyong.com/graphics/2023/12/16/surface-gradient-bump-mapping/#a-note-on-mikktspace-usage
172 let inverse_length_n = 1.0 / length(N);
173 T *= inverse_length_n;
174 B *= inverse_length_n;
175 N *= inverse_length_n;
176#endif
178 return mat3x3(T, B, N);
181fn apply_normal_mapping(
182 standard_material_flags: u32,
183 TBN: mat3x3<f32>,
184 double_sided: bool,
185 is_front: bool,
186 in_Nt: vec3<f32>,
187) -> vec3<f32> {
188 // Unpack the TBN vectors.
189 var T = TBN[0];
190 var B = TBN[1];
191 var N = TBN[2];
193 // Nt is the tangent-space normal.
194 var Nt = in_Nt;
195 if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
196 // Only use the xy components and derive z for 2-component normal maps.
197 Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
198 Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y);
199 } else {
200 Nt = Nt * 2.0 - 1.0;
201 }
202 // Normal maps authored for DirectX require flipping the y component
203 if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u {
204 Nt.y = -Nt.y;
205 }
207 if double_sided && !is_front {
208 Nt = -Nt;
209 }
211 // NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from
212 // the normal map texture in this way to be an EXACT inverse of how the normal map baker
213 // calculates the normal maps so there is no error introduced. Do not change this code
214 // unless you really know what you are doing.
215 // http://www.mikktspace.com/
216 N = Nt.x * T + Nt.y * B + Nt.z * N;
218 return normalize(N);
221#ifdef STANDARD_MATERIAL_ANISOTROPY
223// Modifies the normal to achieve a better approximate direction from the
224// environment map when using anisotropy.
225//
226// This follows the suggested implementation in the `KHR_materials_anisotropy` specification:
227// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md#image-based-lighting
228fn bend_normal_for_anisotropy(lighting_input: ptr<function, lighting::LightingInput>) {
229 // Unpack.
230 let N = (*lighting_input).layers[LAYER_BASE].N;
231 let roughness = (*lighting_input).layers[LAYER_BASE].roughness;
232 let V = (*lighting_input).V;
233 let anisotropy = (*lighting_input).anisotropy;
234 let Ba = (*lighting_input).Ba;
236 var bent_normal = normalize(cross(cross(Ba, V), Ba));
238 // The `KHR_materials_anisotropy` spec states:
239 //
240 // > This heuristic can probably be improved upon
241 let a = pow(2.0, pow(2.0, 1.0 - anisotropy * (1.0 - roughness)));
242 bent_normal = normalize(mix(bent_normal, N, a));
244 // The `KHR_materials_anisotropy` spec states:
245 //
246 // > Mixing the reflection with the normal is more accurate both with and
247 // > without anisotropy and keeps rough objects from gathering light from
248 // > behind their tangent plane.
249 let R = normalize(mix(reflect(-V, bent_normal), bent_normal, roughness * roughness));
251 (*lighting_input).layers[LAYER_BASE].N = bent_normal;
252 (*lighting_input).layers[LAYER_BASE].R = R;
255#endif // STANDARD_MATERIAL_ANISOTROPY
257// NOTE: Correctly calculates the view vector depending on whether
258// the projection is orthographic or perspective.
259fn calculate_view(
260 world_position: vec4<f32>,
261 is_orthographic: bool,
262) -> vec3<f32> {
263 var V: vec3<f32>;
264 if is_orthographic {
265 // Orthographic view vector
266 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));
267 } else {
268 // Only valid for a perspective projection
269 V = normalize(view_bindings::view.world_position.xyz - world_position.xyz);
270 }
271 return V;
274// Diffuse strength is inversely related to metallicity, specular and diffuse transmission
275fn calculate_diffuse_color(
276 base_color: vec3<f32>,
277 metallic: f32,
278 specular_transmission: f32,
279 diffuse_transmission: f32
280) -> vec3<f32> {
281 return base_color * (1.0 - metallic) * (1.0 - specular_transmission) *
282 (1.0 - diffuse_transmission);
285// Remapping [0,1] reflectance to F0 for dielectrics
286fn calculate_F0_dielectric(reflectance: vec3<f32>) -> vec3<f32> {
287 return 0.16 * reflectance * reflectance;
290// Remapping [0,1] reflectance to F0
291// See https://google.github.io/filament/Filament.md.html#materialsystem/parameterization/remapping
292fn calculate_F0(base_color: vec3<f32>, metallic: f32, reflectance: vec3<f32>) -> vec3<f32> {
293 return mix(calculate_F0_dielectric(reflectance), base_color, metallic);
296#ifdef CONTACT_SHADOWS
297#ifdef DEPTH_PREPASS
298fn calculate_contact_shadow(
299 world_position: vec3<f32>,
300 frag_coord: vec2<f32>,
301 light_dir: vec3<f32>,
302 contact_shadow_steps: u32,
303) -> f32 {
304#ifdef BLUE_NOISE_TEXTURE
305 let noise_size = textureDimensions(view_bindings::blue_noise_texture, 0);
306 let noise_layers = textureNumLayers(view_bindings::blue_noise_texture);
307 let noise = textureLoad(
308 view_bindings::blue_noise_texture,
309 vec2<i32>(frag_coord) % vec2<i32>(noise_size),
310 i32(view_bindings::globals.frame_count % noise_layers),
311 0
312 ).x;
313#else
314 let noise = utils::interleaved_gradient_noise(frag_coord, view_bindings::globals.frame_count);
315#endif
317 let depth_size = vec2<f32>(textureDimensions(view_bindings::depth_prepass_texture));
318 var rm = raymarch::depth_ray_march_new_from_depth(depth_size);
319 raymarch::depth_ray_march_from_cs(&rm, position_world_to_ndc(world_position));
320 raymarch::depth_ray_march_to_ws(&rm, world_position + light_dir * view_bindings::contact_shadows_settings.length);
321 rm.linear_steps = contact_shadow_steps;
322 rm.depth_thickness_linear_z = view_bindings::contact_shadows_settings.thickness;
323 rm.march_behind_surfaces = true;
324 rm.jitter = noise;
326 let rm_result = raymarch::depth_ray_march_march(&rm);
327 if rm_result.hit {
328 return clamp((rm_result.hit_penetration_frac - 0.5) / (1.0 - 0.5), 0.0, 1.0);
329 }
330 return 1.0;
332#endif
333#endif
335#ifndef PREPASS_FRAGMENT
336fn apply_pbr_lighting(
337 in: pbr_types::PbrInput,
338) -> vec4<f32> {
339 var output_color: vec4<f32> = in.material.base_color;
341 let emissive = in.material.emissive;
343 // calculate non-linear roughness from linear perceptualRoughness
344 let metallic = in.material.metallic;
345 let perceptual_roughness = in.material.perceptual_roughness;
346 let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);
347 let ior = in.material.ior;
348 let thickness = in.material.thickness;
349 let reflectance = in.material.reflectance;
350 let diffuse_transmission = in.material.diffuse_transmission;
351 let specular_transmission = in.material.specular_transmission;
353 let specular_transmissive_color = specular_transmission * in.material.base_color.rgb;
355 let diffuse_occlusion = in.diffuse_occlusion;
356 let specular_occlusion = in.specular_occlusion;
358 // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
359 let NdotV = max(dot(in.N, in.V), 0.0001);
360 let R = reflect(-in.V, in.N);
362#ifdef STANDARD_MATERIAL_CLEARCOAT
363 // Do the above calculations again for the clearcoat layer. Remember that
364 // the clearcoat can have its own roughness and its own normal.
365 let clearcoat = in.material.clearcoat;
366 let clearcoat_perceptual_roughness = in.material.clearcoat_perceptual_roughness;
367 let clearcoat_roughness = lighting::perceptualRoughnessToRoughness(clearcoat_perceptual_roughness);
368 let clearcoat_N = in.clearcoat_N;
369 let clearcoat_NdotV = max(dot(clearcoat_N, in.V), 0.0001);
370 let clearcoat_R = reflect(-in.V, clearcoat_N);
371#endif // STANDARD_MATERIAL_CLEARCOAT
373 let diffuse_color = calculate_diffuse_color(
374 output_color.rgb,
375 metallic,
376 specular_transmission,
377 diffuse_transmission
378 );
380 // Diffuse transmissive strength is inversely related to metallicity and specular transmission, but directly related to diffuse transmission
381 let diffuse_transmissive_color = output_color.rgb * (1.0 - metallic) * (1.0 - specular_transmission) * diffuse_transmission;
383 // Calculate the world position of the second Lambertian lobe used for diffuse transmission, by subtracting material thickness
384 let diffuse_transmissive_lobe_world_position = in.world_position - vec4<f32>(in.world_normal, 0.0) * thickness;
386 let F0 = calculate_F0(output_color.rgb, metallic, reflectance);
387 let F_ab = lighting::F_AB(perceptual_roughness, NdotV);
389 var direct_light: vec3<f32> = vec3<f32>(0.0);
391 // Transmitted Light (Specular and Diffuse)
392 var transmitted_light: vec3<f32> = vec3<f32>(0.0);
394 // Pack all the values into a structure.
395 var lighting_input: lighting::LightingInput;
396 lighting_input.layers[LAYER_BASE].NdotV = NdotV;
397 lighting_input.layers[LAYER_BASE].N = in.N;
398 lighting_input.layers[LAYER_BASE].R = R;
399 lighting_input.layers[LAYER_BASE].perceptual_roughness = perceptual_roughness;
400 lighting_input.layers[LAYER_BASE].roughness = roughness;
401 lighting_input.P = in.world_position.xyz;
402 lighting_input.V = in.V;
403 lighting_input.diffuse_color = diffuse_color;
404 lighting_input.metallic = metallic;
405 lighting_input.F0_dielectric = calculate_F0_dielectric(reflectance);
406 lighting_input.F0_metallic = output_color.rgb;
407 lighting_input.F_ab = F_ab;
408#ifdef STANDARD_MATERIAL_CLEARCOAT
409 lighting_input.layers[LAYER_CLEARCOAT].NdotV = clearcoat_NdotV;
410 lighting_input.layers[LAYER_CLEARCOAT].N = clearcoat_N;
411 lighting_input.layers[LAYER_CLEARCOAT].R = clearcoat_R;
412 lighting_input.layers[LAYER_CLEARCOAT].perceptual_roughness = clearcoat_perceptual_roughness;
413 lighting_input.layers[LAYER_CLEARCOAT].roughness = clearcoat_roughness;
414 lighting_input.clearcoat_strength = clearcoat;
415#endif // STANDARD_MATERIAL_CLEARCOAT
416#ifdef STANDARD_MATERIAL_ANISOTROPY
417 lighting_input.anisotropy = in.anisotropy_strength;
418 lighting_input.Ta = in.anisotropy_T;
419 lighting_input.Ba = in.anisotropy_B;
420#endif // STANDARD_MATERIAL_ANISOTROPY
422 // And do the same for transmissive if we need to.
423#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
424 var transmissive_lighting_input: lighting::LightingInput;
425 transmissive_lighting_input.layers[LAYER_BASE].NdotV = 1.0;
426 transmissive_lighting_input.layers[LAYER_BASE].N = -in.N;
427 transmissive_lighting_input.layers[LAYER_BASE].R = vec3(0.0);
428 transmissive_lighting_input.layers[LAYER_BASE].perceptual_roughness = 1.0;
429 transmissive_lighting_input.layers[LAYER_BASE].roughness = 1.0;
430 transmissive_lighting_input.P = diffuse_transmissive_lobe_world_position.xyz;
431 transmissive_lighting_input.V = -in.V;
432 transmissive_lighting_input.diffuse_color = diffuse_transmissive_color;
433 transmissive_lighting_input.metallic = 0.0;
434 transmissive_lighting_input.F0_dielectric = vec3(0.0);
435 transmissive_lighting_input.F0_metallic = vec3(0.0);
436 transmissive_lighting_input.F_ab = vec2(0.1);
437#ifdef STANDARD_MATERIAL_CLEARCOAT
438 transmissive_lighting_input.layers[LAYER_CLEARCOAT].NdotV = 0.0;
439 transmissive_lighting_input.layers[LAYER_CLEARCOAT].N = vec3(0.0);
440 transmissive_lighting_input.layers[LAYER_CLEARCOAT].R = vec3(0.0);
441 transmissive_lighting_input.layers[LAYER_CLEARCOAT].perceptual_roughness = 0.0;
442 transmissive_lighting_input.layers[LAYER_CLEARCOAT].roughness = 0.0;
443 transmissive_lighting_input.clearcoat_strength = 0.0;
444#endif // STANDARD_MATERIAL_CLEARCOAT
445#ifdef STANDARD_MATERIAL_ANISOTROPY
446 transmissive_lighting_input.anisotropy = in.anisotropy_strength;
447 transmissive_lighting_input.Ta = in.anisotropy_T;
448 transmissive_lighting_input.Ba = in.anisotropy_B;
449#endif // STANDARD_MATERIAL_ANISOTROPY
450#endif // STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
452 let view_z = dot(vec4<f32>(
453 view_bindings::view.view_from_world[0].z,
454 view_bindings::view.view_from_world[1].z,
455 view_bindings::view.view_from_world[2].z,
456 view_bindings::view.view_from_world[3].z
457 ), in.world_position);
458 let cluster_index = clustering::view_fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic);
459 var clusterable_object_index_ranges =
460 clustering::unpack_clusterable_object_index_ranges(cluster_index);
462#ifdef CONTACT_SHADOWS
463 let contact_shadow_steps = view_bindings::contact_shadows_settings.linear_steps;
464 let contact_shadow_enabled = contact_shadow_steps > 0u;
465#endif
467 // Point lights (direct)
468 for (var i: u32 = clusterable_object_index_ranges.first_point_light_index_offset;
469 i < clusterable_object_index_ranges.first_spot_light_index_offset;
470 i = i + 1u) {
471 let light_id = clustering::get_clusterable_object_id(i);
473 // If we're lightmapped, disable diffuse contribution from the light if
474 // requested, to avoid double-counting light.
475#ifdef LIGHTMAP
476 let enable_diffuse =
477 (view_bindings::clustered_lights.data[light_id].flags &
478 mesh_view_types::POINT_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT) != 0u;
479#else // LIGHTMAP
480 let enable_diffuse = true;
481#endif // LIGHTMAP
483 var shadow: f32 = 1.0;
484 if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
485 && (view_bindings::clustered_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
486 shadow = shadows::fetch_point_shadow(light_id, in.world_position, in.world_normal, in.frag_coord.xy);
487 }
489#ifdef CONTACT_SHADOWS
490#ifdef DEPTH_PREPASS
491 if contact_shadow_enabled && (in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u && shadow > 0.0 &&
492 (view_bindings::clustered_lights.data[light_id].flags &
493 mesh_view_types::POINT_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT) != 0u {
494 let L = normalize(view_bindings::clustered_lights.data[light_id].position_radius.xyz - in.world_position.xyz);
495 shadow *= calculate_contact_shadow(in.world_position.xyz, in.frag_coord.xy, L, contact_shadow_steps);
496 }
497#endif
498#endif
500 let light_contrib = lighting::point_light(light_id, &lighting_input, enable_diffuse, true);
501 direct_light += light_contrib * shadow;
503#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
504 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
505 // world position, inverted normal and view vectors, and the following simplified
506 // values for a fully diffuse transmitted light contribution approximation:
507 //
508 // roughness = 1.0;
509 // NdotV = 1.0;
510 // R = vec3<f32>(0.0) // doesn't really matter
511 // F_ab = vec2<f32>(0.1)
512 // F0 = vec3<f32>(0.0)
513 var transmitted_shadow: f32 = 1.0;
514 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)
515 && (view_bindings::clustered_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
516 transmitted_shadow = shadows::fetch_point_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal, in.frag_coord.xy);
517 }
519 let transmitted_light_contrib =
520 lighting::point_light(light_id, &transmissive_lighting_input, enable_diffuse, true);
521 transmitted_light += transmitted_light_contrib * transmitted_shadow;
522#endif
523 }
525 // Spot lights (direct)
526 for (var i: u32 = clusterable_object_index_ranges.first_spot_light_index_offset;
527 i < clusterable_object_index_ranges.first_reflection_probe_index_offset;
528 i = i + 1u) {
529 let light_id = clustering::get_clusterable_object_id(i);
531 // If we're lightmapped, disable diffuse contribution from the light if
532 // requested, to avoid double-counting light.
533#ifdef LIGHTMAP
534 let enable_diffuse =
535 (view_bindings::clustered_lights.data[light_id].flags &
536 mesh_view_types::POINT_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT) != 0u;
537#else // LIGHTMAP
538 let enable_diffuse = true;
539#endif // LIGHTMAP
541 var shadow: f32 = 1.0;
542 if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
543 && (view_bindings::clustered_lights.data[light_id].flags &
544 mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
545 shadow = shadows::fetch_spot_shadow(
546 light_id,
547 in.world_position,
548 in.world_normal,
549 view_bindings::clustered_lights.data[light_id].shadow_map_near_z,
550 in.frag_coord.xy,
551 );
552 }
554#ifdef CONTACT_SHADOWS
555#ifdef DEPTH_PREPASS
556 if contact_shadow_enabled && (in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u && shadow > 0.0 &&
557 (view_bindings::clustered_lights.data[light_id].flags &
558 mesh_view_types::POINT_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT) != 0u {
559 let L = normalize(view_bindings::clustered_lights.data[light_id].position_radius.xyz - in.world_position.xyz);
560 shadow *= calculate_contact_shadow(in.world_position.xyz, in.frag_coord.xy, L, contact_shadow_steps);
561 }
562#endif
563#endif
565 let light_contrib = lighting::spot_light(light_id, &lighting_input, enable_diffuse);
566 direct_light += light_contrib * shadow;
568#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
569 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
570 // world position, inverted normal and view vectors, and the following simplified
571 // values for a fully diffuse transmitted light contribution approximation:
572 //
573 // roughness = 1.0;
574 // NdotV = 1.0;
575 // R = vec3<f32>(0.0) // doesn't really matter
576 // F_ab = vec2<f32>(0.1)
577 // F0 = vec3<f32>(0.0)
578 var transmitted_shadow: f32 = 1.0;
579 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)
580 && (view_bindings::clustered_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
581 transmitted_shadow = shadows::fetch_spot_shadow(
582 light_id,
583 diffuse_transmissive_lobe_world_position,
584 -in.world_normal,
585 view_bindings::clustered_lights.data[light_id].shadow_map_near_z,
586 in.frag_coord.xy,
587 );
588 }
590 let transmitted_light_contrib =
591 lighting::spot_light(light_id, &transmissive_lighting_input, enable_diffuse);
592 transmitted_light += transmitted_light_contrib * transmitted_shadow;
593#endif
594 }
596 // directional lights (direct)
597 let n_directional_lights = view_bindings::lights.n_directional_lights;
598 for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
599 // check if this light should be skipped, which occurs if this light does not intersect with the view
600 // note point and spot lights aren't skippable, as the relevant lights are filtered in `assign_lights_to_clusters`
601 let light = &view_bindings::lights.directional_lights[i];
603 // If we're lightmapped, disable diffuse contribution from the light if
604 // requested, to avoid double-counting light.
605#ifdef LIGHTMAP
606 let enable_diffuse =
607 ((*light).flags &
608 mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT) !=
609 0u;
610#else // LIGHTMAP
611 let enable_diffuse = true;
612#endif // LIGHTMAP
614 var shadow: f32 = 1.0;
615 if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
616 && (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
617 shadow = shadows::fetch_directional_shadow(i, in.world_position, in.world_normal, view_z, in.frag_coord.xy);
618 }
620#ifdef CONTACT_SHADOWS
621#ifdef DEPTH_PREPASS
622 if contact_shadow_enabled && (in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u && shadow > 0.0 &&
623 (view_bindings::lights.directional_lights[i].flags &
624 mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT) != 0u {
625 let L = view_bindings::lights.directional_lights[i].direction_to_light;
626 shadow *= calculate_contact_shadow(in.world_position.xyz, in.frag_coord.xy, L, contact_shadow_steps);
627 }
628#endif
629#endif
631 var light_contrib = lighting::directional_light(i, &lighting_input, enable_diffuse);
633#ifdef DIRECTIONAL_LIGHT_SHADOW_MAP_DEBUG_CASCADES
634 light_contrib = shadows::cascade_debug_visualization(light_contrib, i, view_z);
635#endif
636 direct_light += light_contrib * shadow;
638#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
639 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
640 // world position, inverted normal and view vectors, and the following simplified
641 // values for a fully diffuse transmitted light contribution approximation:
642 //
643 // roughness = 1.0;
644 // NdotV = 1.0;
645 // R = vec3<f32>(0.0) // doesn't really matter
646 // F_ab = vec2<f32>(0.1)
647 // F0 = vec3<f32>(0.0)
648 var transmitted_shadow: f32 = 1.0;
649 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)
650 && (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
651 transmitted_shadow = shadows::fetch_directional_shadow(i, diffuse_transmissive_lobe_world_position, -in.world_normal, view_z, in.frag_coord.xy);
652 }
654 let transmitted_light_contrib =
655 lighting::directional_light(i, &transmissive_lighting_input, enable_diffuse);
656 transmitted_light += transmitted_light_contrib * transmitted_shadow;
657#endif
658 }
660#ifdef AREA_LIGHT_LUTS
661 // Rect lights
662 let n_rect_lights = view_bindings::lights.n_rect_lights;
663 for (var i: u32 = 0u; i < n_rect_lights; i = i + 1u) {
664 let enable_diffuse = true;
665 let light_contrib = lighting::rect_light(i, &lighting_input, enable_diffuse);
666 direct_light += light_contrib;
668 #ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
669 let transmitted_light_contrib =
670 lighting::rect_light(i, &transmissive_lighting_input, enable_diffuse);
671 transmitted_light += transmitted_light_contrib;
672 #endif
673 }
674#endif
676#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
677 // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated
678 // world position, inverted normal and view vectors, and the following simplified
679 // values for a fully diffuse transmitted light contribution approximation:
680 //
681 // perceptual_roughness = 1.0;
682 // NdotV = 1.0;
683 // F0 = vec3<f32>(0.0)
684 // diffuse_occlusion = vec3<f32>(1.0)
685 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));
686#endif
688 // Diffuse indirect lighting can come from a variety of sources. The
689 // priority goes like this:
690 //
691 // 1. Lightmap (highest)
692 // 2. Irradiance volume
693 // 3. Environment map (lowest)
694 //
695 // When we find a source of diffuse indirect lighting, we stop accumulating
696 // any more diffuse indirect light. This avoids double-counting if, for
697 // example, both lightmaps and irradiance volumes are present.
699 var indirect_light = vec3(0.0f);
700 var found_diffuse_indirect = false;
702#ifdef LIGHTMAP
703 indirect_light += in.lightmap_light * diffuse_color;
704 found_diffuse_indirect = true;
705#endif
707#ifdef IRRADIANCE_VOLUME
708 // Irradiance volume light (indirect)
709 if (!found_diffuse_indirect) {
710 let irradiance_volume_light = irradiance_volume::irradiance_volume_light(
711 in.world_position.xyz,
712 in.N,
713 &clusterable_object_index_ranges,
714 );
715 indirect_light += irradiance_volume_light * diffuse_color * diffuse_occlusion;
716 found_diffuse_indirect = true;
717 }
718#endif
720 // Environment map light (indirect)
721#ifdef ENVIRONMENT_MAP
722 // If screen space reflections are going to be used for this material, only
723 // accumulate the diffuse part of the environment map light. The SSR shader
724 // will accumulate the specular part (including the environment map fallback
725 // if SSR misses).
726#ifdef SCREEN_SPACE_REFLECTIONS
727 let use_ssr = perceptual_roughness <= view_bindings::ssr_settings.max_perceptual_roughness
728 && perceptual_roughness >= view_bindings::ssr_settings.min_perceptual_roughness;
729#else // SCREEN_SPACE_REFLECTIONS
730 let use_ssr = false;
731#endif // SCREEN_SPACE_REFLECTIONS
733#ifdef STANDARD_MATERIAL_ANISOTROPY
734 var bent_normal_lighting_input = lighting_input;
735 bend_normal_for_anisotropy(&bent_normal_lighting_input);
736 let environment_map_lighting_input = &bent_normal_lighting_input;
737#else // STANDARD_MATERIAL_ANISOTROPY
738 let environment_map_lighting_input = &lighting_input;
739#endif // STANDARD_MATERIAL_ANISOTROPY
741 let environment_light = environment_map::environment_map_light(
742 environment_map_lighting_input,
743 &clusterable_object_index_ranges,
744 found_diffuse_indirect,
745 );
747 indirect_light += environment_light.diffuse * diffuse_occlusion;
748 if (!use_ssr) {
749 indirect_light += environment_light.specular * specular_occlusion;
750 }
751#endif // ENVIRONMENT_MAP
753 // Ambient light (indirect)
754 // If we are lightmapped, disable the ambient contribution if requested.
755 // This is to avoid double-counting ambient light. (It might be part of the lightmap)
756#ifdef LIGHTMAP
757 let enable_ambient = view_bindings::lights.ambient_light_affects_lightmapped_meshes != 0u;
758#else // LIGHTMAP
759 let enable_ambient = true;
760#endif // LIGHTMAP
761 if (enable_ambient) {
762 indirect_light += ambient::ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, diffuse_occlusion);
763 }
765 // we'll use the specular component of the transmitted environment
766 // light in the call to `specular_transmissive_light()` below
767 var specular_transmitted_environment_light = vec3<f32>(0.0);
769#ifdef ENVIRONMENT_MAP
771#ifdef STANDARD_MATERIAL_DIFFUSE_OR_SPECULAR_TRANSMISSION
772 // NOTE: We use the diffuse transmissive color, inverted normal and view vectors,
773 // and the following simplified values for the transmitted environment light contribution
774 // approximation:
775 //
776 // diffuse_color = vec3<f32>(1.0) // later we use `diffuse_transmissive_color` and `specular_transmissive_color`
777 // NdotV = 1.0;
778 // R = T // see definition below
779 // F0 = vec3<f32>(1.0) (using F0_dielectric = 1, F0_metallic = 0 and metallic = 0)
780 // diffuse_occlusion = 1.0
781 //
782 // (This one is slightly different from the other light types above, because the environment
783 // map light returns both diffuse and specular components separately, and we want to use both)
785 let T = -normalize(
786 in.V + // start with view vector at entry point
787 refract(in.V, -in.N, 1.0 / ior) * thickness // add refracted vector scaled by thickness, towards exit point
788 ); // normalize to find exit point view vector
790 var transmissive_environment_light_input: lighting::LightingInput;
791 transmissive_environment_light_input.diffuse_color = vec3(1.0);
792 transmissive_environment_light_input.layers[LAYER_BASE].NdotV = 1.0;
793 transmissive_environment_light_input.P = in.world_position.xyz;
794 transmissive_environment_light_input.layers[LAYER_BASE].N = -in.N;
795 transmissive_environment_light_input.V = in.V;
796 transmissive_environment_light_input.layers[LAYER_BASE].R = T;
797 transmissive_environment_light_input.layers[LAYER_BASE].perceptual_roughness = perceptual_roughness;
798 transmissive_environment_light_input.layers[LAYER_BASE].roughness = roughness;
799 transmissive_environment_light_input.metallic = 0.0;
800 transmissive_environment_light_input.F0_dielectric = vec3<f32>(1.0);
801 transmissive_environment_light_input.F0_metallic = vec3<f32>(0.0);
802 transmissive_environment_light_input.F_ab = vec2(0.1);
803#ifdef STANDARD_MATERIAL_CLEARCOAT
804 // No clearcoat.
805 transmissive_environment_light_input.clearcoat_strength = 0.0;
806 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].NdotV = 0.0;
807 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].N = in.N;
808 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].R = vec3(0.0);
809 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].perceptual_roughness = 0.0;
810 transmissive_environment_light_input.layers[LAYER_CLEARCOAT].roughness = 0.0;
811#endif // STANDARD_MATERIAL_CLEARCOAT
813 let transmitted_environment_light = environment_map::environment_map_light(
814 &transmissive_environment_light_input,
815 &clusterable_object_index_ranges,
816 false,
817 );
819#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
820 transmitted_light += transmitted_environment_light.diffuse * diffuse_transmissive_color;
821#endif // STANDARD_MATERIAL_DIFFUSE_TRANSMISSION
822#ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION
823 specular_transmitted_environment_light = transmitted_environment_light.specular * specular_transmissive_color;
824#endif // STANDARD_MATERIAL_SPECULAR_TRANSMISSION
826#endif // STANDARD_MATERIAL_SPECULAR_OR_DIFFUSE_TRANSMISSION
828#endif // ENVIRONMENT_MAP
830 var emissive_light = emissive.rgb * output_color.a;
832 // "The clearcoat layer is on top of emission in the layering stack.
833 // Consequently, the emission is darkened by the Fresnel term."
834 //
835 // <https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md#emission>
836#ifdef STANDARD_MATERIAL_CLEARCOAT
837 emissive_light = emissive_light * (0.04 + (1.0 - 0.04) * pow(1.0 - clearcoat_NdotV, 5.0));
838#endif
840 emissive_light = emissive_light * mix(1.0, view_bindings::view.exposure, emissive.a);
842#ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION
843 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;
845 if (in.material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ATTENUATION_ENABLED_BIT) != 0u {
846 // We reuse the `atmospheric_fog()` function here, as it's fundamentally
847 // equivalent to the attenuation that takes place inside the material volume,
848 // and will allow us to eventually hook up subsurface scattering more easily
849 var attenuation_fog: mesh_view_types::Fog;
850 attenuation_fog.base_color.a = 1.0;
851 attenuation_fog.be = pow(1.0 - in.material.attenuation_color.rgb, vec3<f32>(E)) / in.material.attenuation_distance;
852 // TODO: Add the subsurface scattering factor below
853 // attenuation_fog.bi = /* ... */
854 transmitted_light = bevy_pbr::fog::atmospheric_fog(
855 attenuation_fog, vec4<f32>(transmitted_light, 1.0), thickness,
856 vec3<f32>(0.0) // TODO: Pass in (pre-attenuated) scattered light contribution here
857 ).rgb;
858 }
859#endif
861 // Total light
862 output_color = vec4<f32>(
863 (view_bindings::view.exposure * (transmitted_light + direct_light + indirect_light)) + emissive_light,
864 output_color.a
865 );
867 output_color = clustering::cluster_debug_visualization(
868 output_color,
869 view_z,
870 in.is_orthographic,
871 clusterable_object_index_ranges,
872 cluster_index,
873 );
875 return output_color;
877#endif // PREPASS_FRAGMENT
879#ifdef DISTANCE_FOG
880fn apply_fog(
881 fog_params: mesh_view_types::Fog,
882 input_color: vec4<f32>,
883 fragment_world_position: vec3<f32>,
884 view_world_position: vec3<f32>,
885 frag_coord_xy: vec2<f32>,
886) -> vec4<f32> {
887 let view_to_world = fragment_world_position.xyz - view_world_position.xyz;
889 // `length()` is used here instead of just `view_to_world.z` since that produces more
890 // high quality results, especially for denser/smaller fogs. we get a "curved"
891 // fog shape that remains consistent with camera rotation, instead of a "linear"
892 // fog shape that looks a bit fake
893 let distance = length(view_to_world);
895 // Calculate view_z for shadow cascade selection
896 let view_pos = view_transformations::position_world_to_view(fragment_world_position);
897 let view_z = view_pos.z;
899 // Approximate surface normal using view direction for shadow sampling
900 let view_direction_normal = normalize(-view_to_world);
901 let fragment_world_position_vec4 = vec4<f32>(fragment_world_position, 1.0);
903 var scattering = vec3<f32>(0.0);
904 if fog_params.directional_light_color.a > 0.0 {
905 let view_to_world_normalized = view_to_world / distance;
906 let n_directional_lights = view_bindings::lights.n_directional_lights;
907 for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
908 let light = view_bindings::lights.directional_lights[i];
909 let scattering_contribution = pow(
910 max(
911 dot(view_to_world_normalized, light.direction_to_light),
912 0.0
913 ),
914 fog_params.directional_light_exponent
915 ) * light.color.rgb * view_bindings::view.exposure;
917 // Sample shadow map to attenuate inscattering in shadowed areas
918 var shadow: f32 = 1.0;
919 if ((light.flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
920 shadow = shadows::fetch_directional_shadow(i, fragment_world_position_vec4, view_direction_normal, view_z, frag_coord_xy);
921 }
922 scattering += scattering_contribution * shadow;
923 }
924 }
926 if fog_params.mode == mesh_view_types::FOG_MODE_LINEAR {
927 return bevy_pbr::fog::linear_fog(fog_params, input_color, distance, scattering);
928 } else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL {
929 return bevy_pbr::fog::exponential_fog(fog_params, input_color, distance, scattering);
930 } else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL_SQUARED {
931 return bevy_pbr::fog::exponential_squared_fog(fog_params, input_color, distance, scattering);
932 } else if fog_params.mode == mesh_view_types::FOG_MODE_ATMOSPHERIC {
933 return bevy_pbr::fog::atmospheric_fog(fog_params, input_color, distance, scattering);
934 } else {
935 return input_color;
936 }
938#endif // DISTANCE_FOG
940#ifdef PREMULTIPLY_ALPHA
941fn premultiply_alpha(standard_material_flags: u32, color: vec4<f32>) -> vec4<f32> {
942// `Blend`, `Premultiplied` and `Alpha` all share the same `BlendState`. Depending
943// on the alpha mode, we premultiply the color channels by the alpha channel value,
944// (and also optionally replace the alpha value with 0.0) so that the result produces
945// the desired blend mode when sent to the blending operation.
946#ifdef BLEND_PREMULTIPLIED_ALPHA
947 // For `BlendState::PREMULTIPLIED_ALPHA_BLENDING` the blend function is:
948 //
949 // result = 1 * src_color + (1 - src_alpha) * dst_color
950 let alpha_mode = standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
951 if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD {
952 // Here, we premultiply `src_color` by `src_alpha`, and replace `src_alpha` with 0.0:
953 //
954 // src_color *= src_alpha
955 // src_alpha = 0.0
956 //
957 // We end up with:
958 //
959 // result = 1 * (src_alpha * src_color) + (1 - 0) * dst_color
960 // result = src_alpha * src_color + 1 * dst_color
961 //
962 // Which is the blend operation for additive blending
963 return vec4<f32>(color.rgb * color.a, 0.0);
964 } else {
965 // Here, we don't do anything, so that we get premultiplied alpha blending. (As expected)
966 return color.rgba;
967 }
968#endif
969// `Multiply` uses its own `BlendState`, but we still need to premultiply here in the
970// shader so that we get correct results as we tweak the alpha channel
971#ifdef BLEND_MULTIPLY
972 // The blend function is:
973 //
974 // result = dst_color * src_color + (1 - src_alpha) * dst_color
975 //
976 // We premultiply `src_color` by `src_alpha`:
977 //
978 // src_color *= src_alpha
979 //
980 // We end up with:
981 //
982 // result = dst_color * (src_color * src_alpha) + (1 - src_alpha) * dst_color
983 // result = src_alpha * (src_color * dst_color) + (1 - src_alpha) * dst_color
984 //
985 // Which is the blend operation for multiplicative blending with arbitrary mixing
986 // controlled by the source alpha channel
987 return vec4<f32>(color.rgb * color.a, color.a);
988#endif
990#endif
992// fog, alpha premultiply
993// for non-hdr cameras, tonemapping and debanding
994fn main_pass_post_lighting_processing(
995 pbr_input: pbr_types::PbrInput,
996 input_color: vec4<f32>,
997) -> vec4<f32> {
998 var output_color = input_color;
1000#ifdef DISTANCE_FOG
1001 // fog
1002 if ((pbr_input.material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
1003 output_color = apply_fog(
1004 view_bindings::fog,
1005 output_color,
1006 pbr_input.world_position.xyz,
1007 view_bindings::view.world_position.xyz,
1008 pbr_input.frag_coord.xy,
1009 );
1011#endif // DISTANCE_FOG
1013#ifdef TONEMAP_IN_SHADER
1014 output_color = tone_mapping(output_color, view_bindings::view.color_grading);
1015#ifdef DEBAND_DITHER
1016 var output_rgb = output_color.rgb;
1017 output_rgb = powsafe(output_rgb, 1.0 / 2.2);
1018 output_rgb += screen_space_dither(pbr_input.frag_coord.xy);
1019 // This conversion back to linear space is required because our output texture format is
1020 // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
1021 output_rgb = powsafe(output_rgb, 2.2);
1022 output_color = vec4(output_rgb, output_color.a);
1023#endif
1024#endif
1025#ifdef PREMULTIPLY_ALPHA
1026 output_color = premultiply_alpha(pbr_input.material.flags, output_color);
1027#endif
1028 return output_color;
Contents