Skip to content

Add material with specializations for debugging meshes #7390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ repository = "https://github.com/bevyengine/bevy"
rust-version = "1.67.0"

[workspace]
exclude = ["benches", "crates/bevy_ecs_compile_fail_tests", "crates/bevy_reflect_compile_fail_tests"]
exclude = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a format-on-save whitespace change.

"benches",
"crates/bevy_ecs_compile_fail_tests",
"crates/bevy_reflect_compile_fail_tests",
]
members = [
"crates/*",
"examples/android",
Expand Down Expand Up @@ -416,6 +420,17 @@ description = "Demonstrates parent->child relationships and relative transformat
category = "3D Rendering"
wasm = true


[[example]]
name = "pbr_debug"
path = "examples/3d/pbr_debug.rs"

[package.metadata.example.pbr_debug]
name = "Physically Based Rendering Debug"
description = "Shows properties and textures used in Physically Based Rendering (PBR) for debugging"
category = "3D Rendering"
wasm = true

[[example]]
name = "pbr"
path = "examples/3d/pbr.rs"
Expand Down
9 changes: 9 additions & 0 deletions crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub const PBR_PREPASS_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 9407115064344201137);
pub const PBR_FUNCTIONS_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 16550102964439850292);
pub const PBR_DEBUG_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2633850506518412226);
pub const SHADOW_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1836745567947005696);

Expand Down Expand Up @@ -135,6 +137,12 @@ impl Plugin for PbrPlugin {
Shader::from_wgsl
);
load_internal_asset!(app, PBR_SHADER_HANDLE, "render/pbr.wgsl", Shader::from_wgsl);
load_internal_asset!(
app,
PBR_DEBUG_HANDLE,
"render/pbr_debug.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
SHADOW_SHADER_HANDLE,
Expand Down Expand Up @@ -167,6 +175,7 @@ impl Plugin for PbrPlugin {
prepass_enabled: self.prepass_enabled,
..default()
})
.add_plugin(MaterialPlugin::<PbrDebugMaterial>::default())
.init_resource::<AmbientLight>()
.init_resource::<GlobalVisiblePointLights>()
.init_resource::<DirectionalLightShadowMap>()
Expand Down
42 changes: 23 additions & 19 deletions crates/bevy_pbr/src/render/clustered_forward.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn get_light_id(index: u32) -> u32 {
}

