Make StandardMaterial bindless. (#16644)

This commit makes `StandardMaterial` use bindless textures, as
implemented in PR #16368. Non-bindless mode, as used for example in
Metal and WebGL 2, remains fully supported via a plethora of `#ifdef
BINDLESS` preprocessor definitions.

Unfortunately, this PR introduces quite a bit of unsightliness into the
PBR shaders. This is a result of the fact that WGSL supports neither
passing binding arrays to functions nor passing individual *elements* of
binding arrays to functions, except directly to texture sample
functions. Thus we're unable to use the `sample_texture` abstraction
that helped abstract over the meshlet and non-meshlet paths. I don't
think there's anything we can do to help this other than to suggest
improvements to upstream Naga.
This commit is contained in:
Patrick Walton 2024-12-10 09:48:56 -08:00 committed by GitHub
parent 7236070573
commit 7ed1f327d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 574 additions and 166 deletions

View file

@ -155,14 +155,32 @@ impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
layout: &BindGroupLayout, layout: &BindGroupLayout,
render_device: &RenderDevice, render_device: &RenderDevice,
(base_param, extended_param): &mut SystemParamItem<'_, '_, Self::Param>, (base_param, extended_param): &mut SystemParamItem<'_, '_, Self::Param>,
mut force_no_bindless: bool,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> { ) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> {
// Only allow bindless mode if both the base material and the extension
// support it.
force_no_bindless = force_no_bindless
|| B::BINDLESS_SLOT_COUNT.is_none()
|| E::BINDLESS_SLOT_COUNT.is_none();
// add together the bindings of the base material and the user material // add together the bindings of the base material and the user material
let UnpreparedBindGroup { let UnpreparedBindGroup {
mut bindings, mut bindings,
data: base_data, data: base_data,
} = B::unprepared_bind_group(&self.base, layout, render_device, base_param)?; } = B::unprepared_bind_group(
let extended_bindgroup = &self.base,
E::unprepared_bind_group(&self.extension, layout, render_device, extended_param)?; layout,
render_device,
base_param,
force_no_bindless,
)?;
let extended_bindgroup = E::unprepared_bind_group(
&self.extension,
layout,
render_device,
extended_param,
force_no_bindless,
)?;
bindings.extend(extended_bindgroup.bindings.0); bindings.extend(extended_bindgroup.bindings.0);
@ -174,13 +192,23 @@ impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
fn bind_group_layout_entries( fn bind_group_layout_entries(
render_device: &RenderDevice, render_device: &RenderDevice,
mut force_no_bindless: bool,
) -> Vec<bevy_render::render_resource::BindGroupLayoutEntry> ) -> Vec<bevy_render::render_resource::BindGroupLayoutEntry>
where where
Self: Sized, Self: Sized,
{ {
// Only allow bindless mode if both the base material and the extension
// support it.
force_no_bindless = force_no_bindless
|| B::BINDLESS_SLOT_COUNT.is_none()
|| E::BINDLESS_SLOT_COUNT.is_none();
// add together the bindings of the standard material and the user material // add together the bindings of the standard material and the user material
let mut entries = B::bind_group_layout_entries(render_device); let mut entries = B::bind_group_layout_entries(render_device, force_no_bindless);
entries.extend(E::bind_group_layout_entries(render_device)); entries.extend(E::bind_group_layout_entries(
render_device,
force_no_bindless,
));
entries entries
} }
} }

View file

