Skip to content

Commit

Permalink
improve shader import model (#5703)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
robtfm committed Jun 27, 2023
1 parent 0f4d16a commit 10f5c92
Show file tree
Hide file tree
Showing 69 changed files with 950 additions and 3,144 deletions.
10 changes: 3 additions & 7 deletions assets/shaders/animate_shader.wgsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#import bevy_pbr::mesh_types
// The time since startup data is in the globals binding which is part of the mesh_view_bindings import
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_view_bindings globals
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
let L = c.x;
Expand All @@ -22,12 +22,8 @@ fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
);
}

struct FragmentInput {
#import bevy_pbr::mesh_vertex_output
}

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {
let speed = 2.0;
// The globals binding contains various global values like time
// which is the time since startup in seconds
Expand Down
57 changes: 24 additions & 33 deletions assets/shaders/array_texture.wgsl
Original file line number Diff line number Diff line change
@@ -1,60 +1,51 @@
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_bindings

#import bevy_pbr::pbr_types
#import bevy_pbr::utils
#import bevy_pbr::clustered_forward
#import bevy_pbr::lighting
#import bevy_pbr::shadows
#import bevy_pbr::fog
#import bevy_pbr::pbr_functions
#import bevy_pbr::pbr_ambient
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::pbr_types STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT
#import bevy_core_pipeline::tonemapping tone_mapping
#import bevy_pbr::pbr_functions as fns

@group(1) @binding(0)
var my_array_texture: texture_2d_array<f32>;
@group(1) @binding(1)
var my_array_texture_sampler: sampler;

struct FragmentInput {
@builtin(front_facing) is_front: bool,
@builtin(position) frag_coord: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
};

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
let layer = i32(in.world_position.x) & 0x3;
fn fragment(
@builtin(front_facing) is_front: bool,
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
let layer = i32(mesh.world_position.x) & 0x3;

// Prepare a 'processed' StandardMaterial by sampling all textures to resolve
// the material members
var pbr_input: PbrInput = pbr_input_new();
var pbr_input: fns::PbrInput = fns::pbr_input_new();

pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, in.uv, layer);
pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, mesh.uv, layer);
#ifdef VERTEX_COLORS
pbr_input.material.base_color = pbr_input.material.base_color * in.color;
pbr_input.material.base_color = pbr_input.material.base_color * mesh.color;
#endif

