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::mesh_view_bindings::{view, lights, globals, clusterable_objects}
2#import bevy_pbr::shadows::fetch_directional_shadow
3#import bevy_pbr::shadows::fetch_point_shadow
4#import bevy_pbr::mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT
5#import bevy_pbr::clustered_forward::{fragment_cluster_index, unpack_clusterable_object_index_ranges, get_clusterable_object_id}
7#import bevy_pbr::mesh_functions::mesh_normal_local_to_world
8#import bevy_pbr::view_transformations::position_world_to_clip
9#import bevy_pbr::utils::rand_f
10#import bevy_pbr::mesh_bindings::mesh
12#import bevy_feronia::wind::{Wind, BindlessWindIndices}
13#import bevy_feronia::types::{SampledNoise, DisplacedVertex, InstanceInfo}
14#import bevy_feronia::bindings::wind
15#import bevy_feronia::displace::displace_vertex_and_calc_normal
16#import bevy_feronia::noise::sample_noise
18struct InstanceUniforms {
19 world_from_local: mat4x4<f32>,
20 top_color: vec4<f32>,
21 bottom_color: vec4<f32>,
22 visibility_range: vec4<f32>,
23 static_bend_strength: f32,
24 curve_factor: f32,
25 translucency: f32,
26 specular_strength: f32,
27 specular_power: f32,
28};
30@group(4) @binding(0)
31var<uniform> instance_uniforms: InstanceUniforms;
33struct Vertex {
34 @location(0) position: vec3<f32>,
36#ifdef VERTEX_NORMALS
37 @location(1) normal: vec3<f32>,
38#endif
39#ifdef VERTEX_UVS_A
40 @location(2) uv: vec2<f32>,
41#endif
42#ifdef VERTEX_UVS_B
43 @location(3) uv_b: vec2<f32>,
44#endif
45#ifdef VERTEX_TANGENTS
46 @location(4) tangent: vec4<f32>,
47#endif
48#ifdef VERTEX_COLORS
49 @location(5) color: vec4<f32>,
50#endif
51#ifdef SKINNED
52 @location(6) joint_indices: vec4<u32>,
53 @location(7) joint_weights: vec4<f32>,
54#endif
56 @location(8) i_pos_scale: vec4<f32>,
57 @location(9) i_index: u32,
58};
60struct VertexOutput {
61 @builtin(position) clip_position: vec4<f32>,
62 @location(0) ao: f32,
64#ifdef VISIBILITY_RANGE_DITHER
65 @location(1) @interpolate(flat) visibility_range_dither: i32,
66#endif
68 @location(2) world_position: vec3<f32>,
69 @location(3) world_normal: vec3<f32>,
70 @location(4) uv: vec2<f32>,
71 @location(5) local_pos: vec3<f32>,
72 @location(6) world_tangent: vec4<f32>,
73 @location(7) curve_factor: f32,
74};
76@vertex
77fn vertex(vertex: Vertex) -> VertexOutput {
78 var out: VertexOutput;
80 var scale = vertex.i_pos_scale.w;
81 var translation = vertex.i_pos_scale.xyz;
83 var world_from_local_matrix: mat4x4<f32>;
85 var rand_state = vertex.i_index;
86#ifdef BILLBOARDING
87 world_from_local_matrix = mat4x4<f32>(
88 vec4<f32>(scale, 0.0, 0.0, 0.0),
89 vec4<f32>(0.0, scale, 0.0, 0.0),
90 vec4<f32>(0.0, 0.0, scale, 0.0),
91 vec4<f32>(translation, 1.0)
92 );
93#else
94 // TODO pre-calculate / expose
96 let angle = rand_f(&rand_state) * 6.2831853;
98 let c = cos(angle);
99 let s = sin(angle);
101 let rot_y_matrix = mat3x3<f32>(
102 vec3<f32>(c, 0.0, s),
103 vec3<f32>(0.0, 1.0, 0.0),
104 vec3<f32>(-s, 0.0, c)
105 );
107 let rot_scale_matrix = rot_y_matrix * scale;
109 world_from_local_matrix = mat4x4<f32>(
110 vec4<f32>(rot_scale_matrix[0], 0.0),
111 vec4<f32>(rot_scale_matrix[1], 0.0),
112 vec4<f32>(rot_scale_matrix[2], 0.0),
113 vec4<f32>(translation, 1.0)
114 );
116#endif
118 var instance_world_matrix = instance_uniforms.world_from_local * world_from_local_matrix;
120 var instance: InstanceInfo;
121 instance.world_from_local = instance_world_matrix;
122 instance.instance_position = instance_world_matrix[3];
123 instance.wrapped_time = globals.time;
124 instance.instance_index = vertex.i_index;
126#ifdef STATIC_BEND
127 let raw_rand = rand_f(&rand_state);
128 let biased_rand = instance_uniforms.static_bend_strength + (raw_rand * (1. - instance_uniforms.static_bend_strength));
130 let static_bend_angle = rand_f(&rand_state) * 6.28318;
132 let static_bend_strength = biased_rand * instance_uniforms.static_bend_strength;
134 let static_bend = vec2<f32>(cos(static_bend_angle), sin(static_bend_angle)) * static_bend_strength;
135#endif
137 let noise = sample_noise(instance, vertex.position);
139 // --- DISPLACEMENT ---
140 let displaced = displace_vertex_and_calc_normal(
141 wind,
142 noise,
143 vertex.position,
144 instance,
145#ifdef STATIC_BEND
146 static_bend,
147#endif
148#ifdef VERTEX_NORMALS
149 vertex.normal,
150#endif
151#ifdef VERTEX_TANGENTS
152 vertex.tangent,
153#endif
154#ifdef VERTEX_UVS_A
155 vertex.uv
156#endif
157 );
159 out.world_position = displaced.world_position.xyz;
160 out.world_normal = displaced.world_normal;
161 out.local_pos = vertex.position;
162 out.world_tangent = displaced.world_tangent;
163 out.clip_position = position_world_to_clip(out.world_position);
165#ifdef VERTEX_UVS_A
166 out.uv = vertex.uv;
167#endif
169 // Calculate the AO here, but leave the gradient/texture for the fragment shader.
170 let height_range = wind.aabb_max.y - wind.aabb_min.y;
171 let normalized_height = saturate((vertex.position.y - wind.aabb_min.y) / max(height_range, 0.0001));
173 // 1.0 = full brightness, 0.0 = dark root area (global)
174 out.ao = normalized_height;
176#ifdef VISIBILITY_RANGE_DITHER
177 out.visibility_range_dither = get_visibility_range_dither_level(
178 instance_uniforms.visibility_range, instance.world_from_local[3]);
179#endif
181#ifdef CURVE_NORMALS
182 out.curve_factor = instance_uniforms.curve_factor;
183#endif
185 return out;
191#ifdef VISIBILITY_RANGE_DITHER
192// taken/adapted from https://github.com/bevyengine/bevy/blob/main/crates/bevy_pbr/src/render/mesh_functions.wgsl
193fn get_visibility_range_dither_level(lod_range: vec4<f32>, world_position: vec4<f32>) -> i32 {
194 let camera_distance = length(view.world_position.xyz - world_position.xyz);
196 // This encodes the following mapping:
197 //
198 // `lod_range.` x y z w camera distance
199 // ←───────┼────────┼────────┼────────┼────────→
200 // Dither Level -16 -16 0 0 16 16 Dither Level
202 let offset = select(-16, 0, camera_distance >= lod_range.z);
203 let bounds = select(lod_range.xy, lod_range.zw, camera_distance >= lod_range.z);
204 let level = i32(round((camera_distance - bounds.x) / (bounds.y - bounds.x) * 16.));
205 return offset + clamp(level, 0, 16);
207#endif
212@fragment
213fn fragment(
214 in: VertexOutput,
215 @builtin(front_facing) is_front: bool,
216) -> @location(0) vec4<f32> {
219#ifdef VISIBILITY_RANGE_DITHER
220 #ifndef SHADOW_PASS
221 bevy_pbr::pbr_functions::visibility_range_dither(in.clip_position, in.visibility_range_dither);
222 #endif
223#endif
225 var top_color = instance_uniforms.top_color.rgb;
226 var bottom_color = instance_uniforms.bottom_color.rgb;
228 // Blender exports UVs with Y=0 at bottom.
229 let corrected_uv = vec2<f32>(in.uv.x, 1.0 - in.uv.y);
231 let gradient_mix = pow(corrected_uv.y, 0.8);
232 let blade_color_rgb = mix(bottom_color, top_color, gradient_mix);
234 // TODO: allow texture usage here
235 var albedo = blade_color_rgb;
237 // in.color.r holds the normalized world height
238 let global_height_factor = in.ao;
240 // fake ao
241 let ambient_occlusion = mix(0.4, 1.0, global_height_factor);
243 albedo = albedo * ambient_occlusion;
245 var color_for_lighting = vec4<f32>(albedo, 1.0);
247 var normal = in.world_normal;
249 if !is_front {
250 normal = -normal;
251 }
253#ifndef DIRECTIONAL_LIGHTS
254#ifndef POINT_LIGHTS
255 return vec4<f32>(blade_color_rgb, in.ao);
256#endif
257#endif
260// TODO move out / re-use
261#ifdef CURVE_NORMALS
263 let signed_norm_x = in.uv.x * 2.0 - 1.0;
265 let curve_angle = in.curve_factor * abs(signed_norm_x);
266 let clamped_angle = clamp(curve_angle, 0.0, 1.4); // ~80 deg
267 let offset_mag = sin(clamped_angle);
269 let curve_offset_world = in.world_tangent.xyz * offset_mag * sign(signed_norm_x);
271 normal = normalize(normal + curve_offset_world);
272#endif
274 // --- LIGHTING MODEL ---
275 // Implements a simplified Blinn-Phong reflection model
276 // for specular highlights, combined with a standard Lambertian diffuse term.
277 //
278 // 1. Blinn-Phong: Blinn, J. F. (1977). "Models of light reflection
279 // for computer synthesized pictures". SIGGRAPH '77.
280 //
281 // 2. Phong (used in bevy_procedural_grass):
282 // Phong, B. T. (1975). "Illumination for computer generated pictures".
283 // Communications of the ACM.
284 // ---
286 // TODO expose/unify as much as possible with extended shader before exposing fields in `MaterialOptions`
288 // TODO tweak && expose
289 const DIFFUSE_SCALING: f32 = 1.0;
291 // Scale down light rgb
292 const LIGHT_INTENSITY_SCALE: f32 = 0.00005;
293 const AMBIENT_INTENSITY_SCALE: f32 = 0.001;
295 var final_color_rgb = color_for_lighting.rgb * lights.ambient_color.rgb * AMBIENT_INTENSITY_SCALE;
296 var final_specular = vec3<f32>(0.);
298 let V = normalize(view.world_position.xyz - in.world_position);
300 let view_z = dot(vec4<f32>(
301 view.view_from_world[0].z,
302 view.view_from_world[1].z,
303 view.view_from_world[2].z,
304 view.view_from_world[3].z
305 ), vec4<f32>(in.world_position, 1.));
307#ifdef DIRECTIONAL_LIGHTS
309 // --- Directional Lights (Sun) ---
310 for (var i = 0u; i < lights.n_directional_lights; i = i + 1u) {
311 let sun = lights.directional_lights[i];
312 let L = sun.direction_to_light;
314 let scaled_light_color = sun.color.rgb * LIGHT_INTENSITY_SCALE;
316 // Translucency
317 let NdotL_raw = dot(normal, L);
318 let NdotL_front = saturate(NdotL_raw);
319 let NdotL_back = saturate(-NdotL_raw) * instance_uniforms.translucency;
320 let NdotL = NdotL_front + NdotL_back;
322 // Specular Term
323 let H = normalize(V + L);
324 let NdotH = saturate(dot(normal, H));
325 let specular_factor = pow(NdotH, instance_uniforms.specular_power);
327 let shadow = fetch_directional_shadow(
328 i,
329 vec4<f32>(in.world_position, 1.),
330 normal,
331 view_z
332 );
333 let final_shadow = clamp(shadow, 0.1, 1.);
335 // Accumulate Diffuse
336 final_color_rgb += color_for_lighting.rgb * scaled_light_color * NdotL * final_shadow * DIFFUSE_SCALING;
338 // Accumulate Specular
339 if NdotL_raw > 0. {
340 final_specular += scaled_light_color * specular_factor * instance_uniforms.specular_strength * shadow;
341 }
342 }
343#endif
345#ifdef POINT_LIGHTS
346 let is_orthographic = view.clip_from_view[3].w == 1.;
347 let cluster_index = fragment_cluster_index(
348 in.clip_position.xy,
349 view_z,
350 is_orthographic
351 );
352 let ranges = unpack_clusterable_object_index_ranges(cluster_index);
354 for (var i = ranges.first_point_light_index_offset; i < ranges.first_spot_light_index_offset; i = i + 1u) {
355 let light_id = get_clusterable_object_id(i);
356 let light = clusterable_objects.data[light_id];
358 let light_position = light.position_radius.xyz;
359 let scaled_light_color = light.color_inverse_square_range.rgb * LIGHT_INTENSITY_SCALE;
360 let inverse_square_range = light.color_inverse_square_range.w;
362 if (inverse_square_range <= 0.) { continue; }
364 // Skip out of range lights
365 let range_sq = 1. / inverse_square_range;
366 let light_vector = light_position - in.world_position.xyz;
367 let distance_sq = dot(light_vector, light_vector);
369 if (distance_sq > range_sq) { continue; }
371 let L = normalize(light_vector);
373 let range_factor = distance_sq * inverse_square_range;
374 let smooth_falloff = saturate(1.0 - range_factor);
375 let attenuation = smooth_falloff * smooth_falloff;
377 // Translucency
378 let NdotL_raw = dot(normal, L);
379 let NdotL_front = saturate(NdotL_raw);
380 let NdotL_back = saturate(-NdotL_raw) * instance_uniforms.translucency;
381 let NdotL = NdotL_front + NdotL_back;
383 // Specular Term
384 let H = normalize(V + L);
385 let NdotH = saturate(dot(normal, H));
386 let specular_factor = pow(NdotH, instance_uniforms.specular_power);
388 var shadow = 1.;
389 if ((light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
390 shadow = fetch_point_shadow(light_id, vec4<f32>(in.world_position, 1.), normal);
391 }
392 let final_shadow = clamp(shadow, 0.1, 1.);
394 // Accumulate Diffuse
395 final_color_rgb += color_for_lighting.rgb * scaled_light_color * attenuation * NdotL * final_shadow * DIFFUSE_SCALING;
397 // Accumulate Specular
398 if NdotL_raw > 0. {
399 final_specular += scaled_light_color * attenuation * specular_factor * instance_uniforms.specular_strength * shadow;
400 }
401 }
402#endif // POINT_LIGHTS
404 final_color_rgb += final_specular;
406 var final_color = vec4<f32>(final_color_rgb, in.ao);
408#ifdef MATERIAL_DEBUG
409 final_color = wind.debug_color;
410#endif
412 return final_color;
Contents