Closes #6, #7, #8, #9, #10 Refs #2, #122 Vendored bevy_render, bevy_core_pipeline, and bevy_pbr from Bevy v0.17.2 (commit 566358363126dd69f6e457e47f306c68f8041d2a) into libmarathon. - ~51K LOC vendored to crates/libmarathon/src/render/ - Merged bevy_render_macros into crates/macros/ - Fixed 773→0 compilation errors - Updated dependencies (encase 0.10→0.11, added 4 new deps) - Removed bevy_render/pbr/core_pipeline from app Cargo features All builds passing, macOS smoke test successful. Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
155 lines
6.0 KiB
WebGPU Shading Language
155 lines
6.0 KiB
WebGPU Shading Language
#define_import_path bevy_pbr::light_probe
|
||
|
||
#import bevy_pbr::clustered_forward
|
||
#import bevy_pbr::clustered_forward::ClusterableObjectIndexRanges
|
||
#import bevy_pbr::mesh_view_bindings::light_probes
|
||
#import bevy_pbr::mesh_view_types::LightProbe
|
||
|
||
// The result of searching for a light probe.
|
||
struct LightProbeQueryResult {
|
||
// The index of the light probe texture or textures in the binding array or
|
||
// arrays.
|
||
texture_index: i32,
|
||
// A scale factor that's applied to the diffuse and specular light from the
|
||
// light probe. This is in units of cd/m² (candela per square meter).
|
||
intensity: f32,
|
||
// Transform from world space to the light probe model space. In light probe
|
||
// model space, the light probe is a 1×1×1 cube centered on the origin.
|
||
light_from_world: mat4x4<f32>,
|
||
// Whether this light probe contributes diffuse light to lightmapped meshes.
|
||
affects_lightmapped_mesh_diffuse: bool,
|
||
};
|
||
|
||
fn transpose_affine_matrix(matrix: mat3x4<f32>) -> mat4x4<f32> {
|
||
let matrix4x4 = mat4x4<f32>(
|
||
matrix[0],
|
||
matrix[1],
|
||
matrix[2],
|
||
vec4<f32>(0.0, 0.0, 0.0, 1.0));
|
||
return transpose(matrix4x4);
|
||
}
|
||
|
||
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
||
|
||
// Searches for a light probe that contains the fragment.
|
||
//
|
||
// This is the version that's used when storage buffers are available and
|
||
// light probes are clustered.
|
||
//
|
||
// TODO: Interpolate between multiple light probes.
|
||
fn query_light_probe(
|
||
world_position: vec3<f32>,
|
||
is_irradiance_volume: bool,
|
||
clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>,
|
||
) -> LightProbeQueryResult {
|
||
var result: LightProbeQueryResult;
|
||
result.texture_index = -1;
|
||
|
||
// Reflection probe indices are followed by irradiance volume indices in the
|
||
// cluster index list. Use this fact to create our bracketing range of
|
||
// indices.
|
||
var start_offset: u32;
|
||
var end_offset: u32;
|
||
if is_irradiance_volume {
|
||
start_offset = (*clusterable_object_index_ranges).first_irradiance_volume_index_offset;
|
||
end_offset = (*clusterable_object_index_ranges).first_decal_offset;
|
||
} else {
|
||
start_offset = (*clusterable_object_index_ranges).first_reflection_probe_index_offset;
|
||
end_offset = (*clusterable_object_index_ranges).first_irradiance_volume_index_offset;
|
||
}
|
||
|
||
for (var light_probe_index_offset: u32 = start_offset;
|
||
light_probe_index_offset < end_offset && result.texture_index < 0;
|
||
light_probe_index_offset += 1u) {
|
||
let light_probe_index = i32(clustered_forward::get_clusterable_object_id(
|
||
light_probe_index_offset));
|
||
|
||
var light_probe: LightProbe;
|
||
if is_irradiance_volume {
|
||
light_probe = light_probes.irradiance_volumes[light_probe_index];
|
||
} else {
|
||
light_probe = light_probes.reflection_probes[light_probe_index];
|
||
}
|
||
|
||
// Unpack the inverse transform.
|
||
let light_from_world =
|
||
transpose_affine_matrix(light_probe.light_from_world_transposed);
|
||
|
||
// Check to see if the transformed point is inside the unit cube
|
||
// centered at the origin.
|
||
let probe_space_pos = (light_from_world * vec4<f32>(world_position, 1.0f)).xyz;
|
||
if (all(abs(probe_space_pos) <= vec3(0.5f))) {
|
||
result.texture_index = light_probe.cubemap_index;
|
||
result.intensity = light_probe.intensity;
|
||
result.light_from_world = light_from_world;
|
||
result.affects_lightmapped_mesh_diffuse =
|
||
light_probe.affects_lightmapped_mesh_diffuse != 0u;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
#else // AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
||
|
||
// Searches for a light probe that contains the fragment.
|
||
//
|
||
// This is the version that's used when storage buffers aren't available and
|
||
// light probes aren't clustered. It simply does a brute force search of all
|
||
// light probes. Because platforms without sufficient SSBO bindings typically
|
||
// lack bindless shaders, there will usually only be one of each type of light
|
||
// probe present anyway.
|
||
fn query_light_probe(
|
||
world_position: vec3<f32>,
|
||
is_irradiance_volume: bool,
|
||
clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>,
|
||
) -> LightProbeQueryResult {
|
||
var result: LightProbeQueryResult;
|
||
result.texture_index = -1;
|
||
|
||
var light_probe_count: i32;
|
||
if is_irradiance_volume {
|
||
light_probe_count = light_probes.irradiance_volume_count;
|
||
} else {
|
||
light_probe_count = light_probes.reflection_probe_count;
|
||
}
|
||
|
||
for (var light_probe_index: i32 = 0;
|
||
light_probe_index < light_probe_count && result.texture_index < 0;
|
||
light_probe_index += 1) {
|
||
var light_probe: LightProbe;
|
||
if is_irradiance_volume {
|
||
light_probe = light_probes.irradiance_volumes[light_probe_index];
|
||
} else {
|
||
light_probe = light_probes.reflection_probes[light_probe_index];
|
||
}
|
||
|
||
// Unpack the inverse transform.
|
||
let light_from_world =
|
||
transpose_affine_matrix(light_probe.light_from_world_transposed);
|
||
|
||
// Check to see if the transformed point is inside the unit cube
|
||
// centered at the origin.
|
||
let probe_space_pos = (light_from_world * vec4<f32>(world_position, 1.0f)).xyz;
|
||
if (all(abs(probe_space_pos) <= vec3(0.5f))) {
|
||
result.texture_index = light_probe.cubemap_index;
|
||
result.intensity = light_probe.intensity;
|
||
result.light_from_world = light_from_world;
|
||
result.affects_lightmapped_mesh_diffuse =
|
||
light_probe.affects_lightmapped_mesh_diffuse != 0u;
|
||
|
||
// TODO: Workaround for ICE in DXC https://github.com/microsoft/DirectXShaderCompiler/issues/6183
|
||
// We can't use `break` here because of the ICE.
|
||
// So instead we rely on the fact that we set `result.texture_index`
|
||
// above and check its value in the `for` loop header before
|
||
// looping.
|
||
// break;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
#endif // AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|