pbr_input.frag_coord = in.frag_coord;
pbr_input.world_position = in.world_position;
pbr_input.world_normal = prepare_world_normal(
in.world_normal,
pbr_input.frag_coord = mesh.position;
pbr_input.world_position = mesh.world_position;
pbr_input.world_normal = fns::prepare_world_normal(
mesh.world_normal,
(pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
in.is_front,
is_front,
);

pbr_input.is_orthographic = view.projection[3].w == 1.0;

pbr_input.N = apply_normal_mapping(
pbr_input.N = fns::apply_normal_mapping(
pbr_input.material.flags,
pbr_input.world_normal,
mesh.world_normal,
#ifdef VERTEX_TANGENTS
#ifdef STANDARDMATERIAL_NORMAL_MAP
in.world_tangent,
mesh.world_tangent,
#endif
#endif
in.uv,
mesh.uv,
);
pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic);
pbr_input.V = fns::calculate_view(mesh.world_position, pbr_input.is_orthographic);

return tone_mapping(pbr(pbr_input));
return tone_mapping(fns::pbr(pbr_input), view.color_grading);
}
6 changes: 3 additions & 3 deletions assets/shaders/cubemap_unlit.wgsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

#ifdef CUBEMAP_ARRAY
@group(1) @binding(0)
Expand All @@ -13,9 +13,9 @@ var base_color_sampler: sampler;

@fragment
fn fragment(
#import bevy_pbr::mesh_vertex_output
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
let fragment_position_view_lh = world_position.xyz * vec3<f32>(1.0, 1.0, -1.0);
let fragment_position_view_lh = mesh.world_position.xyz * vec3<f32>(1.0, 1.0, -1.0);
return textureSample(
base_color_texture,
base_color_sampler,
Expand Down
6 changes: 3 additions & 3 deletions assets/shaders/custom_gltf_2d.wgsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#import bevy_sprite::mesh2d_view_bindings
#import bevy_sprite::mesh2d_bindings
#import bevy_sprite::mesh2d_functions
#import bevy_sprite::mesh2d_view_bindings globals
#import bevy_sprite::mesh2d_bindings mesh
#import bevy_sprite::mesh2d_functions mesh2d_position_local_to_clip

struct Vertex {
@location(0) position: vec3<f32>,
Expand Down
4 changes: 4 additions & 0 deletions assets/shaders/custom_material.frag
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ layout(set = 1, binding = 0) uniform CustomMaterial {
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
layout(set = 1, binding = 2) uniform sampler CustomMaterial_sampler;

// wgsl modules can be imported and used in glsl
// FIXME - this doesn't work any more ...
// #import bevy_pbr::pbr_functions as PbrFuncs

void main() {
// o_Target = PbrFuncs::tone_mapping(Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv));
o_Target = Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv);
}
6 changes: 4 additions & 2 deletions assets/shaders/custom_material.wgsl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

struct CustomMaterial {
color: vec4<f32>,
};
Expand All @@ -11,7 +13,7 @@ var base_color_sampler: sampler;

@fragment
fn fragment(
#import bevy_pbr::mesh_vertex_output
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
return material.color * textureSample(base_color_texture, base_color_sampler, uv);
return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv);
}
10 changes: 5 additions & 5 deletions assets/shaders/custom_material_screenspace_texture.wgsl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::utils
#import bevy_pbr::mesh_view_bindings view
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::utils coords_to_viewport_uv

@group(1) @binding(0)
var texture: texture_2d<f32>;
Expand All @@ -8,10 +9,9 @@ var texture_sampler: sampler;

@fragment
fn fragment(
@builtin(position) position: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
let viewport_uv = coords_to_viewport_uv(position.xy, view.viewport);
let viewport_uv = coords_to_viewport_uv(mesh.position.xy, view.viewport);
let color = textureSample(texture, texture_sampler, viewport_uv);
return color;
}
12 changes: 6 additions & 6 deletions assets/shaders/custom_vertex_attribute.wgsl
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_bindings
#import bevy_pbr::mesh_bindings mesh
#import bevy_pbr::mesh_functions mesh_position_local_to_clip

struct CustomMaterial {
color: vec4<f32>,
};
@group(1) @binding(0)
var<uniform> material: CustomMaterial;

// NOTE: Bindings must come before functions that use them!
#import bevy_pbr::mesh_functions

struct Vertex {
@location(0) position: vec3<f32>,
@location(1) blend_color: vec4<f32>,
Expand All @@ -23,7 +20,10 @@ struct VertexOutput {
@vertex
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
out.clip_position = mesh_position_local_to_clip(
mesh.model,
vec4<f32>(vertex.position, 1.0)
);
out.blend_color = vertex.blend_color;
return out;
}
Expand Down
15 changes: 6 additions & 9 deletions assets/shaders/instancing.wgsl
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
#import bevy_pbr::mesh_types
#import bevy_pbr::mesh_view_bindings

@group(1) @binding(0)
var<uniform> mesh: Mesh;

// NOTE: Bindings must come before functions that use them!
#import bevy_pbr::mesh_functions
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
#import bevy_pbr::mesh_bindings mesh

struct Vertex {
@location(0) position: vec3<f32>,
Expand All @@ -25,7 +19,10 @@ struct VertexOutput {
fn vertex(vertex: Vertex) -> VertexOutput {
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(position, 1.0));
out.clip_position = mesh_position_local_to_clip(
mesh.model,
vec4<f32>(position, 1.0)
);
out.color = vertex.i_color;
return out;
}
Expand Down
4 changes: 3 additions & 1 deletion assets/shaders/line_material.wgsl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

struct LineMaterial {
color: vec4<f32>,
};
Expand All @@ -7,7 +9,7 @@ var<uniform> material: LineMaterial;

@fragment
fn fragment(
#import bevy_pbr::mesh_vertex_output
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
return material.color;
}
2 changes: 1 addition & 1 deletion assets/shaders/post_processing.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// As you can see, the triangle ends up bigger than the screen.
//
// You don't need to worry about this too much since bevy will compute the correct UVs for you.
#import bevy_core_pipeline::fullscreen_vertex_shader
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput

@group(0) @binding(0)
var screen_texture: texture_2d<f32>;
Expand Down
4 changes: 3 additions & 1 deletion assets/shaders/shader_defs.wgsl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

struct CustomMaterial {
color: vec4<f32>,
};
Expand All @@ -7,7 +9,7 @@ var<uniform> material: CustomMaterial;

@fragment
fn fragment(
#import bevy_pbr::mesh_vertex_output
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
#ifdef IS_RED
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
Expand Down
12 changes: 6 additions & 6 deletions assets/shaders/show_prepass.wgsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import bevy_pbr::mesh_types
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_view_bindings globals
#import bevy_pbr::prepass_utils
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

struct ShowPrepassSettings {
show_depth: u32,
Expand All @@ -14,23 +15,22 @@ var<uniform> settings: ShowPrepassSettings;

@fragment
fn fragment(
@builtin(position) frag_coord: vec4<f32>,
#ifdef MULTISAMPLED
@builtin(sample_index) sample_index: u32,
#endif
#import bevy_pbr::mesh_vertex_output
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
#ifndef MULTISAMPLED
let sample_index = 0u;
#endif
if settings.show_depth == 1u {
let depth = prepass_depth(frag_coord, sample_index);
let depth = bevy_pbr::prepass_utils::prepass_depth(mesh.position, sample_index);
return vec4(depth, depth, depth, 1.0);
} else if settings.show_normals == 1u {
let normal = prepass_normal(frag_coord, sample_index);
let normal = bevy_pbr::prepass_utils::prepass_normal(mesh.position, sample_index);
return vec4(normal, 1.0);
} else if settings.show_motion_vectors == 1u {
let motion_vector = prepass_motion_vector(frag_coord, sample_index);
let motion_vector = bevy_pbr::prepass_utils::prepass_motion_vector(mesh.position, sample_index);
return vec4(motion_vector / globals.delta_time, 0.0, 1.0);
}

Expand Down
8 changes: 5 additions & 3 deletions assets/shaders/texture_binding_array.wgsl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

@group(1) @binding(0)
var textures: binding_array<texture_2d<f32>>;
@group(1) @binding(1)
Expand All @@ -7,11 +9,11 @@ var nearest_sampler: sampler;

@fragment
fn fragment(
#import bevy_pbr::mesh_vertex_output
mesh: MeshVertexOutput,
) -> @location(0) vec4<f32> {
// Select the texture to sample from using non-uniform uv coordinates
let coords = clamp(vec2<u32>(uv * 4.0), vec2<u32>(0u), vec2<u32>(3u));
let coords = clamp(vec2<u32>(mesh.uv * 4.0), vec2<u32>(0u), vec2<u32>(3u));
let index = coords.y * 4u + coords.x;
let inner_uv = fract(uv * 4.0);
let inner_uv = fract(mesh.uv * 4.0);
return textureSample(textures[index], nearest_sampler, inner_uv);
}
15 changes: 6 additions & 9 deletions assets/shaders/tonemapping_test_patterns.wgsl
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::mesh_bindings
#import bevy_pbr::utils
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::utils PI

#ifdef TONEMAP_IN_SHADER
#import bevy_core_pipeline::tonemapping
#endif

struct FragmentInput {
@builtin(front_facing) is_front: bool,
@builtin(position) frag_coord: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
};

// Sweep across hues on y axis with value from 0.0 to +15EV across x axis
// quantized into 24 steps for both axis.
fn color_sweep(uv: vec2<f32>) -> vec3<f32> {
Expand Down Expand Up @@ -47,7 +42,9 @@ fn continuous_hue(uv: vec2<f32>) -> vec3<f32> {
}

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
fn fragment(
in: MeshVertexOutput,
) -> @location(0) vec4<f32> {
var uv = in.uv;
var out = vec3(0.0);
if uv.y > 0.5 {
Expand All @@ -58,7 +55,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
}
var color = vec4(out, 1.0);
#ifdef TONEMAP_IN_SHADER
color = tone_mapping(color);
color = tone_mapping(color, bevy_pbr::mesh_view_bindings::view.color_grading);
#endif
return color;
}
Loading

0 comments on commit 10f5c92

Please sign in to comment.