bevy/crates/bevy_pbr/src/render/pbr_fragment.wgsl
JMS55 4f20faaa43
Meshlet rendering (initial feature) (#10164)
# Objective
- Implements a more efficient, GPU-driven
(https://github.com/bevyengine/bevy/issues/1342) rendering pipeline
based on meshlets.
- Meshes are split into small clusters of triangles called meshlets,
each of which acts as a mini index buffer into the larger mesh data.
Meshlets can be compressed, streamed, culled, and batched much more
efficiently than monolithic meshes.


![image](https://github.com/bevyengine/bevy/assets/47158642/cb2aaad0-7a9a-4e14-93b0-15d4e895b26a)

![image](https://github.com/bevyengine/bevy/assets/47158642/7534035b-1eb7-4278-9b99-5322e4401715)

# Misc
* Future work: https://github.com/bevyengine/bevy/issues/11518
* Nanite reference:
https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf
Two pass occlusion culling explained very well:
https://medium.com/@mil_kru/two-pass-occlusion-culling-4100edcad501

---------

Co-authored-by: Ricky Taylor <rickytaylor26@gmail.com>
Co-authored-by: vero <email@atlasdostal.com>
Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: atlas dostal <rodol@rivalrebels.com>
2024-03-25 19:08:27 +00:00

257 lines
11 KiB
WebGPU Shading Language

#define_import_path bevy_pbr::pbr_fragment
#import bevy_pbr::{
pbr_functions,
pbr_bindings,
pbr_types,
prepass_utils,
lighting,
mesh_bindings::mesh,
mesh_view_bindings::view,
parallax_mapping::parallaxed_uv,
lightmap::lightmap,
}
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
#import bevy_pbr::mesh_view_bindings::screen_space_ambient_occlusion_texture
#import bevy_pbr::gtao_utils::gtao_multibounce
#endif
#ifdef MESHLET_MESH_MATERIAL_PASS
#import bevy_pbr::meshlet_visibility_buffer_resolve::VertexOutput
#else ifdef PREPASS_PIPELINE
#import bevy_pbr::prepass_io::VertexOutput
#else
#import bevy_pbr::forward_io::VertexOutput
#endif
// prepare a basic PbrInput from the vertex stage output, mesh binding and view binding
fn pbr_input_from_vertex_output(
in: VertexOutput,
is_front: bool,
double_sided: bool,
) -> pbr_types::PbrInput {
var pbr_input: pbr_types::PbrInput = pbr_types::pbr_input_new();
#ifdef MESHLET_MESH_MATERIAL_PASS
pbr_input.flags = in.mesh_flags;
#else
pbr_input.flags = mesh[in.instance_index].flags;
#endif
pbr_input.is_orthographic = view.projection[3].w == 1.0;
pbr_input.V = pbr_functions::calculate_view(in.world_position, pbr_input.is_orthographic);
pbr_input.frag_coord = in.position;
pbr_input.world_position = in.world_position;
#ifdef VERTEX_COLORS
pbr_input.material.base_color = in.color;
#endif
pbr_input.world_normal = pbr_functions::prepare_world_normal(
in.world_normal,
double_sided,
is_front,
);
#ifdef LOAD_PREPASS_NORMALS
pbr_input.N = prepass_utils::prepass_normal(in.position, 0u);
#else
pbr_input.N = normalize(pbr_input.world_normal);
#endif
return pbr_input;
}
// Prepare a full PbrInput by sampling all textures to resolve
// the material members
fn pbr_input_from_standard_material(
in: VertexOutput,
is_front: bool,
) -> pbr_types::PbrInput {
let double_sided = (pbr_bindings::material.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);
pbr_input.material.flags = pbr_bindings::material.flags;
pbr_input.material.base_color *= pbr_bindings::material.base_color;
pbr_input.material.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id;
// 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);
#ifdef VERTEX_UVS
let uv_transform = pbr_bindings::material.uv_transform;
var uv = (uv_transform * vec3(in.uv, 1.0)).xy;
#ifdef VERTEX_TANGENTS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) {
let V = pbr_input.V;
let N = in.world_normal;
let T = in.world_tangent.xyz;
let B = in.world_tangent.w * cross(N, T);
// Transform V from fragment to camera in world space to tangent space.
let Vt = vec3(dot(V, T), dot(V, B), dot(V, N));
uv = parallaxed_uv(
pbr_bindings::material.parallax_depth_scale,
pbr_bindings::material.max_parallax_layer_count,
pbr_bindings::material.max_relief_mapping_search_steps,
uv,
// Flip the direction of Vt to go toward the surface to make the
// parallax mapping algorithm easier to understand and reason
// about.
-Vt,
);
}
#endif // VERTEX_TANGENTS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
#ifdef MESHLET_MESH_MATERIAL_PASS
pbr_input.material.base_color *= textureSampleGrad(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, in.ddx_uv, in.ddy_uv);
#else
pbr_input.material.base_color *= textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias);
#endif
}
#endif // VERTEX_UVS
pbr_input.material.flags = pbr_bindings::material.flags;
// 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) {
pbr_input.material.reflectance = pbr_bindings::material.reflectance;
pbr_input.material.ior = pbr_bindings::material.ior;
pbr_input.material.attenuation_color = pbr_bindings::material.attenuation_color;
pbr_input.material.attenuation_distance = pbr_bindings::material.attenuation_distance;
pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff;
// emissive
// TODO use .a for exposure compensation in HDR
var emissive: vec4<f32> = pbr_bindings::material.emissive;
#ifdef VERTEX_UVS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
#ifdef MESHLET_MESH_MATERIAL_PASS
emissive = vec4<f32>(emissive.rgb * textureSampleGrad(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, uv, in.ddx_uv, in.ddy_uv).rgb, 1.0);
#else
emissive = vec4<f32>(emissive.rgb * textureSampleBias(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, uv, view.mip_bias).rgb, 1.0);
#endif
}
#endif
pbr_input.material.emissive = emissive;
// metallic and perceptual roughness
var metallic: f32 = pbr_bindings::material.metallic;
var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness;
let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);
#ifdef VERTEX_UVS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
#ifdef MESHLET_MESH_MATERIAL_PASS
let metallic_roughness = textureSampleGrad(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, uv, in.ddx_uv, in.ddy_uv);
#else
let metallic_roughness = textureSampleBias(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, uv, view.mip_bias);
#endif
// Sampling from GLTF standard channels for now
metallic *= metallic_roughness.b;
perceptual_roughness *= metallic_roughness.g;
}
#endif
pbr_input.material.metallic = metallic;
pbr_input.material.perceptual_roughness = perceptual_roughness;
var specular_transmission: f32 = pbr_bindings::material.specular_transmission;
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_SPECULAR_TRANSMISSION_TEXTURE_BIT) != 0u) {
#ifdef MESHLET_MESH_MATERIAL_PASS
specular_transmission *= textureSampleGrad(pbr_bindings::specular_transmission_texture, pbr_bindings::specular_transmission_sampler, uv, in.ddx_uv, in.ddy_uv).r;
#else
specular_transmission *= textureSampleBias(pbr_bindings::specular_transmission_texture, pbr_bindings::specular_transmission_sampler, uv, view.mip_bias).r;
#endif
}
#endif
pbr_input.material.specular_transmission = specular_transmission;
var thickness: f32 = pbr_bindings::material.thickness;
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_THICKNESS_TEXTURE_BIT) != 0u) {
#ifdef MESHLET_MESH_MATERIAL_PASS
thickness *= textureSampleGrad(pbr_bindings::thickness_texture, pbr_bindings::thickness_sampler, uv, in.ddx_uv, in.ddy_uv).g;
#else
thickness *= textureSampleBias(pbr_bindings::thickness_texture, pbr_bindings::thickness_sampler, uv, view.mip_bias).g;
#endif
}
#endif
// scale thickness, accounting for non-uniform scaling (e.g. a “squished” mesh)
// TODO: Meshlet support
#ifndef MESHLET_MESH_MATERIAL_PASS
thickness *= length(
(transpose(mesh[in.instance_index].model) * vec4(pbr_input.N, 0.0)).xyz
);
#endif
pbr_input.material.thickness = thickness;
var diffuse_transmission = pbr_bindings::material.diffuse_transmission;
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DIFFUSE_TRANSMISSION_TEXTURE_BIT) != 0u) {
#ifdef MESHLET_MESH_MATERIAL_PASS
diffuse_transmission *= textureSampleGrad(pbr_bindings::diffuse_transmission_texture, pbr_bindings::diffuse_transmission_sampler, uv, in.ddx_uv, in.ddy_uv).a;
#else
diffuse_transmission *= textureSampleBias(pbr_bindings::diffuse_transmission_texture, pbr_bindings::diffuse_transmission_sampler, uv, view.mip_bias).a;
#endif
}
#endif
pbr_input.material.diffuse_transmission = diffuse_transmission;
var diffuse_occlusion: vec3<f32> = vec3(1.0);
var specular_occlusion: f32 = 1.0;
#ifdef VERTEX_UVS
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
#ifdef MESHLET_MESH_MATERIAL_PASS
diffuse_occlusion = vec3(textureSampleGrad(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, uv, in.ddx_uv, in.ddy_uv).r);
#else
diffuse_occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, uv, view.mip_bias).r);
#endif
}
#endif
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.position.xy), 0i).r;
let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb);
diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce);
// Use SSAO to estimate the specular occlusion.
// Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering"
specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao);
#endif
pbr_input.diffuse_occlusion = diffuse_occlusion;
pbr_input.specular_occlusion = specular_occlusion;
// N (normal vector)
#ifndef LOAD_PREPASS_NORMALS
pbr_input.N = pbr_functions::apply_normal_mapping(
pbr_bindings::material.flags,
pbr_input.world_normal,
double_sided,
is_front,
#ifdef VERTEX_TANGENTS
#ifdef STANDARD_MATERIAL_NORMAL_MAP
in.world_tangent,
#endif
#endif
#ifdef VERTEX_UVS
uv,
#endif
view.mip_bias,
#ifdef MESHLET_MESH_MATERIAL_PASS
in.ddx_uv,
in.ddy_uv,
#endif
);
#endif
// TODO: Meshlet support
#ifdef LIGHTMAP
pbr_input.lightmap_light = lightmap(
in.uv_b,
pbr_bindings::material.lightmap_exposure,
in.instance_index);
#endif
}
return pbr_input;
}