@ -1062,6 +1062,7 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
&pipeline.material_layout, &pipeline.material_layout,
render_device, render_device,
material_param, material_param,
false,
) { ) {
Ok(unprepared) => { Ok(unprepared) => {
bind_group_allocator.init(render_device, *material_binding_id, unprepared); bind_group_allocator.init(render_device, *material_binding_id, unprepared);

View file

@ -767,7 +767,7 @@ where
fn from_world(world: &mut World) -> Self { fn from_world(world: &mut World) -> Self {
// Create a new bind group allocator. // Create a new bind group allocator.
let render_device = world.resource::<RenderDevice>(); let render_device = world.resource::<RenderDevice>();
let bind_group_layout_entries = M::bind_group_layout_entries(render_device); let bind_group_layout_entries = M::bind_group_layout_entries(render_device, false);
let bind_group_layout = let bind_group_layout =
render_device.create_bind_group_layout(M::label(), &bind_group_layout_entries); render_device.create_bind_group_layout(M::label(), &bind_group_layout_entries);
let fallback_buffers = let fallback_buffers =

View file

@ -31,6 +31,7 @@ pub enum UvChannel {
#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)] #[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
#[bind_group_data(StandardMaterialKey)] #[bind_group_data(StandardMaterialKey)]
#[uniform(0, StandardMaterialUniform)] #[uniform(0, StandardMaterialUniform)]
#[bindless(16)]
#[reflect(Default, Debug)] #[reflect(Default, Debug)]
pub struct StandardMaterial { pub struct StandardMaterial {
/// The color of the surface of the material before lighting. /// The color of the surface of the material before lighting.

View file

@ -494,6 +494,11 @@ where
shader_defs.push("HAS_PREVIOUS_MORPH".into()); shader_defs.push("HAS_PREVIOUS_MORPH".into());
} }
// If bindless mode is on, add a `BINDLESS` define.
if self.material_pipeline.bindless {
shader_defs.push("BINDLESS".into());
}
if key if key
.mesh_key .mesh_key
.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) .contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER)

View file

@ -1,8 +1,12 @@
#define_import_path bevy_pbr::parallax_mapping #define_import_path bevy_pbr::parallax_mapping
#import bevy_pbr::pbr_bindings::{depth_map_texture, depth_map_sampler} #import bevy_pbr::{
pbr_bindings::{depth_map_texture, depth_map_sampler},
mesh_bindings::mesh
}
fn sample_depth_map(uv: vec2<f32>) -> f32 { fn sample_depth_map(uv: vec2<f32>, instance_index: u32) -> f32 {
let slot = mesh[instance_index].material_bind_group_slot;
// We use `textureSampleLevel` over `textureSample` because the wgpu DX12 // We use `textureSampleLevel` over `textureSample` because the wgpu DX12
// backend (Fxc) panics when using "gradient instructions" inside a loop. // backend (Fxc) panics when using "gradient instructions" inside a loop.
// It results in the whole loop being unrolled by the shader compiler, // It results in the whole loop being unrolled by the shader compiler,
@ -13,7 +17,17 @@ fn sample_depth_map(uv: vec2<f32>) -> f32 {
// the MIP level, so no gradient instructions are used, and we can use // the MIP level, so no gradient instructions are used, and we can use
// sample_depth_map in our loop. // sample_depth_map in our loop.
// See https://stackoverflow.com/questions/56581141/direct3d11-gradient-instruction-used-in-a-loop-with-varying-iteration-forcing // See https://stackoverflow.com/questions/56581141/direct3d11-gradient-instruction-used-in-a-loop-with-varying-iteration-forcing
return textureSampleLevel(depth_map_texture, depth_map_sampler, uv, 0.0).r; return textureSampleLevel(
#ifdef BINDLESS
depth_map_texture[slot],
depth_map_sampler[slot],
#else // BINDLESS
depth_map_texture,
depth_map_sampler,
#endif // BINDLESS
uv,
0.0
).r;
} }
// An implementation of parallax mapping, see https://en.wikipedia.org/wiki/Parallax_mapping // An implementation of parallax mapping, see https://en.wikipedia.org/wiki/Parallax_mapping
@ -26,6 +40,7 @@ fn parallaxed_uv(
original_uv: vec2<f32>, original_uv: vec2<f32>,
// The vector from the camera to the fragment at the surface in tangent space // The vector from the camera to the fragment at the surface in tangent space
Vt: vec3<f32>, Vt: vec3<f32>,
instance_index: u32,
) -> vec2<f32> { ) -> vec2<f32> {
if max_layer_count < 1.0 { if max_layer_count < 1.0 {
return original_uv; return original_uv;
@ -53,7 +68,7 @@ fn parallaxed_uv(
var delta_uv = depth_scale * layer_depth * Vt.xy * vec2(1.0, -1.0) / view_steepness; var delta_uv = depth_scale * layer_depth * Vt.xy * vec2(1.0, -1.0) / view_steepness;
var current_layer_depth = 0.0; var current_layer_depth = 0.0;
var texture_depth = sample_depth_map(uv); var texture_depth = sample_depth_map(uv, instance_index);
// texture_depth > current_layer_depth means the depth map depth is deeper // texture_depth > current_layer_depth means the depth map depth is deeper
// than the depth the ray would be at this UV offset so the ray has not // than the depth the ray would be at this UV offset so the ray has not
@ -61,7 +76,7 @@ fn parallaxed_uv(
for (var i: i32 = 0; texture_depth > current_layer_depth && i <= i32(layer_count); i++) { for (var i: i32 = 0; texture_depth > current_layer_depth && i <= i32(layer_count); i++) {
current_layer_depth += layer_depth; current_layer_depth += layer_depth;
uv += delta_uv; uv += delta_uv;
texture_depth = sample_depth_map(uv); texture_depth = sample_depth_map(uv, instance_index);
} }
#ifdef RELIEF_MAPPING #ifdef RELIEF_MAPPING
@ -79,7 +94,7 @@ fn parallaxed_uv(
current_layer_depth -= delta_depth; current_layer_depth -= delta_depth;
for (var i: u32 = 0u; i < max_steps; i++) { for (var i: u32 = 0u; i < max_steps; i++) {
texture_depth = sample_depth_map(uv); texture_depth = sample_depth_map(uv, instance_index);
// Halve the deltas for the next step // Halve the deltas for the next step
delta_uv *= 0.5; delta_uv *= 0.5;
@ -103,7 +118,7 @@ fn parallaxed_uv(
// may skip small details and result in writhing material artifacts. // may skip small details and result in writhing material artifacts.
let previous_uv = uv - delta_uv; let previous_uv = uv - delta_uv;
let next_depth = texture_depth - current_layer_depth; let next_depth = texture_depth - current_layer_depth;
let previous_depth = sample_depth_map(previous_uv) - current_layer_depth + layer_depth; let previous_depth = sample_depth_map(previous_uv, instance_index) - current_layer_depth + layer_depth;
let weight = next_depth / (next_depth - previous_depth); let weight = next_depth / (next_depth - previous_depth);

View file

@ -2,6 +2,21 @@
#import bevy_pbr::pbr_types::StandardMaterial #import bevy_pbr::pbr_types::StandardMaterial
#ifdef BINDLESS
@group(2) @binding(0) var<storage> material: binding_array<StandardMaterial, 16>;
@group(2) @binding(1) var base_color_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(2) var base_color_sampler: binding_array<sampler, 16>;
@group(2) @binding(3) var emissive_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(4) var emissive_sampler: binding_array<sampler, 16>;
@group(2) @binding(5) var metallic_roughness_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(6) var metallic_roughness_sampler: binding_array<sampler, 16>;
@group(2) @binding(7) var occlusion_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(8) var occlusion_sampler: binding_array<sampler, 16>;
@group(2) @binding(9) var normal_map_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(10) var normal_map_sampler: binding_array<sampler, 16>;
@group(2) @binding(11) var depth_map_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(12) var depth_map_sampler: binding_array<sampler, 16>;
#else // BINDLESS
@group(2) @binding(0) var<uniform> material: StandardMaterial; @group(2) @binding(0) var<uniform> material: StandardMaterial;
@group(2) @binding(1) var base_color_texture: texture_2d<f32>; @group(2) @binding(1) var base_color_texture: texture_2d<f32>;
@group(2) @binding(2) var base_color_sampler: sampler; @group(2) @binding(2) var base_color_sampler: sampler;
@ -15,23 +30,50 @@
@group(2) @binding(10) var normal_map_sampler: sampler; @group(2) @binding(10) var normal_map_sampler: sampler;
@group(2) @binding(11) var depth_map_texture: texture_2d<f32>; @group(2) @binding(11) var depth_map_texture: texture_2d<f32>;
@group(2) @binding(12) var depth_map_sampler: sampler; @group(2) @binding(12) var depth_map_sampler: sampler;
#endif // BINDLESS
#ifdef PBR_ANISOTROPY_TEXTURE_SUPPORTED #ifdef PBR_ANISOTROPY_TEXTURE_SUPPORTED
#ifdef BINDLESS
@group(2) @binding(13) var anisotropy_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(14) var anisotropy_sampler: binding_array<sampler, 16>;
#else // BINDLESS
@group(2) @binding(13) var anisotropy_texture: texture_2d<f32>; @group(2) @binding(13) var anisotropy_texture: texture_2d<f32>;
@group(2) @binding(14) var anisotropy_sampler: sampler; @group(2) @binding(14) var anisotropy_sampler: sampler;
#endif #endif // BINDLESS
#endif // PBR_ANISOTROPY_TEXTURE_SUPPORTED
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
#ifdef BINDLESS
@group(2) @binding(15) var specular_transmission_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(16) var specular_transmission_sampler: binding_array<sampler, 16>;
@group(2) @binding(17) var thickness_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(18) var thickness_sampler: binding_array<sampler, 16>;
@group(2) @binding(19) var diffuse_transmission_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(20) var diffuse_transmission_sampler: binding_array<sampler, 16>;
#else // BINDLESS
@group(2) @binding(15) var specular_transmission_texture: texture_2d<f32>; @group(2) @binding(15) var specular_transmission_texture: texture_2d<f32>;
@group(2) @binding(16) var specular_transmission_sampler: sampler; @group(2) @binding(16) var specular_transmission_sampler: sampler;
@group(2) @binding(17) var thickness_texture: texture_2d<f32>; @group(2) @binding(17) var thickness_texture: texture_2d<f32>;
@group(2) @binding(18) var thickness_sampler: sampler; @group(2) @binding(18) var thickness_sampler: sampler;
@group(2) @binding(19) var diffuse_transmission_texture: texture_2d<f32>; @group(2) @binding(19) var diffuse_transmission_texture: texture_2d<f32>;
@group(2) @binding(20) var diffuse_transmission_sampler: sampler; @group(2) @binding(20) var diffuse_transmission_sampler: sampler;
#endif #endif // BINDLESS
#endif // PBR_TRANSMISSION_TEXTURES_SUPPORTED
#ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
#ifdef BINDLESS
@group(2) @binding(21) var clearcoat_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(22) var clearcoat_sampler: binding_array<sampler, 16>;
@group(2) @binding(23) var clearcoat_roughness_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(24) var clearcoat_roughness_sampler: binding_array<sampler, 16>;
@group(2) @binding(25) var clearcoat_normal_texture: binding_array<texture_2d<f32>, 16>;
@group(2) @binding(26) var clearcoat_normal_sampler: binding_array<sampler, 16>;
#else // BINDLESS
@group(2) @binding(21) var clearcoat_texture: texture_2d<f32>; @group(2) @binding(21) var clearcoat_texture: texture_2d<f32>;
@group(2) @binding(22) var clearcoat_sampler: sampler; @group(2) @binding(22) var clearcoat_sampler: sampler;
@group(2) @binding(23) var clearcoat_roughness_texture: texture_2d<f32>; @group(2) @binding(23) var clearcoat_roughness_texture: texture_2d<f32>;
@group(2) @binding(24) var clearcoat_roughness_sampler: sampler; @group(2) @binding(24) var clearcoat_roughness_sampler: sampler;
@group(2) @binding(25) var clearcoat_normal_texture: texture_2d<f32>; @group(2) @binding(25) var clearcoat_normal_texture: texture_2d<f32>;
@group(2) @binding(26) var clearcoat_normal_sampler: sampler; @group(2) @binding(26) var clearcoat_normal_sampler: sampler;
#endif #endif // BINDLESS
#endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED

View file

@ -70,12 +70,23 @@ fn pbr_input_from_standard_material(
in: VertexOutput, in: VertexOutput,
is_front: bool, is_front: bool,
) -> pbr_types::PbrInput { ) -> pbr_types::PbrInput {
let double_sided = (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u; #ifdef BINDLESS
let slot = mesh[in.instance_index].material_bind_group_slot;
let flags = pbr_bindings::material[slot].flags;
let base_color = pbr_bindings::material[slot].base_color;
let deferred_lighting_pass_id = pbr_bindings::material[slot].deferred_lighting_pass_id;
#else // BINDLESS
let flags = pbr_bindings::material.flags;
let base_color = pbr_bindings::material.base_color;
let deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id;
#endif
let double_sided = (flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u;
var pbr_input: pbr_types::PbrInput = pbr_input_from_vertex_output(in, is_front, double_sided); var pbr_input: pbr_types::PbrInput = pbr_input_from_vertex_output(in, is_front, double_sided);
pbr_input.material.flags = pbr_bindings::material.flags; pbr_input.material.flags = flags;
pbr_input.material.base_color *= pbr_bindings::material.base_color; pbr_input.material.base_color *= base_color;
pbr_input.material.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; pbr_input.material.deferred_lighting_pass_id = deferred_lighting_pass_id;
// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001);
@ -91,7 +102,13 @@ fn pbr_input_from_standard_material(
// TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass // TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef BINDLESS
let uv_transform = pbr_bindings::material[slot].uv_transform;
#else // BINDLESS
let uv_transform = pbr_bindings::material.uv_transform; let uv_transform = pbr_bindings::material.uv_transform;
#endif // BINDLESS
#ifdef VERTEX_UVS_A #ifdef VERTEX_UVS_A
var uv = (uv_transform * vec3(in.uv, 1.0)).xy; var uv = (uv_transform * vec3(in.uv, 1.0)).xy;
#endif #endif
@ -104,7 +121,7 @@ fn pbr_input_from_standard_material(
#endif #endif
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) {
let V = pbr_input.V; let V = pbr_input.V;
let TBN = pbr_functions::calculate_tbn_mikktspace(in.world_normal, in.world_tangent); let TBN = pbr_functions::calculate_tbn_mikktspace(in.world_normal, in.world_tangent);
let T = TBN[0]; let T = TBN[0];
@ -115,28 +132,42 @@ fn pbr_input_from_standard_material(
#ifdef VERTEX_UVS_A #ifdef VERTEX_UVS_A
// TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass // TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass
uv = parallaxed_uv( uv = parallaxed_uv(
#ifdef BINDLESS
pbr_bindings::material[slot].parallax_depth_scale,
pbr_bindings::material[slot].max_parallax_layer_count,
pbr_bindings::material[slot].max_relief_mapping_search_steps,
#else // BINDLESS
pbr_bindings::material.parallax_depth_scale, pbr_bindings::material.parallax_depth_scale,
pbr_bindings::material.max_parallax_layer_count, pbr_bindings::material.max_parallax_layer_count,
pbr_bindings::material.max_relief_mapping_search_steps, pbr_bindings::material.max_relief_mapping_search_steps,
#endif // BINDLESS
uv, uv,
// Flip the direction of Vt to go toward the surface to make the // Flip the direction of Vt to go toward the surface to make the
// parallax mapping algorithm easier to understand and reason // parallax mapping algorithm easier to understand and reason
// about. // about.
-Vt, -Vt,
in.instance_index,
); );
#endif #endif
#ifdef VERTEX_UVS_B #ifdef VERTEX_UVS_B
// TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass // TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass
uv_b = parallaxed_uv( uv_b = parallaxed_uv(
#ifdef BINDLESS
pbr_bindings::material[slot].parallax_depth_scale,
pbr_bindings::material[slot].max_parallax_layer_count,
pbr_bindings::material[slot].max_relief_mapping_search_steps,
#else // BINDLESS
pbr_bindings::material.parallax_depth_scale, pbr_bindings::material.parallax_depth_scale,
pbr_bindings::material.max_parallax_layer_count, pbr_bindings::material.max_parallax_layer_count,
pbr_bindings::material.max_relief_mapping_search_steps, pbr_bindings::material.max_relief_mapping_search_steps,
#endif // BINDLESS
uv_b, uv_b,
// Flip the direction of Vt to go toward the surface to make the // Flip the direction of Vt to go toward the surface to make the
// parallax mapping algorithm easier to understand and reason // parallax mapping algorithm easier to understand and reason
// about. // about.
-Vt, -Vt,
in.instance_index,
); );
#else #else
uv_b = uv; uv_b = uv;
@ -144,27 +175,47 @@ fn pbr_input_from_standard_material(
} }
#endif // VERTEX_TANGENTS #endif // VERTEX_TANGENTS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
pbr_input.material.base_color *= pbr_functions::sample_texture( pbr_input.material.base_color *=
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::base_color_texture[slot],
pbr_bindings::base_color_sampler[slot],
#else // BINDLESS
pbr_bindings::base_color_texture, pbr_bindings::base_color_texture,
pbr_bindings::base_color_sampler, pbr_bindings::base_color_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_BASE_COLOR_UV_B #ifdef STANDARD_MATERIAL_BASE_COLOR_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
); );
#ifdef ALPHA_TO_COVERAGE #ifdef ALPHA_TO_COVERAGE
// Sharpen alpha edges. // Sharpen alpha edges.
// //
// https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f // https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f
let alpha_mode = pbr_bindings::material.flags & let alpha_mode = flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ALPHA_TO_COVERAGE { if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ALPHA_TO_COVERAGE {
pbr_input.material.base_color.a = (pbr_input.material.base_color.a -
pbr_bindings::material.alpha_cutoff) / #ifdef BINDLESS
let alpha_cutoff = pbr_bindings::material[slot].alpha_cutoff;
#else // BINDLESS
let alpha_cutoff = pbr_bindings::material.alpha_cutoff;
#endif // BINDLESS
pbr_input.material.base_color.a = (pbr_input.material.base_color.a - alpha_cutoff) /
max(fwidth(pbr_input.material.base_color.a), 0.0001) + 0.5; max(fwidth(pbr_input.material.base_color.a), 0.0001) + 0.5;
} }
#endif // ALPHA_TO_COVERAGE #endif // ALPHA_TO_COVERAGE
@ -172,49 +223,99 @@ fn pbr_input_from_standard_material(
} }
#endif // VERTEX_UVS #endif // VERTEX_UVS
pbr_input.material.flags = pbr_bindings::material.flags; pbr_input.material.flags = flags;
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {
#ifdef BINDLESS
pbr_input.material.reflectance = pbr_bindings::material[slot].reflectance;
pbr_input.material.ior = pbr_bindings::material[slot].ior;
pbr_input.material.attenuation_color = pbr_bindings::material[slot].attenuation_color;
pbr_input.material.attenuation_distance = pbr_bindings::material[slot].attenuation_distance;
pbr_input.material.alpha_cutoff = pbr_bindings::material[slot].alpha_cutoff;
#else // BINDLESS
pbr_input.material.reflectance = pbr_bindings::material.reflectance; pbr_input.material.reflectance = pbr_bindings::material.reflectance;
pbr_input.material.ior = pbr_bindings::material.ior; pbr_input.material.ior = pbr_bindings::material.ior;
pbr_input.material.attenuation_color = pbr_bindings::material.attenuation_color; pbr_input.material.attenuation_color = pbr_bindings::material.attenuation_color;
pbr_input.material.attenuation_distance = pbr_bindings::material.attenuation_distance; pbr_input.material.attenuation_distance = pbr_bindings::material.attenuation_distance;
pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff; pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff;
#endif // BINDLESS
// emissive // emissive
#ifdef BINDLESS
var emissive: vec4<f32> = pbr_bindings::material[slot].emissive;
#else // BINDLESS
var emissive: vec4<f32> = pbr_bindings::material.emissive; var emissive: vec4<f32> = pbr_bindings::material.emissive;
#endif // BINDLESS
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
emissive = vec4<f32>(emissive.rgb * pbr_functions::sample_texture( emissive = vec4<f32>(emissive.rgb *
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::emissive_texture[slot],
pbr_bindings::emissive_sampler[slot],
#else // BINDLESS
pbr_bindings::emissive_texture, pbr_bindings::emissive_texture,
pbr_bindings::emissive_sampler, pbr_bindings::emissive_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_EMISSIVE_UV_B #ifdef STANDARD_MATERIAL_EMISSIVE_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
).rgb, emissive.a); bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).rgb,
emissive.a);
} }
#endif #endif
pbr_input.material.emissive = emissive; pbr_input.material.emissive = emissive;
// metallic and perceptual roughness // metallic and perceptual roughness
#ifdef BINDLESS
var metallic: f32 = pbr_bindings::material[slot].metallic;
var perceptual_roughness: f32 = pbr_bindings::material[slot].perceptual_roughness;
#else // BINDLESS
var metallic: f32 = pbr_bindings::material.metallic; var metallic: f32 = pbr_bindings::material.metallic;
var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness; var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness;
#endif // BINDLESS
let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
let metallic_roughness = pbr_functions::sample_texture( let metallic_roughness =
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::metallic_roughness_texture[slot],
pbr_bindings::metallic_roughness_sampler[slot],
#else // BINDLESS
pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_texture,
pbr_bindings::metallic_roughness_sampler, pbr_bindings::metallic_roughness_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_METALLIC_ROUGHNESS_UV_B #ifdef STANDARD_MATERIAL_METALLIC_ROUGHNESS_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
); );
// Sampling from GLTF standard channels for now // Sampling from GLTF standard channels for now
metallic *= metallic_roughness.b; metallic *= metallic_roughness.b;
@ -225,75 +326,157 @@ fn pbr_input_from_standard_material(
pbr_input.material.perceptual_roughness = perceptual_roughness; pbr_input.material.perceptual_roughness = perceptual_roughness;
// Clearcoat factor // Clearcoat factor
#ifdef BINDLESS
pbr_input.material.clearcoat = pbr_bindings::material[slot].clearcoat;
#else // BINDLESS
pbr_input.material.clearcoat = pbr_bindings::material.clearcoat; pbr_input.material.clearcoat = pbr_bindings::material.clearcoat;
#endif // BINDLESS
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_TEXTURE_BIT) != 0u) {
pbr_input.material.clearcoat *= pbr_functions::sample_texture( pbr_input.material.clearcoat *=
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::clearcoat_texture[slot],
pbr_bindings::clearcoat_sampler[slot],
#else // BINDLESS
pbr_bindings::clearcoat_texture, pbr_bindings::clearcoat_texture,
pbr_bindings::clearcoat_sampler, pbr_bindings::clearcoat_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_CLEARCOAT_UV_B #ifdef STANDARD_MATERIAL_CLEARCOAT_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).r; ).r;
} }
#endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
#endif // VERTEX_UVS #endif // VERTEX_UVS
// Clearcoat roughness // Clearcoat roughness
pbr_input.material.clearcoat_perceptual_roughness = pbr_bindings::material.clearcoat_perceptual_roughness; #ifdef BINDLESS
pbr_input.material.clearcoat_perceptual_roughness =
pbr_bindings::material[slot].clearcoat_perceptual_roughness;
#else // BINDLESS
pbr_input.material.clearcoat_perceptual_roughness =
pbr_bindings::material.clearcoat_perceptual_roughness;
#endif // BINDLESS
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_ROUGHNESS_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_CLEARCOAT_ROUGHNESS_TEXTURE_BIT) != 0u) {
pbr_input.material.clearcoat_perceptual_roughness *= pbr_functions::sample_texture( pbr_input.material.clearcoat_perceptual_roughness *=
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::clearcoat_roughness_texture[slot],
pbr_bindings::clearcoat_roughness_sampler[slot],
#else // BINDLESS
pbr_bindings::clearcoat_roughness_texture, pbr_bindings::clearcoat_roughness_texture,
pbr_bindings::clearcoat_roughness_sampler, pbr_bindings::clearcoat_roughness_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_CLEARCOAT_ROUGHNESS_UV_B #ifdef STANDARD_MATERIAL_CLEARCOAT_ROUGHNESS_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).g; ).g;
} }
#endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
#endif // VERTEX_UVS #endif // VERTEX_UVS
#ifdef BINDLESS
var specular_transmission: f32 = pbr_bindings::material[slot].specular_transmission;
#else // BINDLESS
var specular_transmission: f32 = pbr_bindings::material.specular_transmission; var specular_transmission: f32 = pbr_bindings::material.specular_transmission;
#endif // BINDLESS
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_SPECULAR_TRANSMISSION_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_SPECULAR_TRANSMISSION_TEXTURE_BIT) != 0u) {
specular_transmission *= pbr_functions::sample_texture( specular_transmission *=
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::specular_transmission_texture[slot],
pbr_bindings::specular_transmission_sampler[slot],
#else // BINDLESS
pbr_bindings::specular_transmission_texture, pbr_bindings::specular_transmission_texture,
pbr_bindings::specular_transmission_sampler, pbr_bindings::specular_transmission_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION_UV_B #ifdef STANDARD_MATERIAL_SPECULAR_TRANSMISSION_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).r; ).r;
} }
#endif #endif
#endif #endif
pbr_input.material.specular_transmission = specular_transmission; pbr_input.material.specular_transmission = specular_transmission;
#ifdef BINDLESS
var thickness: f32 = pbr_bindings::material[slot].thickness;
#else // BINDLESS
var thickness: f32 = pbr_bindings::material.thickness; var thickness: f32 = pbr_bindings::material.thickness;
#endif // BINDLESS
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_THICKNESS_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_THICKNESS_TEXTURE_BIT) != 0u) {
thickness *= pbr_functions::sample_texture( thickness *=
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::thickness_texture[slot],
pbr_bindings::thickness_sampler[slot],
#else // BINDLESS
pbr_bindings::thickness_texture, pbr_bindings::thickness_texture,
pbr_bindings::thickness_sampler, pbr_bindings::thickness_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_THICKNESS_UV_B #ifdef STANDARD_MATERIAL_THICKNESS_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).g; ).g;
} }
#endif #endif
@ -307,19 +490,39 @@ fn pbr_input_from_standard_material(
#endif #endif
pbr_input.material.thickness = thickness; pbr_input.material.thickness = thickness;
#ifdef BINDLESS
var diffuse_transmission = pbr_bindings::material[slot].diffuse_transmission;
#else // BINDLESS
var diffuse_transmission = pbr_bindings::material.diffuse_transmission; var diffuse_transmission = pbr_bindings::material.diffuse_transmission;
#endif // BINDLESS
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DIFFUSE_TRANSMISSION_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_DIFFUSE_TRANSMISSION_TEXTURE_BIT) != 0u) {
diffuse_transmission *= pbr_functions::sample_texture( diffuse_transmission *=
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::diffuse_transmission_texture[slot],
pbr_bindings::diffuse_transmission_sampler[slot],
#else // BINDLESS
pbr_bindings::diffuse_transmission_texture, pbr_bindings::diffuse_transmission_texture,
pbr_bindings::diffuse_transmission_sampler, pbr_bindings::diffuse_transmission_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION_UV_B #ifdef STANDARD_MATERIAL_DIFFUSE_TRANSMISSION_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).a; ).a;
} }
#endif #endif
@ -329,16 +532,31 @@ fn pbr_input_from_standard_material(
var diffuse_occlusion: vec3<f32> = vec3(1.0); var diffuse_occlusion: vec3<f32> = vec3(1.0);
var specular_occlusion: f32 = 1.0; var specular_occlusion: f32 = 1.0;
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
diffuse_occlusion *= pbr_functions::sample_texture( diffuse_occlusion *=
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::occlusion_texture[slot],
pbr_bindings::occlusion_sampler[slot],
#else // BINDLESS
pbr_bindings::occlusion_texture, pbr_bindings::occlusion_texture,
pbr_bindings::occlusion_sampler, pbr_bindings::occlusion_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_OCCLUSION_UV_B #ifdef STANDARD_MATERIAL_OCCLUSION_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).r; ).r;
} }
#endif #endif
@ -366,24 +584,33 @@ fn pbr_input_from_standard_material(
#ifdef STANDARD_MATERIAL_NORMAL_MAP #ifdef STANDARD_MATERIAL_NORMAL_MAP
let Nt = pbr_functions::sample_texture( let Nt =
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::normal_map_texture[slot],
pbr_bindings::normal_map_sampler[slot],
#else // BINDLESS
pbr_bindings::normal_map_texture, pbr_bindings::normal_map_texture,
pbr_bindings::normal_map_sampler, pbr_bindings::normal_map_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_NORMAL_MAP_UV_B #ifdef STANDARD_MATERIAL_NORMAL_MAP_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).rgb; ).rgb;
pbr_input.N = pbr_functions::apply_normal_mapping( pbr_input.N = pbr_functions::apply_normal_mapping(flags, TBN, double_sided, is_front, Nt);
pbr_bindings::material.flags,
TBN,
double_sided,
is_front,
Nt,
);
#endif // STANDARD_MATERIAL_NORMAL_MAP #endif // STANDARD_MATERIAL_NORMAL_MAP
@ -395,19 +622,34 @@ fn pbr_input_from_standard_material(
#ifdef STANDARD_MATERIAL_CLEARCOAT_NORMAL_MAP #ifdef STANDARD_MATERIAL_CLEARCOAT_NORMAL_MAP
let clearcoat_Nt = pbr_functions::sample_texture( let clearcoat_Nt =
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::clearcoat_normal_texture[slot],
pbr_bindings::clearcoat_normal_sampler[slot],
#else // BINDLESS
pbr_bindings::clearcoat_normal_texture, pbr_bindings::clearcoat_normal_texture,
pbr_bindings::clearcoat_normal_sampler, pbr_bindings::clearcoat_normal_sampler,
#endif // BINDLESS
#ifdef STANDARD_MATERIAL_CLEARCOAT_NORMAL_UV_B #ifdef STANDARD_MATERIAL_CLEARCOAT_NORMAL_UV_B
uv_b, uv_b,
#else #else
uv, uv,
#endif #endif
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).rgb; ).rgb;
pbr_input.clearcoat_N = pbr_functions::apply_normal_mapping( pbr_input.clearcoat_N = pbr_functions::apply_normal_mapping(
pbr_bindings::material.flags, flags,
TBN, TBN,
double_sided, double_sided,
is_front, is_front,
@ -429,20 +671,40 @@ fn pbr_input_from_standard_material(
#ifdef VERTEX_TANGENTS #ifdef VERTEX_TANGENTS
#ifdef STANDARD_MATERIAL_ANISOTROPY #ifdef STANDARD_MATERIAL_ANISOTROPY
#ifdef BINDLESS
var anisotropy_strength = pbr_bindings::material[slot].anisotropy_strength;
var anisotropy_direction = pbr_bindings::material[slot].anisotropy_rotation;
#else // BINDLESS
var anisotropy_strength = pbr_bindings::material.anisotropy_strength; var anisotropy_strength = pbr_bindings::material.anisotropy_strength;
var anisotropy_direction = pbr_bindings::material.anisotropy_rotation; var anisotropy_direction = pbr_bindings::material.anisotropy_rotation;
#endif // BINDLESS
// Adjust based on the anisotropy map if there is one. // Adjust based on the anisotropy map if there is one.
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ANISOTROPY_TEXTURE_BIT) != 0u) { if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_ANISOTROPY_TEXTURE_BIT) != 0u) {
let anisotropy_texel = pbr_functions::sample_texture( let anisotropy_texel =
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::anisotropy_texture[slot],
pbr_bindings::anisotropy_sampler[slot],
#else // BINDLESS
pbr_bindings::anisotropy_texture, pbr_bindings::anisotropy_texture,
pbr_bindings::anisotropy_sampler, pbr_bindings::anisotropy_sampler,
#endif
#ifdef STANDARD_MATERIAL_ANISOTROPY_UV_B #ifdef STANDARD_MATERIAL_ANISOTROPY_UV_B
uv_b, uv_b,
#else // STANDARD_MATERIAL_ANISOTROPY_UV_B #else // STANDARD_MATERIAL_ANISOTROPY_UV_B
uv, uv,
#endif // STANDARD_MATERIAL_ANISOTROPY_UV_B #endif // STANDARD_MATERIAL_ANISOTROPY_UV_B
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).rgb; ).rgb;
let anisotropy_direction_from_texture = normalize(anisotropy_texel.rg * 2.0 - 1.0); let anisotropy_direction_from_texture = normalize(anisotropy_texel.rg * 2.0 - 1.0);
@ -468,10 +730,14 @@ fn pbr_input_from_standard_material(
// TODO: Meshlet support // TODO: Meshlet support
#ifdef LIGHTMAP #ifdef LIGHTMAP
pbr_input.lightmap_light = lightmap(
in.uv_b, #ifdef BINDLESS
pbr_bindings::material.lightmap_exposure, let lightmap_exposure = pbr_bindings::material[slot].lightmap_exposure;
in.instance_index); #else // BINDLESS
let lightmap_exposure = pbr_bindings::material.lightmap_exposure;
#endif // BINDLESS
pbr_input.lightmap_light = lightmap(in.uv_b, lightmap_exposure, in.instance_index);
#endif #endif
} }

View file

@ -121,21 +121,6 @@ fn alpha_discard(material: pbr_types::StandardMaterial, output_color: vec4<f32>)
return color; return color;
} }
// Samples a texture using the appropriate biasing metric for the type of mesh
// in use (mesh vs. meshlet).
fn sample_texture(
texture: texture_2d<f32>,
samp: sampler,
uv: vec2<f32>,
bias: SampleBias,
) -> vec4<f32> {
#ifdef MESHLET_MESH_MATERIAL_PASS
return textureSampleGrad(texture, samp, uv, bias.ddx_uv, bias.ddy_uv);
#else
return textureSampleBias(texture, samp, uv, bias.mip_bias);
#endif
}
fn prepare_world_normal( fn prepare_world_normal(
world_normal: vec3<f32>, world_normal: vec3<f32>,
double_sided: bool, double_sided: bool,

View file

@ -76,11 +76,26 @@ fn fragment(
bias.mip_bias = view.mip_bias; bias.mip_bias = view.mip_bias;
#endif // MESHLET_MESH_MATERIAL_PASS #endif // MESHLET_MESH_MATERIAL_PASS
let Nt = pbr_functions::sample_texture( let Nt =
#ifdef MESHLET_MESH_MATERIAL_PASS
textureSampleGrad(
#else // MESHLET_MESH_MATERIAL_PASS
textureSampleBias(
#endif // MESHLET_MESH_MATERIAL_PASS
#ifdef BINDLESS
pbr_bindings::normal_map_texture[slot],
pbr_bindings::normal_map_sampler[slot],
#else // BINDLESS
pbr_bindings::normal_map_texture, pbr_bindings::normal_map_texture,
pbr_bindings::normal_map_sampler, pbr_bindings::normal_map_sampler,
#endif // BINDLESS
uv, uv,
bias, #ifdef MESHLET_MESH_MATERIAL_PASS
bias.ddx_uv,
bias.ddy_uv,
#else // MESHLET_MESH_MATERIAL_PASS
bias.mip_bias,
#endif // MESHLET_MESH_MATERIAL_PASS
).rgb; ).rgb;
let TBN = pbr_functions::calculate_tbn_mikktspace(normal, in.world_tangent); let TBN = pbr_functions::calculate_tbn_mikktspace(normal, in.world_tangent);

View file

@ -3,6 +3,7 @@
#import bevy_pbr::{ #import bevy_pbr::{
prepass_io::VertexOutput, prepass_io::VertexOutput,
prepass_bindings::previous_view_uniforms, prepass_bindings::previous_view_uniforms,
mesh_bindings::mesh,
mesh_view_bindings::view, mesh_view_bindings::view,
pbr_bindings, pbr_bindings,
pbr_types, pbr_types,
@ -15,7 +16,12 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05;
fn prepass_alpha_discard(in: VertexOutput) { fn prepass_alpha_discard(in: VertexOutput) {
#ifdef MAY_DISCARD #ifdef MAY_DISCARD
#ifdef BINDLESS
let slot = mesh[in.instance_index].material_bind_group_slot;
var output_color: vec4<f32> = pbr_bindings::material[slot].base_color;
#else // BINDLESS
var output_color: vec4<f32> = pbr_bindings::material.base_color; var output_color: vec4<f32> = pbr_bindings::material.base_color;
#endif // BINDLESS
#ifdef VERTEX_UVS #ifdef VERTEX_UVS
#ifdef STANDARD_MATERIAL_BASE_COLOR_UV_B #ifdef STANDARD_MATERIAL_BASE_COLOR_UV_B
@ -24,16 +30,38 @@ fn prepass_alpha_discard(in: VertexOutput) {
var uv = in.uv; var uv = in.uv;
#endif // STANDARD_MATERIAL_BASE_COLOR_UV_B #endif // STANDARD_MATERIAL_BASE_COLOR_UV_B
#ifdef BINDLESS
let uv_transform = pbr_bindings::material[slot].uv_transform;
let flags = pbr_bindings::material[slot].flags;
#else // BINDLESS
let uv_transform = pbr_bindings::material.uv_transform; let uv_transform = pbr_bindings::material.uv_transform;
let flags = pbr_bindings::material.flags;
#endif // BINDLESS
uv = (uv_transform * vec3(uv, 1.0)).xy; uv = (uv_transform * vec3(uv, 1.0)).xy;
if (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u { if (flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); output_color = output_color * textureSampleBias(
#ifdef BINDLESS
pbr_bindings::base_color_texture[slot],
pbr_bindings::base_color_sampler[slot],
#else // BINDLESS
pbr_bindings::base_color_texture,
pbr_bindings::base_color_sampler,
#endif // BINDLESS
uv,
view.mip_bias
);
} }
#endif // VERTEX_UVS #endif // VERTEX_UVS
let alpha_mode = pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; let alpha_mode = flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK { if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
if output_color.a < pbr_bindings::material.alpha_cutoff { #ifdef BINDLESS
let alpha_cutoff = pbr_bindings::material[slot].alpha_cutoff;
#else // BINDLESS
let alpha_cutoff = pbr_bindings::material.alpha_cutoff;
#endif // BINDLESS
if output_color.a < alpha_cutoff {
discard; discard;
} }
} else if (alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || } else if (alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND ||

View file

@ -443,7 +443,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
if render_device.features().contains( if render_device.features().contains(
#render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY | #render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY |
#render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY #render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY
) && render_device.limits().max_storage_buffers_per_shader_stage > 0 { ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 &&
!force_no_bindless {
( (
#render_path::render_resource::BufferBindingType::Storage { read_only: true }, #render_path::render_resource::BufferBindingType::Storage { read_only: true },
#render_path::render_resource::BufferUsages::STORAGE, #render_path::render_resource::BufferUsages::STORAGE,
@ -571,7 +572,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
let #actual_bindless_slot_count = if render_device.features().contains( let #actual_bindless_slot_count = if render_device.features().contains(
#render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY | #render_path::settings::WgpuFeatures::BUFFER_BINDING_ARRAY |
#render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY #render_path::settings::WgpuFeatures::TEXTURE_BINDING_ARRAY
) && render_device.limits().max_storage_buffers_per_shader_stage > 0 { ) && render_device.limits().max_storage_buffers_per_shader_stage > 0 &&
!force_no_bindless {
::core::num::NonZeroU32::new(#bindless_count) ::core::num::NonZeroU32::new(#bindless_count)
} else { } else {
None None
@ -607,6 +609,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
layout: &#render_path::render_resource::BindGroupLayout, layout: &#render_path::render_resource::BindGroupLayout,
render_device: &#render_path::renderer::RenderDevice, render_device: &#render_path::renderer::RenderDevice,
(images, fallback_image, storage_buffers): &mut #ecs_path::system::SystemParamItem<'_, '_, Self::Param>, (images, fallback_image, storage_buffers): &mut #ecs_path::system::SystemParamItem<'_, '_, Self::Param>,
force_no_bindless: bool,
) -> Result<#render_path::render_resource::UnpreparedBindGroup<Self::Data>, #render_path::render_resource::AsBindGroupError> { ) -> Result<#render_path::render_resource::UnpreparedBindGroup<Self::Data>, #render_path::render_resource::AsBindGroupError> {
#uniform_binding_type_declarations #uniform_binding_type_declarations
@ -618,7 +621,10 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
}) })
} }
fn bind_group_layout_entries(render_device: &#render_path::renderer::RenderDevice) -> Vec<#render_path::render_resource::BindGroupLayoutEntry> { fn bind_group_layout_entries(
render_device: &#render_path::renderer::RenderDevice,
force_no_bindless: bool
) -> Vec<#render_path::render_resource::BindGroupLayoutEntry> {
#actual_bindless_slot_count_declaration #actual_bindless_slot_count_declaration
#uniform_binding_type_declarations #uniform_binding_type_declarations

View file

@ -352,7 +352,7 @@ pub trait AsBindGroup {
param: &mut SystemParamItem<'_, '_, Self::Param>, param: &mut SystemParamItem<'_, '_, Self::Param>,
) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> { ) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> {
let UnpreparedBindGroup { bindings, data } = let UnpreparedBindGroup { bindings, data } =
Self::unprepared_bind_group(self, layout, render_device, param)?; Self::unprepared_bind_group(self, layout, render_device, param, false)?;
let entries = bindings let entries = bindings
.iter() .iter()
@ -372,30 +372,45 @@ pub trait AsBindGroup {
} }
/// Returns a vec of (binding index, `OwnedBindingResource`). /// Returns a vec of (binding index, `OwnedBindingResource`).
/// In cases where `OwnedBindingResource` is not available (as for bindless texture arrays currently), ///
/// an implementor may return `AsBindGroupError::CreateBindGroupDirectly` /// In cases where `OwnedBindingResource` is not available (as for bindless
/// from this function and instead define `as_bind_group` directly. This may /// texture arrays currently), an implementor may return
/// prevent certain features, such as bindless mode, from working correctly. /// `AsBindGroupError::CreateBindGroupDirectly` from this function and
/// instead define `as_bind_group` directly. This may prevent certain
/// features, such as bindless mode, from working correctly.
///
/// Set `force_no_bindless` to true to require that bindless textures *not*
/// be used. `ExtendedMaterial` uses this in order to ensure that the base
/// material doesn't use bindless mode if the extension doesn't.
fn unprepared_bind_group( fn unprepared_bind_group(
&self, &self,
layout: &BindGroupLayout, layout: &BindGroupLayout,
render_device: &RenderDevice, render_device: &RenderDevice,
param: &mut SystemParamItem<'_, '_, Self::Param>, param: &mut SystemParamItem<'_, '_, Self::Param>,
force_no_bindless: bool,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>; ) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>;
/// Creates the bind group layout matching all bind groups returned by [`AsBindGroup::as_bind_group`] /// Creates the bind group layout matching all bind groups returned by
/// [`AsBindGroup::as_bind_group`]
fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout
where where
Self: Sized, Self: Sized,
{ {
render_device.create_bind_group_layout( render_device.create_bind_group_layout(
Self::label(), Self::label(),
&Self::bind_group_layout_entries(render_device), &Self::bind_group_layout_entries(render_device, false),
) )
} }
/// Returns a vec of bind group layout entries /// Returns a vec of bind group layout entries
fn bind_group_layout_entries(render_device: &RenderDevice) -> Vec<BindGroupLayoutEntry> ///
/// Set `force_no_bindless` to true to require that bindless textures *not*
/// be used. `ExtendedMaterial` uses this in order to ensure that the base
/// material doesn't use bindless mode if the extension doesn't.
fn bind_group_layout_entries(
render_device: &RenderDevice,
force_no_bindless: bool,
) -> Vec<BindGroupLayoutEntry>
where where
Self: Sized; Self: Sized;
} }

View file

@ -144,6 +144,7 @@ impl AsBindGroup for BindlessMaterial {
_layout: &BindGroupLayout, _layout: &BindGroupLayout,
_render_device: &RenderDevice, _render_device: &RenderDevice,
_param: &mut SystemParamItem<'_, '_, Self::Param>, _param: &mut SystemParamItem<'_, '_, Self::Param>,
_force_no_bindless: bool,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> { ) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> {
// We implement `as_bind_group`` directly because bindless texture // We implement `as_bind_group`` directly because bindless texture
// arrays can't be owned. // arrays can't be owned.
@ -152,7 +153,7 @@ impl AsBindGroup for BindlessMaterial {
Err(AsBindGroupError::CreateBindGroupDirectly) Err(AsBindGroupError::CreateBindGroupDirectly)
} }
fn bind_group_layout_entries(_: &RenderDevice) -> Vec<BindGroupLayoutEntry> fn bind_group_layout_entries(_: &RenderDevice, _: bool) -> Vec<BindGroupLayoutEntry>
where where
Self: Sized, Self: Sized,
{ {