mirror of
https://github.com/bevyengine/bevy
synced 2024-12-21 10:33:08 +00:00
10f5c92068
# Objective operate on naga IR directly to improve handling of shader modules. - give codespan reporting into imported modules - allow glsl to be used from wgsl and vice-versa the ultimate objective is to make it possible to - provide user hooks for core shader functions (to modify light behaviour within the standard pbr pipeline, for example) - make automatic binding slot allocation possible but ... since this is already big, adds some value and (i think) is at feature parity with the existing code, i wanted to push this now. ## Solution i made a crate called naga_oil (https://github.com/robtfm/naga_oil - unpublished for now, could be part of bevy) which manages modules by - building each module independantly to naga IR - creating "header" files for each supported language, which are used to build dependent modules/shaders - make final shaders by combining the shader IR with the IR for imported modules then integrated this into bevy, replacing some of the existing shader processing stuff. also reworked examples to reflect this. ## Migration Guide shaders that don't use `#import` directives should work without changes. the most notable user-facing difference is that imported functions/variables/etc need to be qualified at point of use, and there's no "leakage" of visible stuff into your shader scope from the imports of your imports, so if you used things imported by your imports, you now need to import them directly and qualify them. the current strategy of including/'spreading' `mesh_vertex_output` directly into a struct doesn't work any more, so these need to be modified as per the examples (e.g. color_material.wgsl, or many others). mesh data is assumed to be in bindgroup 2 by default, if mesh data is bound into bindgroup 1 instead then the shader def `MESH_BINDGROUP_1` needs to be added to the pipeline shader_defs.
104 lines
5 KiB
WebGPU Shading Language
104 lines
5 KiB
WebGPU Shading Language
#define_import_path bevy_pbr::clustered_forward
|
|
|
|
#import bevy_pbr::mesh_view_bindings as bindings
|
|
#import bevy_pbr::utils hsv2rgb
|
|
|
|
// NOTE: Keep in sync with bevy_pbr/src/light.rs
|
|
fn view_z_to_z_slice(view_z: f32, is_orthographic: bool) -> u32 {
|
|
var z_slice: u32 = 0u;
|
|
if (is_orthographic) {
|
|
// NOTE: view_z is correct in the orthographic case
|
|
z_slice = u32(floor((view_z - bindings::lights.cluster_factors.z) * bindings::lights.cluster_factors.w));
|
|
} else {
|
|
// NOTE: had to use -view_z to make it positive else log(negative) is nan
|
|
z_slice = u32(log(-view_z) * bindings::lights.cluster_factors.z - bindings::lights.cluster_factors.w + 1.0);
|
|
}
|
|
// NOTE: We use min as we may limit the far z plane used for clustering to be closer than
|
|
// the furthest thing being drawn. This means that we need to limit to the maximum cluster.
|
|
return min(z_slice, bindings::lights.cluster_dimensions.z - 1u);
|
|
}
|
|
|
|
fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: bool) -> u32 {
|
|
let xy = vec2<u32>(floor((frag_coord - bindings::view.viewport.xy) * bindings::lights.cluster_factors.xy));
|
|
let z_slice = view_z_to_z_slice(view_z, is_orthographic);
|
|
// NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer
|
|
// arrays based on the cluster index.
|
|
return min(
|
|
(xy.y * bindings::lights.cluster_dimensions.x + xy.x) * bindings::lights.cluster_dimensions.z + z_slice,
|
|
bindings::lights.cluster_dimensions.w - 1u
|
|
);
|
|
}
|
|
|
|
// this must match CLUSTER_COUNT_SIZE in light.rs
|
|
const CLUSTER_COUNT_SIZE = 9u;
|
|
fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
|
|
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
|
return bindings::cluster_offsets_and_counts.data[cluster_index].xyz;
|
|
#else
|
|
let offset_and_counts = bindings::cluster_offsets_and_counts.data[cluster_index >> 2u][cluster_index & ((1u << 2u) - 1u)];
|
|
// [ 31 .. 18 | 17 .. 9 | 8 .. 0 ]
|
|
// [ offset | point light count | spot light count ]
|
|
return vec3<u32>(
|
|
(offset_and_counts >> (CLUSTER_COUNT_SIZE * 2u)) & ((1u << (32u - (CLUSTER_COUNT_SIZE * 2u))) - 1u),
|
|
(offset_and_counts >> CLUSTER_COUNT_SIZE) & ((1u << CLUSTER_COUNT_SIZE) - 1u),
|
|
offset_and_counts & ((1u << CLUSTER_COUNT_SIZE) - 1u),
|
|
);
|
|
#endif
|
|
}
|
|
|
|
fn get_light_id(index: u32) -> u32 {
|
|
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
|
return bindings::cluster_light_index_lists.data[index];
|
|
#else
|
|
// The index is correct but in cluster_light_index_lists we pack 4 u8s into a u32
|
|
// This means the index into cluster_light_index_lists is index / 4
|
|
let indices = bindings::cluster_light_index_lists.data[index >> 4u][(index >> 2u) & ((1u << 2u) - 1u)];
|
|
// And index % 4 gives the sub-index of the u8 within the u32 so we shift by 8 * sub-index
|
|
return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u);
|
|
#endif
|
|
}
|
|
|
|
fn cluster_debug_visualization(
|
|
output_color: vec4<f32>,
|
|
view_z: f32,
|
|
is_orthographic: bool,
|
|
offset_and_counts: vec3<u32>,
|
|
cluster_index: u32,
|
|
) -> vec4<f32> {
|
|
// Cluster allocation debug (using 'over' alpha blending)
|
|
#ifdef CLUSTERED_FORWARD_DEBUG_Z_SLICES
|
|
// NOTE: This debug mode visualises the z-slices
|
|
let cluster_overlay_alpha = 0.1;
|
|
var z_slice: u32 = view_z_to_z_slice(view_z, is_orthographic);
|
|
// A hack to make the colors alternate a bit more
|
|
if ((z_slice & 1u) == 1u) {
|
|
z_slice = z_slice + bindings::lights.cluster_dimensions.z / 2u;
|
|
}
|
|
let slice_color = hsv2rgb(f32(z_slice) / f32(bindings::lights.cluster_dimensions.z + 1u), 1.0, 0.5);
|
|
output_color = vec4<f32>(
|
|
(1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * slice_color,
|
|
output_color.a
|
|
);
|
|
#endif // CLUSTERED_FORWARD_DEBUG_Z_SLICES
|
|
#ifdef CLUSTERED_FORWARD_DEBUG_CLUSTER_LIGHT_COMPLEXITY
|
|
// NOTE: This debug mode visualises the number of lights within the cluster that contains
|
|
// the fragment. It shows a sort of lighting complexity measure.
|
|
let cluster_overlay_alpha = 0.1;
|
|
let max_light_complexity_per_cluster = 64.0;
|
|
output_color.r = (1.0 - cluster_overlay_alpha) * output_color.r
|
|
+ cluster_overlay_alpha * smoothStep(0.0, max_light_complexity_per_cluster, f32(offset_and_counts[1] + offset_and_counts[2]));
|
|
output_color.g = (1.0 - cluster_overlay_alpha) * output_color.g
|
|
+ cluster_overlay_alpha * (1.0 - smoothStep(0.0, max_light_complexity_per_cluster, f32(offset_and_counts[1] + offset_and_counts[2])));
|
|
#endif // CLUSTERED_FORWARD_DEBUG_CLUSTER_LIGHT_COMPLEXITY
|
|
#ifdef CLUSTERED_FORWARD_DEBUG_CLUSTER_COHERENCY
|
|
// NOTE: Visualizes the cluster to which the fragment belongs
|
|
let cluster_overlay_alpha = 0.1;
|
|
let cluster_color = hsv2rgb(random1D(f32(cluster_index)), 1.0, 0.5);
|
|
output_color = vec4<f32>(
|
|
(1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * cluster_color,
|
|
output_color.a
|
|
);
|
|
#endif // CLUSTERED_FORWARD_DEBUG_CLUSTER_COHERENCY
|
|
|
|
return output_color;
|
|
}
|