fn cluster_debug_visualization(
output_color: vec4<f32>,
shaded_color: vec4<f32>,
view_z: f32,
is_orthographic: bool,
offset_and_counts: vec3<u32>,
Expand All @@ -65,37 +65,41 @@ fn cluster_debug_visualization(
// 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;
let cluster_overlay_alpha = 0.5;
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 + lights.cluster_dimensions.z / 2u;
}
let slice_color = hsv2rgb(f32(z_slice) / f32(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
return vec4<f32>(
(1.0 - cluster_overlay_alpha) * shaded_color.rgb + cluster_overlay_alpha * slice_color,
shaded_color.a
);
#endif // CLUSTERED_FORWARD_DEBUG_Z_SLICES
#ifdef CLUSTERED_FORWARD_DEBUG_CLUSTER_LIGHT_COMPLEXITY
#else 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
let smoothed_complexity = smoothstep(
0.0,
max_light_complexity_per_cluster,
f32(offset_and_counts[1] + offset_and_counts[2])
);
return vec4<f32>(
(1.0 - cluster_overlay_alpha) * shaded_color.rg
+ cluster_overlay_alpha * vec2<f32>(smoothed_complexity, 1.0 - smoothed_complexity),
shaded_color.ba
);
#else 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
return vec4<f32>(
(1.0 - cluster_overlay_alpha) * shaded_color.rgb + cluster_overlay_alpha * cluster_color,
shaded_color.a
);
#endif // CLUSTERED_FORWARD_DEBUG_CLUSTER_COHERENCY

return output_color;
#else
return shaded_color;
#endif
}
141 changes: 137 additions & 4 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::{
FogMeta, GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta, NotShadowCaster,
NotShadowReceiver, ShadowPipeline, ViewClusterBindings, ViewFogUniformOffset,
ViewLightsUniformOffset, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS,
FogMeta, GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta, Material,
MaterialPipeline, NotShadowCaster, NotShadowReceiver, ShadowPipeline, StandardMaterial,
ViewClusterBindings, ViewFogUniformOffset, ViewLightsUniformOffset, ViewShadowBindings,
CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS,
PBR_SHADER_HANDLE,
};
use bevy_app::Plugin;
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
Expand Down Expand Up @@ -102,6 +103,7 @@ impl Plugin for MeshRenderPlugin {
render_app
.init_resource::<MeshPipeline>()
.init_resource::<SkinnedMeshUniform>()
.add_system_to_stage(RenderStage::Extract, extract_pbr_debug)
.add_system_to_stage(RenderStage::Extract, extract_meshes)
.add_system_to_stage(RenderStage::Extract, extract_skinned_meshes)
.add_system_to_stage(RenderStage::Prepare, prepare_skinned_meshes)
Expand Down Expand Up @@ -221,6 +223,131 @@ impl SkinnedMeshJoints {
}
}

#[derive(Clone, Copy, Debug, Resource, Hash, PartialEq, Eq)]
pub enum PbrDebug {
Uvs,
Depth,
InterpolatedVertexNormals,
InterpolatedVertexTangents,
TangentSpaceNormalMap,
NormalMappedNormal,
ViewSpaceNormalMappedNormal,
BaseColor,
BaseColorTexture,
Emissive,
EmissiveTexture,
Roughness,
RoughnessTexture,
Metallic,
MetallicTexture,
Reflectance,
OcclusionTexture,
Opaque,
AlphaMask,
AlphaBlend,
ClusteredForwardDebugZSlices,
ClusteredForwardDebugClusterLightComplexity,
ClusteredForwardDebugClusterCoherency,
}

impl PbrDebug {
fn shader_defs(&self) -> Vec<ShaderDefVal> {
let mut shader_defs = vec![];
match self {
PbrDebug::Opaque
| PbrDebug::AlphaMask
| PbrDebug::AlphaBlend
| PbrDebug::ClusteredForwardDebugClusterCoherency
| PbrDebug::ClusteredForwardDebugClusterLightComplexity
| PbrDebug::ClusteredForwardDebugZSlices => {}
_ => shader_defs.push("PBR_DEBUG".into()),
}
let def = match self {
PbrDebug::Uvs => "PBR_DEBUG_UVS",
PbrDebug::Depth => "PBR_DEBUG_DEPTH",
PbrDebug::InterpolatedVertexNormals => "PBR_DEBUG_INTERPOLATED_VERTEX_NORMALS",
PbrDebug::InterpolatedVertexTangents => "PBR_DEBUG_INTERPOLATED_VERTEX_TANGENTS",
PbrDebug::TangentSpaceNormalMap => "PBR_DEBUG_TANGENT_SPACE_NORMAL_MAP",
PbrDebug::NormalMappedNormal => "PBR_DEBUG_NORMAL_MAPPED_NORMAL",
PbrDebug::ViewSpaceNormalMappedNormal => "PBR_DEBUG_VIEW_SPACE_NORMAL_MAPPED_NORMAL",
PbrDebug::BaseColor => "PBR_DEBUG_BASE_COLOR",
PbrDebug::BaseColorTexture => "PBR_DEBUG_BASE_COLOR_TEXTURE",
PbrDebug::Emissive => "PBR_DEBUG_EMISSIVE",
PbrDebug::EmissiveTexture => "PBR_DEBUG_EMISSIVE_TEXTURE",
PbrDebug::Roughness => "PBR_DEBUG_ROUGHNESS",
PbrDebug::RoughnessTexture => "PBR_DEBUG_ROUGHNESS_TEXTURE",
PbrDebug::Metallic => "PBR_DEBUG_METALLIC",
PbrDebug::MetallicTexture => "PBR_DEBUG_METALLIC_TEXTURE",
PbrDebug::Reflectance => "PBR_DEBUG_REFLECTANCE",
PbrDebug::OcclusionTexture => "PBR_DEBUG_OCCLUSION_TEXTURE",
PbrDebug::Opaque => "PBR_DEBUG_OPAQUE",
PbrDebug::AlphaMask => "PBR_DEBUG_ALPHA_MASK",
PbrDebug::AlphaBlend => "PBR_DEBUG_ALPHA_BLEND",
PbrDebug::ClusteredForwardDebugZSlices => "CLUSTERED_FORWARD_DEBUG_Z_SLICES",
PbrDebug::ClusteredForwardDebugClusterLightComplexity => {
"CLUSTERED_FORWARD_DEBUG_CLUSTER_LIGHT_COMPLEXITY"
}
PbrDebug::ClusteredForwardDebugClusterCoherency => {
"CLUSTERED_FORWARD_DEBUG_CLUSTER_COHERENCY"
}
};
shader_defs.push(def.into());
shader_defs
}
}

#[derive(AsBindGroup, TypeUuid, Clone, Copy, Debug, Component)]
#[uuid = "824e2d46-9f0a-11ed-b683-d3bb267e3f05"]
#[bind_group_data(PbrDebug)]
pub struct PbrDebugMaterial {
pub pbr_debug: PbrDebug,
}

impl From<&PbrDebugMaterial> for PbrDebug {
fn from(material: &PbrDebugMaterial) -> Self {
material.pbr_debug
}
}

impl Material for PbrDebugMaterial {
fn fragment_shader() -> ShaderRef {
PBR_SHADER_HANDLE.typed().into()
}

fn specialize(
_pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayout,
key: crate::MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
descriptor
.fragment
.as_mut()
.unwrap()
.shader_defs
.extend(key.bind_group_data.shader_defs());

Ok(())
}
}

fn extract_pbr_debug(
pbr_debug: Extract<Option<Res<PbrDebug>>>,
mut mesh_pipeline: ResMut<MeshPipeline>,
mut material_pipeline: ResMut<MaterialPipeline<StandardMaterial>>,
mut specialized_mesh_pipelines: ResMut<
SpecializedMeshPipelines<MaterialPipeline<StandardMaterial>>,
>,
) {
if let Some(pbr_debug) = &*pbr_debug {
if pbr_debug.is_changed() {
mesh_pipeline.pbr_debug = Some(**pbr_debug);
material_pipeline.mesh_pipeline.pbr_debug = Some(**pbr_debug);
specialized_mesh_pipelines.invalidate();
}
}
}

pub fn extract_skinned_meshes(
mut commands: Commands,
mut previous_len: Local<usize>,
Expand Down Expand Up @@ -264,6 +391,7 @@ pub struct MeshPipeline {
// This dummy white texture is to be used in place of optional StandardMaterial textures
pub dummy_white_gpu_image: GpuImage,
pub clustered_forward_buffer_binding_type: BufferBindingType,
pub pbr_debug: Option<PbrDebug>,
}

impl FromWorld for MeshPipeline {
Expand Down Expand Up @@ -541,6 +669,7 @@ impl FromWorld for MeshPipeline {
skinned_mesh_layout,
clustered_forward_buffer_binding_type,
dummy_white_gpu_image,
pbr_debug: None,
}
}
}
Expand Down Expand Up @@ -699,6 +828,10 @@ impl SpecializedMeshPipeline for MeshPipeline {

let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;

if let Some(pbr_debug) = &self.pbr_debug {
shader_defs.extend(pbr_debug.shader_defs());
}

let (label, blend, depth_write_enabled);
let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
if pass == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
Expand Down
40 changes: 33 additions & 7 deletions crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,42 @@ struct FragmentInput {
#import bevy_pbr::mesh_vertex_output
};

// NOTE: bevy_pbr::pbr_debug needs specific things from the fragment input and standard material
#import bevy_pbr::pbr_debug

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef PBR_DEBUG
return pbr_debug(in);
#else
#ifdef PBR_DEBUG_OPAQUE
if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u
|| (material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND) != 0u) {
discard;
}
#endif
#ifdef PBR_DEBUG_ALPHA_MASK
if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u
|| (material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND) != 0u) {
discard;
}
#endif
#ifdef PBR_DEBUG_ALPHA_BLEND
if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u
|| (material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) {
discard;
}
#endif

var output_color: vec4<f32> = material.base_color;
#ifdef VERTEX_COLORS
output_color = output_color * in.color;
#endif
#endif // VERTEX_COLORS
#ifdef VERTEX_UVS
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv);
}
#endif
#endif // VERTEX_UVS

// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
if ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {
Expand All @@ -44,7 +69,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
emissive = vec4<f32>(emissive.rgb * textureSample(emissive_texture, emissive_sampler, in.uv).rgb, 1.0);
}
#endif
#endif // VERTEX_UVS
pbr_input.material.emissive = emissive;

var metallic: f32 = material.metallic;
Expand All @@ -56,7 +81,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
metallic = metallic * metallic_roughness.b;
perceptual_roughness = perceptual_roughness * metallic_roughness.g;
}
#endif
#endif // VERTEX_UVS
pbr_input.material.metallic = metallic;
pbr_input.material.perceptual_roughness = perceptual_roughness;

Expand All @@ -65,7 +90,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
occlusion = textureSample(occlusion_texture, occlusion_sampler, in.uv).r;
}
#endif
#endif // VERTEX_UVS
pbr_input.occlusion = occlusion;

pbr_input.frag_coord = in.frag_coord;
Expand All @@ -84,8 +109,8 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef VERTEX_TANGENTS
#ifdef STANDARDMATERIAL_NORMAL_MAP
in.world_tangent,
#endif
#endif
#endif // STANDARDMATERIAL_NORMAL_MAP
#endif // VERTEX_TANGENTS
#ifdef VERTEX_UVS
in.uv,
#endif
Expand Down Expand Up @@ -117,4 +142,5 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
output_color = premultiply_alpha(material.flags, output_color);
#endif
return output_color;
#endif // PBR_DEBUG
}
Loading