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#import bevy_pbr::{
2 pbr_types,
3 pbr_fragment::pbr_input_from_standard_material,
4 decal::clustered::apply_decal_base_color,
5 mesh_view_bindings::{view, lights, clusterable_objects},
6 mesh_bindings::mesh,
7 pbr_types::{PbrInput, STANDARD_MATERIAL_FLAGS_UNLIT_BIT},
8 forward_io::{FragmentOutput},
9 pbr_functions::{
10 alpha_discard,
11 apply_pbr_lighting,
12 main_pass_post_lighting_processing,
13 visibility_range_dither
14 },
15 shadows::{fetch_directional_shadow, fetch_point_shadow},
16 mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT,
17 clustered_forward::{
18 fragment_cluster_index,
19 unpack_clusterable_object_index_ranges,
20 get_clusterable_object_id
21 }
24#import bevy_feronia::forward_sss_io::VertexOutput
25#import bevy_feronia::wind::Wind
27#ifdef BINDLESS
28 #import bevy_feronia::bindings::{wind_indices, wind_material}
29#else
30 #import bevy_feronia::bindings::{wind}
31#endif
33@fragment
34fn fragment(
35 in: VertexOutput,
36 @builtin(front_facing) is_front: bool,
37) -> FragmentOutput {
38 var standard_in: bevy_pbr::forward_io::VertexOutput;
40 standard_in.position = in.position;
41 standard_in.world_position = in.world_position;
42#ifdef VERTEX_NORMALS
43 standard_in.world_normal = in.world_normal;
45 if !is_front {
46 standard_in.world_normal = -standard_in.world_normal;
47 }
49#endif
51#ifdef VERTEX_UVS_A
52 standard_in.uv = in.uv;
53#endif
55#ifdef VERTEX_TANGENTS
56 standard_in.world_tangent = in.world_tangent;
57#endif
58 standard_in.instance_index = in.instance_index;
60#ifdef VISIBILITY_RANGE_DITHER
61 standard_in.visibility_range_dither = in.visibility_range_dither;
62 visibility_range_dither(in.position, in.visibility_range_dither);
63#endif
65 var pbr_input = pbr_input_from_standard_material(standard_in, is_front);
67 pbr_input.material.base_color = alpha_discard(pbr_input.material, pbr_input.material.base_color);
69 var out: FragmentOutput;
71#ifdef BINDLESS
72 let slot = mesh[in.instance_index].material_and_lightmap_bind_group_slot & 0xffffu;
73 let wind = wind_material[wind_indices[slot].material];
74#endif
76 if (pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
77 #ifdef SUBSURFACE_SCATTERING
78 let sss_glow = calculate_sss_lighting(wind.sss_scale, wind.sss_intensity, pbr_input, in.thinness_factor);
80 #ifdef DEBUG_SSS
81 out.color = vec4<f32>(sss_glow, 1.0);
82 return out;
83 #else
84 pbr_input.material.emissive = vec4<f32>(
85 pbr_input.material.emissive.rgb + (sss_glow * pbr_input.material.base_color.rgb),
86 pbr_input.material.emissive.a
87 );
88 #endif
89 #endif
91 out.color = apply_pbr_lighting(pbr_input);
92 } else {
93 out.color = pbr_input.material.base_color;
94 }
96 out.color = main_pass_post_lighting_processing(pbr_input, out.color);
98#ifdef MATERIAL_DEBUG
99 out.color = wind.debug_color;
100#endif
102 return out;
105// GDC 2011 "Approximating Translucency" Implementation
106// Source: Barré-Brisebois, C., & Bouchard, M. (2011). Approximating Translucency for a
107// Fast, Cheap and Convincing Subsurface Scattering Look. Game Developers Conference.
110// Controls how much the surface normal influences the scattered light direction.
111// A value of 0.0 makes light appear to pass straight through.
112// A value of 1.0 makes light appear to be heavily scattered by the surface.
113const SSS_DISTORTION: f32 = 0.5;
116const SSS_WRAP: f32 = 0.2;
117const SSS_WRAP_INV: f32 = 1.0 / (1.0 + SSS_WRAP);
120// Scale down light rgb
121const LIGHT_INTENSITY_SCALE = 0.00005;
123fn calculate_sss_lighting(scale:f32, intensity:f32, pbr_input: PbrInput, thinness_factor: f32) -> vec3<f32> {
124 var sss_light = vec3<f32>(0.0);
126 let view_pos = view.view_from_world * pbr_input.world_position;
127 let view_z = view_pos.z;
128 let cluster_index = fragment_cluster_index(pbr_input.frag_coord.xy, view_z, pbr_input.is_orthographic);
129 let ranges = unpack_clusterable_object_index_ranges(cluster_index);
131 // --- POINT LIGHTS ---
132 for (var i = ranges.first_point_light_index_offset; i < ranges.first_spot_light_index_offset; i = i + 1u) {
133 let light_id = get_clusterable_object_id(i);
134 let light = clusterable_objects.data[light_id];
136 // Skip if covered by shadow
137 var shadow = 1.0;
138 if (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT)!= 0u {
139 shadow = fetch_point_shadow(light_id, pbr_input.world_position, pbr_input.world_normal);
140 }
142 if (shadow <= 0.0) { continue; }
144 // Skip invalid range lights
145 let light_position = light.position_radius.xyz;
146 let scaled_light_color = light.color_inverse_square_range.rgb * LIGHT_INTENSITY_SCALE;
147 let inverse_square_range = light.color_inverse_square_range.w;
149 if (inverse_square_range <= 0.0) { continue; }
151 // Skip out of range lights
152 let range_sq = 1.0 / inverse_square_range;
153 let light_vector = light_position - pbr_input.world_position.xyz;
154 let distance_sq = dot(light_vector, light_vector);
156 if (distance_sq > range_sq) { continue; }
158 let L = normalize(light_vector);
160 let att_factor = saturate(1.0 - distance_sq / range_sq);
161 let attenuation = att_factor * att_factor;
162 let light_contribution = scaled_light_color * attenuation;
164 // --- GDC 2011 SSS MODEL ---
165 let H = normalize(L + pbr_input.world_normal * SSS_DISTORTION);
167 let back_scatter_dot = saturate(dot(pbr_input.V, -H));
169 // pow(dot, 8) * SSS_SCALE
170 let t = back_scatter_dot * back_scatter_dot;
171 let t2 = t * t;
172 let back_scatter = t2 * t2 * scale;
174 let front_scatter = saturate((dot(pbr_input.world_normal, L) + SSS_WRAP) * SSS_WRAP_INV);
176 let sss_factor = back_scatter + front_scatter;
178 sss_light += sss_factor * light_contribution * shadow;
179 }
181 // --- DIRECTIONAL LIGHTS ---
182 for (var i = 0u; i < lights.n_directional_lights; i = i + 1u) {
183 // Skip if covered by shadow
184 let shadow = fetch_directional_shadow(i, pbr_input.world_position, pbr_input.world_normal, view_z);
185 if (shadow <= 0.0) { continue; }
187 let sun = lights.directional_lights[i];
188 let L = sun.direction_to_light;
190 // --- GDC 2011 SSS MODEL ---
191 let H = normalize(L + pbr_input.world_normal * SSS_DISTORTION);
192 let back_scatter_dot = saturate(dot(pbr_input.V, -H));
194 // pow(dot, 8) * SSS_SCALE
195 let t = back_scatter_dot * back_scatter_dot;
196 let t2 = t * t;
197 let back_scatter = t2 * t2 * scale;
199 let front_scatter = saturate((dot(pbr_input.world_normal, L) + SSS_WRAP) * SSS_WRAP_INV);
201 let sss_factor = back_scatter + front_scatter;
202 let light_contribution = sun.color.rgb * LIGHT_INTENSITY_SCALE;
204 sss_light += sss_factor * light_contribution * shadow;
205 }
207 return sss_light * intensity * thinness_factor;
Contents