mirror of
https://github.com/bevyengine/bevy
synced 2025-01-04 17:28:56 +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.
139 lines
4.9 KiB
WebGPU Shading Language
139 lines
4.9 KiB
WebGPU Shading Language
#import bevy_pbr::prepass_bindings
|
|
#import bevy_pbr::pbr_bindings
|
|
#import bevy_pbr::pbr_types
|
|
#ifdef NORMAL_PREPASS
|
|
#import bevy_pbr::pbr_functions
|
|
#endif // NORMAL_PREPASS
|
|
|
|
struct FragmentInput {
|
|
@builtin(front_facing) is_front: bool,
|
|
@builtin(position) frag_coord: vec4<f32>,
|
|
#ifdef VERTEX_UVS
|
|
@location(0) uv: vec2<f32>,
|
|
#endif // VERTEX_UVS
|
|
|
|
#ifdef NORMAL_PREPASS
|
|
@location(1) world_normal: vec3<f32>,
|
|
#ifdef VERTEX_TANGENTS
|
|
@location(2) world_tangent: vec4<f32>,
|
|
#endif // VERTEX_TANGENTS
|
|
#endif // NORMAL_PREPASS
|
|
|
|
#ifdef MOTION_VECTOR_PREPASS
|
|
@location(3) world_position: vec4<f32>,
|
|
@location(4) previous_world_position: vec4<f32>,
|
|
#endif // MOTION_VECTOR_PREPASS
|
|
|
|
#ifdef DEPTH_CLAMP_ORTHO
|
|
@location(5) clip_position_unclamped: vec4<f32>,
|
|
#endif // DEPTH_CLAMP_ORTHO
|
|
};
|
|
|
|
// Cutoff used for the premultiplied alpha modes BLEND and ADD.
|
|
const PREMULTIPLIED_ALPHA_CUTOFF = 0.05;
|
|
|
|
// We can use a simplified version of alpha_discard() here since we only need to handle the alpha_cutoff
|
|
fn prepass_alpha_discard(in: FragmentInput) {
|
|
|
|
#ifdef MAY_DISCARD
|
|
var output_color: vec4<f32> = bevy_pbr::pbr_bindings::material.base_color;
|
|
|
|
#ifdef VERTEX_UVS
|
|
if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
|
|
output_color = output_color * textureSampleBias(bevy_pbr::pbr_bindings::base_color_texture, bevy_pbr::pbr_bindings::base_color_sampler, in.uv, bevy_pbr::prepass_bindings::view.mip_bias);
|
|
}
|
|
#endif // VERTEX_UVS
|
|
|
|
let alpha_mode = bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
|
if alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
|
if output_color.a < bevy_pbr::pbr_bindings::material.alpha_cutoff {
|
|
discard;
|
|
}
|
|
} else if (alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) {
|
|
if output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
|
|
discard;
|
|
}
|
|
} else if alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED {
|
|
if all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
|
|
discard;
|
|
}
|
|
}
|
|
|
|
#endif // MAY_DISCARD
|
|
}
|
|
|
|
#ifdef PREPASS_FRAGMENT
|
|
struct FragmentOutput {
|
|
#ifdef NORMAL_PREPASS
|
|
@location(0) normal: vec4<f32>,
|
|
#endif // NORMAL_PREPASS
|
|
|
|
#ifdef MOTION_VECTOR_PREPASS
|
|
@location(1) motion_vector: vec2<f32>,
|
|
#endif // MOTION_VECTOR_PREPASS
|
|
|
|
#ifdef DEPTH_CLAMP_ORTHO
|
|
@builtin(frag_depth) frag_depth: f32,
|
|
#endif // DEPTH_CLAMP_ORTHO
|
|
}
|
|
|
|
@fragment
|
|
fn fragment(in: FragmentInput) -> FragmentOutput {
|
|
prepass_alpha_discard(in);
|
|
|
|
var out: FragmentOutput;
|
|
|
|
#ifdef DEPTH_CLAMP_ORTHO
|
|
out.frag_depth = in.clip_position_unclamped.z;
|
|
#endif // DEPTH_CLAMP_ORTHO
|
|
|
|
#ifdef NORMAL_PREPASS
|
|
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
|
|
if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
|
|
let world_normal = bevy_pbr::pbr_functions::prepare_world_normal(
|
|
in.world_normal,
|
|
(bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
|
in.is_front,
|
|
);
|
|
|
|
let normal = bevy_pbr::pbr_functions::apply_normal_mapping(
|
|
bevy_pbr::pbr_bindings::material.flags,
|
|
world_normal,
|
|
#ifdef VERTEX_TANGENTS
|
|
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
|
in.world_tangent,
|
|
#endif // STANDARDMATERIAL_NORMAL_MAP
|
|
#endif // VERTEX_TANGENTS
|
|
#ifdef VERTEX_UVS
|
|
in.uv,
|
|
#endif // VERTEX_UVS
|
|
);
|
|
|
|
out.normal = vec4(normal * 0.5 + vec3(0.5), 1.0);
|
|
} else {
|
|
out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0);
|
|
}
|
|
#endif // NORMAL_PREPASS
|
|
|
|
#ifdef MOTION_VECTOR_PREPASS
|
|
let clip_position_t = bevy_pbr::prepass_bindings::view.unjittered_view_proj * in.world_position;
|
|
let clip_position = clip_position_t.xy / clip_position_t.w;
|
|
let previous_clip_position_t = bevy_pbr::prepass_bindings::previous_view_proj * in.previous_world_position;
|
|
let previous_clip_position = previous_clip_position_t.xy / previous_clip_position_t.w;
|
|
// These motion vectors are used as offsets to UV positions and are stored
|
|
// in the range -1,1 to allow offsetting from the one corner to the
|
|
// diagonally-opposite corner in UV coordinates, in either direction.
|
|
// A difference between diagonally-opposite corners of clip space is in the
|
|
// range -2,2, so this needs to be scaled by 0.5. And the V direction goes
|
|
// down where clip space y goes up, so y needs to be flipped.
|
|
out.motion_vector = (clip_position - previous_clip_position) * vec2(0.5, -0.5);
|
|
#endif // MOTION_VECTOR_PREPASS
|
|
|
|
return out;
|
|
}
|
|
#else
|
|
@fragment
|
|
fn fragment(in: FragmentInput) {
|
|
prepass_alpha_discard(in);
|
|
}
|
|
#endif // PREPASS_FRAGMENT
|