improve shader import model (#5703)
# 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.
This commit is contained in:
parent
0f4d16aa3c
commit
10f5c92068
@ -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
|
// 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> {
|
fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
|
||||||
let L = c.x;
|
let L = c.x;
|
||||||
@ -22,12 +22,8 @@ fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FragmentInput {
|
|
||||||
#import bevy_pbr::mesh_vertex_output
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {
|
||||||
let speed = 2.0;
|
let speed = 2.0;
|
||||||
// The globals binding contains various global values like time
|
// The globals binding contains various global values like time
|
||||||
// which is the time since startup in seconds
|
// which is the time since startup in seconds
|
||||||
|
@ -1,60 +1,51 @@
|
|||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
#import bevy_pbr::mesh_bindings
|
#import bevy_pbr::mesh_view_bindings view
|
||||||
|
#import bevy_pbr::pbr_types STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT
|
||||||
#import bevy_pbr::pbr_types
|
#import bevy_core_pipeline::tonemapping tone_mapping
|
||||||
#import bevy_pbr::utils
|
#import bevy_pbr::pbr_functions as fns
|
||||||
#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
|
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var my_array_texture: texture_2d_array<f32>;
|
var my_array_texture: texture_2d_array<f32>;
|
||||||
@group(1) @binding(1)
|
@group(1) @binding(1)
|
||||||
var my_array_texture_sampler: sampler;
|
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
|
@fragment
|
||||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
fn fragment(
|
||||||
let layer = i32(in.world_position.x) & 0x3;
|
@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
|
// Prepare a 'processed' StandardMaterial by sampling all textures to resolve
|
||||||
// the material members
|
// 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
|
#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
|
#endif
|
||||||
|
|
||||||
pbr_input.frag_coord = in.frag_coord;
|
pbr_input.frag_coord = mesh.position;
|
||||||
pbr_input.world_position = in.world_position;
|
pbr_input.world_position = mesh.world_position;
|
||||||
pbr_input.world_normal = prepare_world_normal(
|
pbr_input.world_normal = fns::prepare_world_normal(
|
||||||
in.world_normal,
|
mesh.world_normal,
|
||||||
(pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
(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.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.material.flags,
|
||||||
pbr_input.world_normal,
|
mesh.world_normal,
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
in.world_tangent,
|
mesh.world_tangent,
|
||||||
#endif
|
#endif
|
||||||
#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);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
|
||||||
#ifdef CUBEMAP_ARRAY
|
#ifdef CUBEMAP_ARRAY
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
@ -13,9 +13,9 @@ var base_color_sampler: sampler;
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
#import bevy_pbr::mesh_vertex_output
|
mesh: MeshVertexOutput,
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @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(
|
return textureSample(
|
||||||
base_color_texture,
|
base_color_texture,
|
||||||
base_color_sampler,
|
base_color_sampler,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#import bevy_sprite::mesh2d_view_bindings
|
#import bevy_sprite::mesh2d_view_bindings globals
|
||||||
#import bevy_sprite::mesh2d_bindings
|
#import bevy_sprite::mesh2d_bindings mesh
|
||||||
#import bevy_sprite::mesh2d_functions
|
#import bevy_sprite::mesh2d_functions mesh2d_position_local_to_clip
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
|
@ -10,7 +10,11 @@ layout(set = 1, binding = 0) uniform CustomMaterial {
|
|||||||
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
|
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
|
||||||
layout(set = 1, binding = 2) uniform sampler CustomMaterial_sampler;
|
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() {
|
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);
|
o_Target = Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
|
||||||
struct CustomMaterial {
|
struct CustomMaterial {
|
||||||
color: vec4<f32>,
|
color: vec4<f32>,
|
||||||
};
|
};
|
||||||
@ -11,7 +13,7 @@ var base_color_sampler: sampler;
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
#import bevy_pbr::mesh_vertex_output
|
mesh: MeshVertexOutput,
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @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);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_view_bindings view
|
||||||
#import bevy_pbr::utils
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
#import bevy_pbr::utils coords_to_viewport_uv
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var texture: texture_2d<f32>;
|
var texture: texture_2d<f32>;
|
||||||
@ -8,10 +9,9 @@ var texture_sampler: sampler;
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
@builtin(position) position: vec4<f32>,
|
mesh: MeshVertexOutput,
|
||||||
#import bevy_pbr::mesh_vertex_output
|
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @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);
|
let color = textureSample(texture, texture_sampler, viewport_uv);
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
#import bevy_pbr::mesh_bindings
|
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||||
|
|
||||||
struct CustomMaterial {
|
struct CustomMaterial {
|
||||||
color: vec4<f32>,
|
color: vec4<f32>,
|
||||||
@ -7,9 +7,6 @@ struct CustomMaterial {
|
|||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> material: CustomMaterial;
|
var<uniform> material: CustomMaterial;
|
||||||
|
|
||||||
// NOTE: Bindings must come before functions that use them!
|
|
||||||
#import bevy_pbr::mesh_functions
|
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
@location(1) blend_color: vec4<f32>,
|
@location(1) blend_color: vec4<f32>,
|
||||||
@ -23,7 +20,10 @@ struct VertexOutput {
|
|||||||
@vertex
|
@vertex
|
||||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||||
var out: 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;
|
out.blend_color = vertex.blend_color;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
#import bevy_pbr::mesh_types
|
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> mesh: Mesh;
|
|
||||||
|
|
||||||
// NOTE: Bindings must come before functions that use them!
|
|
||||||
#import bevy_pbr::mesh_functions
|
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
@ -25,7 +19,10 @@ struct VertexOutput {
|
|||||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||||
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
|
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
|
||||||
var out: VertexOutput;
|
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;
|
out.color = vertex.i_color;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
|
||||||
struct LineMaterial {
|
struct LineMaterial {
|
||||||
color: vec4<f32>,
|
color: vec4<f32>,
|
||||||
};
|
};
|
||||||
@ -7,7 +9,7 @@ var<uniform> material: LineMaterial;
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
#import bevy_pbr::mesh_vertex_output
|
mesh: MeshVertexOutput,
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @location(0) vec4<f32> {
|
||||||
return material.color;
|
return material.color;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
// As you can see, the triangle ends up bigger than the screen.
|
// 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.
|
// 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)
|
@group(0) @binding(0)
|
||||||
var screen_texture: texture_2d<f32>;
|
var screen_texture: texture_2d<f32>;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
|
||||||
struct CustomMaterial {
|
struct CustomMaterial {
|
||||||
color: vec4<f32>,
|
color: vec4<f32>,
|
||||||
};
|
};
|
||||||
@ -7,7 +9,7 @@ var<uniform> material: CustomMaterial;
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
#import bevy_pbr::mesh_vertex_output
|
mesh: MeshVertexOutput,
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @location(0) vec4<f32> {
|
||||||
#ifdef IS_RED
|
#ifdef IS_RED
|
||||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#import bevy_pbr::mesh_types
|
#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::prepass_utils
|
||||||
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
|
||||||
struct ShowPrepassSettings {
|
struct ShowPrepassSettings {
|
||||||
show_depth: u32,
|
show_depth: u32,
|
||||||
@ -14,23 +15,22 @@ var<uniform> settings: ShowPrepassSettings;
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
@builtin(position) frag_coord: vec4<f32>,
|
|
||||||
#ifdef MULTISAMPLED
|
#ifdef MULTISAMPLED
|
||||||
@builtin(sample_index) sample_index: u32,
|
@builtin(sample_index) sample_index: u32,
|
||||||
#endif
|
#endif
|
||||||
#import bevy_pbr::mesh_vertex_output
|
mesh: MeshVertexOutput,
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @location(0) vec4<f32> {
|
||||||
#ifndef MULTISAMPLED
|
#ifndef MULTISAMPLED
|
||||||
let sample_index = 0u;
|
let sample_index = 0u;
|
||||||
#endif
|
#endif
|
||||||
if settings.show_depth == 1u {
|
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);
|
return vec4(depth, depth, depth, 1.0);
|
||||||
} else if settings.show_normals == 1u {
|
} 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);
|
return vec4(normal, 1.0);
|
||||||
} else if settings.show_motion_vectors == 1u {
|
} 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);
|
return vec4(motion_vector / globals.delta_time, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var textures: binding_array<texture_2d<f32>>;
|
var textures: binding_array<texture_2d<f32>>;
|
||||||
@group(1) @binding(1)
|
@group(1) @binding(1)
|
||||||
@ -7,11 +9,11 @@ var nearest_sampler: sampler;
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(
|
fn fragment(
|
||||||
#import bevy_pbr::mesh_vertex_output
|
mesh: MeshVertexOutput,
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @location(0) vec4<f32> {
|
||||||
// Select the texture to sample from using non-uniform uv coordinates
|
// 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 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);
|
return textureSample(textures[index], nearest_sampler, inner_uv);
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_view_bindings
|
||||||
#import bevy_pbr::mesh_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
|
#ifdef TONEMAP_IN_SHADER
|
||||||
#import bevy_core_pipeline::tonemapping
|
#import bevy_core_pipeline::tonemapping
|
||||||
#endif
|
#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
|
// Sweep across hues on y axis with value from 0.0 to +15EV across x axis
|
||||||
// quantized into 24 steps for both axis.
|
// quantized into 24 steps for both axis.
|
||||||
fn color_sweep(uv: vec2<f32>) -> vec3<f32> {
|
fn color_sweep(uv: vec2<f32>) -> vec3<f32> {
|
||||||
@ -47,7 +42,9 @@ fn continuous_hue(uv: vec2<f32>) -> vec3<f32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
fn fragment(
|
||||||
|
in: MeshVertexOutput,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
var uv = in.uv;
|
var uv = in.uv;
|
||||||
var out = vec3(0.0);
|
var out = vec3(0.0);
|
||||||
if uv.y > 0.5 {
|
if uv.y > 0.5 {
|
||||||
@ -58,7 +55,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
}
|
}
|
||||||
var color = vec4(out, 1.0);
|
var color = vec4(out, 1.0);
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
color = tone_mapping(color);
|
color = tone_mapping(color, bevy_pbr::mesh_view_bindings::view.color_grading);
|
||||||
#endif
|
#endif
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
@ -406,7 +406,9 @@ impl AddAsset for App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads an internal asset.
|
/// Loads an internal asset from a project source file.
|
||||||
|
/// the file and its path are passed to the loader function, together with any additional parameters.
|
||||||
|
/// the resulting asset is stored in the app's asset server.
|
||||||
///
|
///
|
||||||
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
|
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
|
||||||
/// using the conventional API. See [`DebugAssetServerPlugin`](crate::debug_asset_server::DebugAssetServerPlugin).
|
/// using the conventional API. See [`DebugAssetServerPlugin`](crate::debug_asset_server::DebugAssetServerPlugin).
|
||||||
@ -427,20 +429,59 @@ macro_rules! load_internal_asset {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||||
assets.set_untracked($handle, ($loader)(include_str!($path_str)));
|
assets.set_untracked(
|
||||||
|
$handle,
|
||||||
|
($loader)(
|
||||||
|
include_str!($path_str),
|
||||||
|
std::path::Path::new(file!())
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join($path_str)
|
||||||
|
.to_string_lossy(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
// we can't support params without variadic arguments, so internal assets with additional params can't be hot-reloaded
|
||||||
|
($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)+) => {{
|
||||||
|
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||||
|
assets.set_untracked(
|
||||||
|
$handle,
|
||||||
|
($loader)(
|
||||||
|
include_str!($path_str),
|
||||||
|
std::path::Path::new(file!())
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join($path_str)
|
||||||
|
.to_string_lossy(),
|
||||||
|
$($param),+
|
||||||
|
),
|
||||||
|
);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads an internal asset.
|
/// Loads an internal asset from a project source file.
|
||||||
|
/// the file and its path are passed to the loader function, together with any additional parameters.
|
||||||
|
/// the resulting asset is stored in the app's asset server.
|
||||||
///
|
///
|
||||||
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
|
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
|
||||||
/// using the conventional API. See `DebugAssetServerPlugin`.
|
/// using the conventional API. See `DebugAssetServerPlugin`.
|
||||||
#[cfg(not(feature = "debug_asset_server"))]
|
#[cfg(not(feature = "debug_asset_server"))]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! load_internal_asset {
|
macro_rules! load_internal_asset {
|
||||||
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{
|
($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)*) => {{
|
||||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||||
assets.set_untracked($handle, ($loader)(include_str!($path_str)));
|
assets.set_untracked(
|
||||||
|
$handle,
|
||||||
|
($loader)(
|
||||||
|
include_str!($path_str),
|
||||||
|
std::path::Path::new(file!())
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join($path_str)
|
||||||
|
.to_string_lossy(),
|
||||||
|
$($param),*
|
||||||
|
),
|
||||||
|
);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +506,18 @@ macro_rules! load_internal_binary_asset {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||||
assets.set_untracked($handle, ($loader)(include_bytes!($path_str).as_ref()));
|
assets.set_untracked(
|
||||||
|
$handle,
|
||||||
|
($loader)(
|
||||||
|
include_bytes!($path_str).as_ref(),
|
||||||
|
std::path::Path::new(file!())
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join($path_str)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,7 +530,18 @@ macro_rules! load_internal_binary_asset {
|
|||||||
macro_rules! load_internal_binary_asset {
|
macro_rules! load_internal_binary_asset {
|
||||||
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{
|
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{
|
||||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||||
assets.set_untracked($handle, ($loader)(include_bytes!($path_str).as_ref()));
|
assets.set_untracked(
|
||||||
|
$handle,
|
||||||
|
($loader)(
|
||||||
|
include_bytes!($path_str).as_ref(),
|
||||||
|
std::path::Path::new(file!())
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join($path_str)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ pub(crate) fn sync_debug_assets<T: Asset + Clone>(
|
|||||||
/// If this feels a bit odd ... that's because it is. This was built to improve the UX of the
|
/// If this feels a bit odd ... that's because it is. This was built to improve the UX of the
|
||||||
/// `load_internal_asset` macro.
|
/// `load_internal_asset` macro.
|
||||||
pub fn register_handle_with_loader<A: Asset, T>(
|
pub fn register_handle_with_loader<A: Asset, T>(
|
||||||
_loader: fn(T) -> A,
|
_loader: fn(T, String) -> A,
|
||||||
app: &mut DebugAssetApp,
|
app: &mut DebugAssetApp,
|
||||||
handle: HandleUntyped,
|
handle: HandleUntyped,
|
||||||
file_path: &str,
|
file_path: &str,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var in_texture: texture_2d<f32>;
|
var in_texture: texture_2d<f32>;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
// Tweaks by mrDIMAS - https://github.com/FyroxEngine/Fyrox/blob/master/src/renderer/shaders/fxaa_fs.glsl
|
// Tweaks by mrDIMAS - https://github.com/FyroxEngine/Fyrox/blob/master/src/renderer/shaders/fxaa_fs.glsl
|
||||||
|
|
||||||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var screenTexture: texture_2d<f32>;
|
var screenTexture: texture_2d<f32>;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#import bevy_render::view
|
#import bevy_render::view View
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var skybox: texture_cube<f32>;
|
var skybox: texture_cube<f32>;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
#define TONEMAPPING_PASS
|
||||||
|
|
||||||
#import bevy_render::view
|
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||||
|
#import bevy_render::view View
|
||||||
|
#import bevy_core_pipeline::tonemapping tone_mapping, powsafe, screen_space_dither
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var<uniform> view: View;
|
var<uniform> view: View;
|
||||||
@ -20,11 +22,11 @@ var dt_lut_sampler: sampler;
|
|||||||
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
||||||
let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv);
|
let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv);
|
||||||
|
|
||||||
var output_rgb = tone_mapping(hdr_color).rgb;
|
var output_rgb = tone_mapping(hdr_color, view.color_grading).rgb;
|
||||||
|
|
||||||
#ifdef DEBAND_DITHER
|
#ifdef DEBAND_DITHER
|
||||||
output_rgb = powsafe(output_rgb.rgb, 1.0 / 2.2);
|
output_rgb = powsafe(output_rgb.rgb, 1.0 / 2.2);
|
||||||
output_rgb = output_rgb + screen_space_dither(in.position.xy);
|
output_rgb = output_rgb + bevy_core_pipeline::tonemapping::screen_space_dither(in.position.xy);
|
||||||
// This conversion back to linear space is required because our output texture format is
|
// This conversion back to linear space is required because our output texture format is
|
||||||
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
|
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
|
||||||
output_rgb = powsafe(output_rgb.rgb, 2.2);
|
output_rgb = powsafe(output_rgb.rgb, 2.2);
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
#define_import_path bevy_core_pipeline::tonemapping
|
#define_import_path bevy_core_pipeline::tonemapping
|
||||||
|
|
||||||
|
#import bevy_render::view View, ColorGrading
|
||||||
|
|
||||||
|
// hack !! not sure what to do with this
|
||||||
|
#ifdef TONEMAPPING_PASS
|
||||||
|
@group(0) @binding(3)
|
||||||
|
var dt_lut_texture: texture_3d<f32>;
|
||||||
|
@group(0) @binding(4)
|
||||||
|
var dt_lut_sampler: sampler;
|
||||||
|
#else
|
||||||
|
@group(0) @binding(15)
|
||||||
|
var dt_lut_texture: texture_3d<f32>;
|
||||||
|
@group(0) @binding(16)
|
||||||
|
var dt_lut_sampler: sampler;
|
||||||
|
#endif
|
||||||
|
|
||||||
fn sample_current_lut(p: vec3<f32>) -> vec3<f32> {
|
fn sample_current_lut(p: vec3<f32>) -> vec3<f32> {
|
||||||
// Don't include code that will try to sample from LUTs if tonemap method doesn't require it
|
// Don't include code that will try to sample from LUTs if tonemap method doesn't require it
|
||||||
@ -48,7 +62,7 @@ fn tonemap_curve(v: f32) -> f32 {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tonemap_curve3(v: vec3<f32>) -> vec3<f32> {
|
fn tonemap_curve3_(v: vec3<f32>) -> vec3<f32> {
|
||||||
return vec3(tonemap_curve(v.r), tonemap_curve(v.g), tonemap_curve(v.b));
|
return vec3(tonemap_curve(v.r), tonemap_curve(v.g), tonemap_curve(v.b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +79,7 @@ fn somewhat_boring_display_transform(col: vec3<f32>) -> vec3<f32> {
|
|||||||
let tm_luma = tonemap_curve(ycbcr.x);
|
let tm_luma = tonemap_curve(ycbcr.x);
|
||||||
let tm0 = boring_color.rgb * max(0.0, tm_luma / max(1e-5, tonemapping_luminance(boring_color.rgb)));
|
let tm0 = boring_color.rgb * max(0.0, tm_luma / max(1e-5, tonemapping_luminance(boring_color.rgb)));
|
||||||
let final_mult = 0.97;
|
let final_mult = 0.97;
|
||||||
let tm1 = tonemap_curve3(desat_col);
|
let tm1 = tonemap_curve3_(desat_col);
|
||||||
|
|
||||||
boring_color = mix(tm0, tm1, bt * bt);
|
boring_color = mix(tm0, tm1, bt * bt);
|
||||||
|
|
||||||
@ -167,7 +181,7 @@ fn saturation(color: vec3<f32>, saturationAmount: f32) -> vec3<f32> {
|
|||||||
Similar to OCIO lg2 AllocationTransform.
|
Similar to OCIO lg2 AllocationTransform.
|
||||||
ref[0]
|
ref[0]
|
||||||
*/
|
*/
|
||||||
fn convertOpenDomainToNormalizedLog2(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> {
|
fn convertOpenDomainToNormalizedLog2_(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> {
|
||||||
let in_midgray = 0.18;
|
let in_midgray = 0.18;
|
||||||
|
|
||||||
// remove negative before log transform
|
// remove negative before log transform
|
||||||
@ -210,7 +224,7 @@ fn applyAgXLog(Image: vec3<f32>) -> vec3<f32> {
|
|||||||
let b = dot(prepared_image, vec3(0.04237565, 0.0784336, 0.87914297));
|
let b = dot(prepared_image, vec3(0.04237565, 0.0784336, 0.87914297));
|
||||||
prepared_image = vec3(r, g, b);
|
prepared_image = vec3(r, g, b);
|
||||||
|
|
||||||
prepared_image = convertOpenDomainToNormalizedLog2(prepared_image, -10.0, 6.5);
|
prepared_image = convertOpenDomainToNormalizedLog2_(prepared_image, -10.0, 6.5);
|
||||||
|
|
||||||
prepared_image = clamp(prepared_image, vec3(0.0), vec3(1.0));
|
prepared_image = clamp(prepared_image, vec3(0.0), vec3(1.0));
|
||||||
return prepared_image;
|
return prepared_image;
|
||||||
@ -226,7 +240,7 @@ fn applyLUT3D(Image: vec3<f32>, block_size: f32) -> vec3<f32> {
|
|||||||
|
|
||||||
fn sample_blender_filmic_lut(stimulus: vec3<f32>) -> vec3<f32> {
|
fn sample_blender_filmic_lut(stimulus: vec3<f32>) -> vec3<f32> {
|
||||||
let block_size = 64.0;
|
let block_size = 64.0;
|
||||||
let normalized = saturate(convertOpenDomainToNormalizedLog2(stimulus, -11.0, 12.0));
|
let normalized = saturate(convertOpenDomainToNormalizedLog2_(stimulus, -11.0, 12.0));
|
||||||
return applyLUT3D(normalized, block_size);
|
return applyLUT3D(normalized, block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +284,7 @@ fn screen_space_dither(frag_coord: vec2<f32>) -> vec3<f32> {
|
|||||||
return (dither - 0.5) / 255.0;
|
return (dither - 0.5) / 255.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
fn tone_mapping(in: vec4<f32>, color_grading: ColorGrading) -> vec4<f32> {
|
||||||
var color = max(in.rgb, vec3(0.0));
|
var color = max(in.rgb, vec3(0.0));
|
||||||
|
|
||||||
// Possible future grading:
|
// Possible future grading:
|
||||||
@ -282,9 +296,9 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
|||||||
// color += color * luma.xxx * 1.0;
|
// color += color * luma.xxx * 1.0;
|
||||||
|
|
||||||
// Linear pre tonemapping grading
|
// Linear pre tonemapping grading
|
||||||
color = saturation(color, view.color_grading.pre_saturation);
|
color = saturation(color, color_grading.pre_saturation);
|
||||||
color = powsafe(color, view.color_grading.gamma);
|
color = powsafe(color, color_grading.gamma);
|
||||||
color = color * powsafe(vec3(2.0), view.color_grading.exposure);
|
color = color * powsafe(vec3(2.0), color_grading.exposure);
|
||||||
color = max(color, vec3(0.0));
|
color = max(color, vec3(0.0));
|
||||||
|
|
||||||
// tone_mapping
|
// tone_mapping
|
||||||
@ -308,7 +322,7 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Perceptual post tonemapping grading
|
// Perceptual post tonemapping grading
|
||||||
color = saturation(color, view.color_grading.post_saturation);
|
color = saturation(color, color_grading.post_saturation);
|
||||||
|
|
||||||
return vec4(color, in.a);
|
return vec4(color, in.a);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#ifdef GIZMO_3D
|
// TODO use common view binding
|
||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_render::view View
|
||||||
#else
|
|
||||||
#import bevy_sprite::mesh2d_view_bindings
|
@group(0) @binding(0)
|
||||||
#endif
|
var<uniform> view: View;
|
||||||
|
|
||||||
|
|
||||||
struct LineGizmoUniform {
|
struct LineGizmoUniform {
|
||||||
line_width: f32,
|
line_width: f32,
|
||||||
|
@ -12,10 +12,7 @@ use bevy_ecs::{
|
|||||||
system::{Query, Res, ResMut, Resource},
|
system::{Query, Res, ResMut, Resource},
|
||||||
world::{FromWorld, World},
|
world::{FromWorld, World},
|
||||||
};
|
};
|
||||||
use bevy_pbr::{
|
use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup};
|
||||||
MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup, MAX_CASCADES_PER_LIGHT,
|
|
||||||
MAX_DIRECTIONAL_LIGHTS,
|
|
||||||
};
|
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
render_asset::RenderAssets,
|
render_asset::RenderAssets,
|
||||||
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
|
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
|
||||||
@ -78,15 +75,6 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
|
|||||||
"SIXTEEN_BYTE_ALIGNMENT".into(),
|
"SIXTEEN_BYTE_ALIGNMENT".into(),
|
||||||
];
|
];
|
||||||
|
|
||||||
shader_defs.push(ShaderDefVal::Int(
|
|
||||||
"MAX_DIRECTIONAL_LIGHTS".to_string(),
|
|
||||||
MAX_DIRECTIONAL_LIGHTS as i32,
|
|
||||||
));
|
|
||||||
shader_defs.push(ShaderDefVal::Int(
|
|
||||||
"MAX_CASCADES_PER_LIGHT".to_string(),
|
|
||||||
MAX_CASCADES_PER_LIGHT as i32,
|
|
||||||
));
|
|
||||||
|
|
||||||
if key.perspective {
|
if key.perspective {
|
||||||
shader_defs.push("PERSPECTIVE".into());
|
shader_defs.push("PERSPECTIVE".into());
|
||||||
}
|
}
|
||||||
|
@ -30,3 +30,4 @@ bitflags = "2.3"
|
|||||||
# direct dependency required for derive macro
|
# direct dependency required for derive macro
|
||||||
bytemuck = { version = "1", features = ["derive"] }
|
bytemuck = { version = "1", features = ["derive"] }
|
||||||
radsort = "0.1"
|
radsort = "0.1"
|
||||||
|
naga_oil = "0.8"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#define_import_path bevy_pbr::environment_map
|
#define_import_path bevy_pbr::environment_map
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_view_bindings as bindings
|
||||||
|
|
||||||
struct EnvironmentMapLight {
|
struct EnvironmentMapLight {
|
||||||
diffuse: vec3<f32>,
|
diffuse: vec3<f32>,
|
||||||
@ -20,9 +21,9 @@ fn environment_map_light(
|
|||||||
// Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
|
// Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
|
||||||
// Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform
|
// Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform
|
||||||
// because textureNumLevels() does not work on WebGL2
|
// because textureNumLevels() does not work on WebGL2
|
||||||
let radiance_level = perceptual_roughness * f32(lights.environment_map_smallest_specular_mip_level);
|
let radiance_level = perceptual_roughness * f32(bindings::lights.environment_map_smallest_specular_mip_level);
|
||||||
let irradiance = textureSample(environment_map_diffuse, environment_map_sampler, vec3(N.xy, -N.z)).rgb;
|
let irradiance = textureSample(bindings::environment_map_diffuse, bindings::environment_map_sampler, vec3(N.xy, -N.z)).rgb;
|
||||||
let radiance = textureSampleLevel(environment_map_specular, environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb;
|
let radiance = textureSampleLevel(bindings::environment_map_specular, bindings::environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb;
|
||||||
|
|
||||||
// Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf
|
// Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf
|
||||||
// Useful reference: https://bruop.github.io/ibl
|
// Useful reference: https://bruop.github.io/ibl
|
||||||
|
@ -31,8 +31,8 @@ use bevy_render::{
|
|||||||
BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType,
|
BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType,
|
||||||
ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
|
ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
|
||||||
DynamicUniformBuffer, FragmentState, FrontFace, MultisampleState, PipelineCache,
|
DynamicUniformBuffer, FragmentState, FrontFace, MultisampleState, PipelineCache,
|
||||||
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderDefVal, ShaderRef,
|
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderRef, ShaderStages,
|
||||||
ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
||||||
SpecializedMeshPipelines, StencilFaceState, StencilState, TextureSampleType,
|
SpecializedMeshPipelines, StencilFaceState, StencilState, TextureSampleType,
|
||||||
TextureViewDimension, VertexState,
|
TextureViewDimension, VertexState,
|
||||||
},
|
},
|
||||||
@ -47,7 +47,7 @@ use bevy_utils::tracing::error;
|
|||||||
use crate::{
|
use crate::{
|
||||||
prepare_lights, setup_morph_and_skinning_defs, AlphaMode, DrawMesh, Material, MaterialPipeline,
|
prepare_lights, setup_morph_and_skinning_defs, AlphaMode, DrawMesh, Material, MaterialPipeline,
|
||||||
MaterialPipelineKey, MeshLayouts, MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials,
|
MaterialPipelineKey, MeshLayouts, MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials,
|
||||||
SetMaterialBindGroup, SetMeshBindGroup, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS,
|
SetMaterialBindGroup, SetMeshBindGroup,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{hash::Hash, marker::PhantomData};
|
use std::{hash::Hash, marker::PhantomData};
|
||||||
@ -373,14 +373,6 @@ where
|
|||||||
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
|
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
shader_defs.push(ShaderDefVal::UInt(
|
|
||||||
"MAX_DIRECTIONAL_LIGHTS".to_string(),
|
|
||||||
MAX_DIRECTIONAL_LIGHTS as u32,
|
|
||||||
));
|
|
||||||
shader_defs.push(ShaderDefVal::UInt(
|
|
||||||
"MAX_CASCADES_PER_LIGHT".to_string(),
|
|
||||||
MAX_CASCADES_PER_LIGHT as u32,
|
|
||||||
));
|
|
||||||
if key.mesh_key.contains(MeshPipelineKey::DEPTH_CLAMP_ORTHO) {
|
if key.mesh_key.contains(MeshPipelineKey::DEPTH_CLAMP_ORTHO) {
|
||||||
shader_defs.push("DEPTH_CLAMP_ORTHO".into());
|
shader_defs.push("DEPTH_CLAMP_ORTHO".into());
|
||||||
// PERF: This line forces the "prepass fragment shader" to always run in
|
// PERF: This line forces the "prepass fragment shader" to always run in
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#import bevy_pbr::prepass_bindings
|
#import bevy_pbr::prepass_bindings
|
||||||
#import bevy_pbr::mesh_functions
|
#import bevy_pbr::mesh_functions
|
||||||
|
#import bevy_pbr::skinning
|
||||||
|
#import bevy_pbr::morph
|
||||||
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
|
|
||||||
// Most of these attributes are not used in the default prepass fragment shader, but they are still needed so we can
|
// Most of these attributes are not used in the default prepass fragment shader, but they are still needed so we can
|
||||||
// pass them to custom prepass shaders like pbr_prepass.wgsl.
|
// pass them to custom prepass shaders like pbr_prepass.wgsl.
|
||||||
@ -83,12 +86,12 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
var model = skin_model(vertex.joint_indices, vertex.joint_weights);
|
var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
|
||||||
#else // SKINNED
|
#else // SKINNED
|
||||||
var model = mesh.model;
|
var model = mesh.model;
|
||||||
#endif // SKINNED
|
#endif // SKINNED
|
||||||
|
|
||||||
out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0));
|
out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0));
|
||||||
#ifdef DEPTH_CLAMP_ORTHO
|
#ifdef DEPTH_CLAMP_ORTHO
|
||||||
out.clip_position_unclamped = out.clip_position;
|
out.clip_position_unclamped = out.clip_position;
|
||||||
out.clip_position.z = min(out.clip_position.z, 1.0);
|
out.clip_position.z = min(out.clip_position.z, 1.0);
|
||||||
@ -100,19 +103,19 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||||||
|
|
||||||
#ifdef NORMAL_PREPASS
|
#ifdef NORMAL_PREPASS
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
out.world_normal = skin_normals(model, vertex.normal);
|
out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
|
||||||
#else // SKINNED
|
#else // SKINNED
|
||||||
out.world_normal = mesh_normal_local_to_world(vertex.normal);
|
out.world_normal = bevy_pbr::mesh_functions::mesh_normal_local_to_world(vertex.normal);
|
||||||
#endif // SKINNED
|
#endif // SKINNED
|
||||||
|
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent);
|
out.world_tangent = bevy_pbr::mesh_functions::mesh_tangent_local_to_world(model, vertex.tangent);
|
||||||
#endif // VERTEX_TANGENTS
|
#endif // VERTEX_TANGENTS
|
||||||
#endif // NORMAL_PREPASS
|
#endif // NORMAL_PREPASS
|
||||||
|
|
||||||
#ifdef MOTION_VECTOR_PREPASS
|
#ifdef MOTION_VECTOR_PREPASS
|
||||||
out.world_position = mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
|
out.world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
|
||||||
out.previous_world_position = mesh_position_local_to_world(mesh.previous_model, vec4<f32>(vertex.position, 1.0));
|
out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(mesh.previous_model, vec4<f32>(vertex.position, 1.0));
|
||||||
#endif // MOTION_VECTOR_PREPASS
|
#endif // MOTION_VECTOR_PREPASS
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@ -165,9 +168,9 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
|
|||||||
#endif // DEPTH_CLAMP_ORTHO
|
#endif // DEPTH_CLAMP_ORTHO
|
||||||
|
|
||||||
#ifdef MOTION_VECTOR_PREPASS
|
#ifdef MOTION_VECTOR_PREPASS
|
||||||
let clip_position_t = view.unjittered_view_proj * in.world_position;
|
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 clip_position = clip_position_t.xy / clip_position_t.w;
|
||||||
let previous_clip_position_t = previous_view_proj * in.previous_world_position;
|
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;
|
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
|
// 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
|
// in the range -1,1 to allow offsetting from the one corner to the
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#define_import_path bevy_pbr::prepass_bindings
|
#define_import_path bevy_pbr::prepass_bindings
|
||||||
|
#import bevy_render::view View
|
||||||
#import bevy_pbr::mesh_view_types
|
#import bevy_render::globals Globals
|
||||||
#import bevy_pbr::mesh_types
|
#import bevy_pbr::mesh_types
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
@ -16,18 +16,4 @@ var<uniform> previous_view_proj: mat4x4<f32>;
|
|||||||
// Material bindings will be in @group(1)
|
// Material bindings will be in @group(1)
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var<uniform> mesh: Mesh;
|
var<uniform> mesh: bevy_pbr::mesh_types::Mesh;
|
||||||
|
|
||||||
#ifdef SKINNED
|
|
||||||
@group(2) @binding(1)
|
|
||||||
var<uniform> joint_matrices: SkinnedMesh;
|
|
||||||
#import bevy_pbr::skinning
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef MORPH_TARGETS
|
|
||||||
@group(2) @binding(2)
|
|
||||||
var<uniform> morph_weights: MorphWeights;
|
|
||||||
@group(2) @binding(3)
|
|
||||||
var morph_targets: texture_3d<f32>;
|
|
||||||
#import bevy_pbr::morph
|
|
||||||
#endif
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#define_import_path bevy_pbr::prepass_utils
|
#define_import_path bevy_pbr::prepass_utils
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_view_bindings as view_bindings
|
||||||
|
|
||||||
#ifndef DEPTH_PREPASS
|
#ifndef DEPTH_PREPASS
|
||||||
fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
|
fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
|
||||||
#ifdef MULTISAMPLED
|
#ifdef MULTISAMPLED
|
||||||
let depth_sample = textureLoad(depth_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
|
let depth_sample = textureLoad(view_bindings::depth_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
|
||||||
#else
|
#else
|
||||||
let depth_sample = textureLoad(depth_prepass_texture, vec2<i32>(frag_coord.xy), 0);
|
let depth_sample = textureLoad(view_bindings::depth_prepass_texture, vec2<i32>(frag_coord.xy), 0);
|
||||||
#endif
|
#endif
|
||||||
return depth_sample;
|
return depth_sample;
|
||||||
}
|
}
|
||||||
@ -14,9 +16,9 @@ fn prepass_depth(frag_coord: vec4<f32>, sample_index: u32) -> f32 {
|
|||||||
#ifndef NORMAL_PREPASS
|
#ifndef NORMAL_PREPASS
|
||||||
fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
|
fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
|
||||||
#ifdef MULTISAMPLED
|
#ifdef MULTISAMPLED
|
||||||
let normal_sample = textureLoad(normal_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
|
let normal_sample = textureLoad(view_bindings::normal_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
|
||||||
#else
|
#else
|
||||||
let normal_sample = textureLoad(normal_prepass_texture, vec2<i32>(frag_coord.xy), 0);
|
let normal_sample = textureLoad(view_bindings::normal_prepass_texture, vec2<i32>(frag_coord.xy), 0);
|
||||||
#endif // MULTISAMPLED
|
#endif // MULTISAMPLED
|
||||||
return normal_sample.xyz * 2.0 - vec3(1.0);
|
return normal_sample.xyz * 2.0 - vec3(1.0);
|
||||||
}
|
}
|
||||||
@ -25,9 +27,9 @@ fn prepass_normal(frag_coord: vec4<f32>, sample_index: u32) -> vec3<f32> {
|
|||||||
#ifndef MOTION_VECTOR_PREPASS
|
#ifndef MOTION_VECTOR_PREPASS
|
||||||
fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> {
|
fn prepass_motion_vector(frag_coord: vec4<f32>, sample_index: u32) -> vec2<f32> {
|
||||||
#ifdef MULTISAMPLED
|
#ifdef MULTISAMPLED
|
||||||
let motion_vector_sample = textureLoad(motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
|
let motion_vector_sample = textureLoad(view_bindings::motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), i32(sample_index));
|
||||||
#else
|
#else
|
||||||
let motion_vector_sample = textureLoad(motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), 0);
|
let motion_vector_sample = textureLoad(view_bindings::motion_vector_prepass_texture, vec2<i32>(frag_coord.xy), 0);
|
||||||
#endif
|
#endif
|
||||||
return motion_vector_sample.rg;
|
return motion_vector_sample.rg;
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,31 @@
|
|||||||
#define_import_path bevy_pbr::clustered_forward
|
#define_import_path bevy_pbr::clustered_forward
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_view_bindings as bindings
|
||||||
|
#import bevy_pbr::utils hsv2rgb
|
||||||
|
|
||||||
// NOTE: Keep in sync with bevy_pbr/src/light.rs
|
// NOTE: Keep in sync with bevy_pbr/src/light.rs
|
||||||
fn view_z_to_z_slice(view_z: f32, is_orthographic: bool) -> u32 {
|
fn view_z_to_z_slice(view_z: f32, is_orthographic: bool) -> u32 {
|
||||||
var z_slice: u32 = 0u;
|
var z_slice: u32 = 0u;
|
||||||
if (is_orthographic) {
|
if (is_orthographic) {
|
||||||
// NOTE: view_z is correct in the orthographic case
|
// NOTE: view_z is correct in the orthographic case
|
||||||
z_slice = u32(floor((view_z - lights.cluster_factors.z) * lights.cluster_factors.w));
|
z_slice = u32(floor((view_z - bindings::lights.cluster_factors.z) * bindings::lights.cluster_factors.w));
|
||||||
} else {
|
} else {
|
||||||
// NOTE: had to use -view_z to make it positive else log(negative) is nan
|
// NOTE: had to use -view_z to make it positive else log(negative) is nan
|
||||||
z_slice = u32(log(-view_z) * lights.cluster_factors.z - lights.cluster_factors.w + 1.0);
|
z_slice = u32(log(-view_z) * bindings::lights.cluster_factors.z - bindings::lights.cluster_factors.w + 1.0);
|
||||||
}
|
}
|
||||||
// NOTE: We use min as we may limit the far z plane used for clustering to be closer than
|
// NOTE: We use min as we may limit the far z plane used for clustering to be closer than
|
||||||
// the furthest thing being drawn. This means that we need to limit to the maximum cluster.
|
// the furthest thing being drawn. This means that we need to limit to the maximum cluster.
|
||||||
return min(z_slice, lights.cluster_dimensions.z - 1u);
|
return min(z_slice, bindings::lights.cluster_dimensions.z - 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: bool) -> u32 {
|
fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: bool) -> u32 {
|
||||||
let xy = vec2<u32>(floor((frag_coord - view.viewport.xy) * lights.cluster_factors.xy));
|
let xy = vec2<u32>(floor((frag_coord - bindings::view.viewport.xy) * bindings::lights.cluster_factors.xy));
|
||||||
let z_slice = view_z_to_z_slice(view_z, is_orthographic);
|
let z_slice = view_z_to_z_slice(view_z, is_orthographic);
|
||||||
// NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer
|
// NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer
|
||||||
// arrays based on the cluster index.
|
// arrays based on the cluster index.
|
||||||
return min(
|
return min(
|
||||||
(xy.y * lights.cluster_dimensions.x + xy.x) * lights.cluster_dimensions.z + z_slice,
|
(xy.y * bindings::lights.cluster_dimensions.x + xy.x) * bindings::lights.cluster_dimensions.z + z_slice,
|
||||||
lights.cluster_dimensions.w - 1u
|
bindings::lights.cluster_dimensions.w - 1u
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,9 +33,9 @@ fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: b
|
|||||||
const CLUSTER_COUNT_SIZE = 9u;
|
const CLUSTER_COUNT_SIZE = 9u;
|
||||||
fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
|
fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
|
||||||
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
||||||
return cluster_offsets_and_counts.data[cluster_index].xyz;
|
return bindings::cluster_offsets_and_counts.data[cluster_index].xyz;
|
||||||
#else
|
#else
|
||||||
let offset_and_counts = cluster_offsets_and_counts.data[cluster_index >> 2u][cluster_index & ((1u << 2u) - 1u)];
|
let offset_and_counts = bindings::cluster_offsets_and_counts.data[cluster_index >> 2u][cluster_index & ((1u << 2u) - 1u)];
|
||||||
// [ 31 .. 18 | 17 .. 9 | 8 .. 0 ]
|
// [ 31 .. 18 | 17 .. 9 | 8 .. 0 ]
|
||||||
// [ offset | point light count | spot light count ]
|
// [ offset | point light count | spot light count ]
|
||||||
return vec3<u32>(
|
return vec3<u32>(
|
||||||
@ -45,11 +48,11 @@ fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> {
|
|||||||
|
|
||||||
fn get_light_id(index: u32) -> u32 {
|
fn get_light_id(index: u32) -> u32 {
|
||||||
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
||||||
return cluster_light_index_lists.data[index];
|
return bindings::cluster_light_index_lists.data[index];
|
||||||
#else
|
#else
|
||||||
// The index is correct but in cluster_light_index_lists we pack 4 u8s into a u32
|
// The index is correct but in cluster_light_index_lists we pack 4 u8s into a u32
|
||||||
// This means the index into cluster_light_index_lists is index / 4
|
// This means the index into cluster_light_index_lists is index / 4
|
||||||
let indices = cluster_light_index_lists.data[index >> 4u][(index >> 2u) & ((1u << 2u) - 1u)];
|
let indices = bindings::cluster_light_index_lists.data[index >> 4u][(index >> 2u) & ((1u << 2u) - 1u)];
|
||||||
// And index % 4 gives the sub-index of the u8 within the u32 so we shift by 8 * sub-index
|
// And index % 4 gives the sub-index of the u8 within the u32 so we shift by 8 * sub-index
|
||||||
return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u);
|
return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u);
|
||||||
#endif
|
#endif
|
||||||
@ -69,9 +72,9 @@ fn cluster_debug_visualization(
|
|||||||
var z_slice: u32 = view_z_to_z_slice(view_z, is_orthographic);
|
var z_slice: u32 = view_z_to_z_slice(view_z, is_orthographic);
|
||||||
// A hack to make the colors alternate a bit more
|
// A hack to make the colors alternate a bit more
|
||||||
if ((z_slice & 1u) == 1u) {
|
if ((z_slice & 1u) == 1u) {
|
||||||
z_slice = z_slice + lights.cluster_dimensions.z / 2u;
|
z_slice = z_slice + bindings::lights.cluster_dimensions.z / 2u;
|
||||||
}
|
}
|
||||||
let slice_color = hsv2rgb(f32(z_slice) / f32(lights.cluster_dimensions.z + 1u), 1.0, 0.5);
|
let slice_color = hsv2rgb(f32(z_slice) / f32(bindings::lights.cluster_dimensions.z + 1u), 1.0, 0.5);
|
||||||
output_color = vec4<f32>(
|
output_color = vec4<f32>(
|
||||||
(1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * slice_color,
|
(1.0 - cluster_overlay_alpha) * output_color.rgb + cluster_overlay_alpha * slice_color,
|
||||||
output_color.a
|
output_color.a
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#define_import_path bevy_pbr::fog
|
#define_import_path bevy_pbr::fog
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_view_bindings fog
|
||||||
|
#import bevy_pbr::mesh_view_types Fog
|
||||||
|
|
||||||
// Fog formulas adapted from:
|
// Fog formulas adapted from:
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
|
// https://learn.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
|
||||||
// https://catlikecoding.com/unity/tutorials/rendering/part-14/
|
// https://catlikecoding.com/unity/tutorials/rendering/part-14/
|
||||||
|
@ -86,7 +86,17 @@ impl Plugin for MeshRenderPlugin {
|
|||||||
app,
|
app,
|
||||||
MESH_VIEW_TYPES_HANDLE,
|
MESH_VIEW_TYPES_HANDLE,
|
||||||
"mesh_view_types.wgsl",
|
"mesh_view_types.wgsl",
|
||||||
Shader::from_wgsl
|
Shader::from_wgsl_with_defs,
|
||||||
|
vec![
|
||||||
|
ShaderDefVal::UInt(
|
||||||
|
"MAX_DIRECTIONAL_LIGHTS".into(),
|
||||||
|
MAX_DIRECTIONAL_LIGHTS as u32
|
||||||
|
),
|
||||||
|
ShaderDefVal::UInt(
|
||||||
|
"MAX_CASCADES_PER_LIGHT".into(),
|
||||||
|
MAX_CASCADES_PER_LIGHT as u32,
|
||||||
|
)
|
||||||
|
]
|
||||||
);
|
);
|
||||||
load_internal_asset!(
|
load_internal_asset!(
|
||||||
app,
|
app,
|
||||||
@ -709,15 +719,6 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||||||
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
|
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
shader_defs.push(ShaderDefVal::UInt(
|
|
||||||
"MAX_DIRECTIONAL_LIGHTS".to_string(),
|
|
||||||
MAX_DIRECTIONAL_LIGHTS as u32,
|
|
||||||
));
|
|
||||||
shader_defs.push(ShaderDefVal::UInt(
|
|
||||||
"MAX_CASCADES_PER_LIGHT".to_string(),
|
|
||||||
MAX_CASCADES_PER_LIGHT as u32,
|
|
||||||
));
|
|
||||||
|
|
||||||
if layout.contains(Mesh::ATTRIBUTE_UV_0) {
|
if layout.contains(Mesh::ATTRIBUTE_UV_0) {
|
||||||
shader_defs.push("VERTEX_UVS".into());
|
shader_defs.push("VERTEX_UVS".into());
|
||||||
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
|
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_functions as mesh_functions
|
||||||
#import bevy_pbr::mesh_bindings
|
#import bevy_pbr::skinning
|
||||||
|
#import bevy_pbr::morph
|
||||||
// NOTE: Bindings must come before functions that use them!
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
#import bevy_pbr::mesh_functions
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
#ifdef VERTEX_POSITIONS
|
#ifdef VERTEX_POSITIONS
|
||||||
@ -29,26 +29,21 @@ struct Vertex {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
#import bevy_pbr::mesh_vertex_output
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef MORPH_TARGETS
|
#ifdef MORPH_TARGETS
|
||||||
fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
||||||
var vertex = vertex_in;
|
var vertex = vertex_in;
|
||||||
let weight_count = layer_count();
|
let weight_count = bevy_pbr::morph::layer_count();
|
||||||
for (var i: u32 = 0u; i < weight_count; i ++) {
|
for (var i: u32 = 0u; i < weight_count; i ++) {
|
||||||
let weight = weight_at(i);
|
let weight = bevy_pbr::morph::weight_at(i);
|
||||||
if weight == 0.0 {
|
if weight == 0.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
vertex.position += weight * morph(vertex.index, position_offset, i);
|
vertex.position += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::position_offset, i);
|
||||||
#ifdef VERTEX_NORMALS
|
#ifdef VERTEX_NORMALS
|
||||||
vertex.normal += weight * morph(vertex.index, normal_offset, i);
|
vertex.normal += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::normal_offset, i);
|
||||||
#endif
|
#endif
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
vertex.tangent += vec4(weight * morph(vertex.index, tangent_offset, i), 0.0);
|
vertex.tangent += vec4(weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::tangent_offset, i), 0.0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return vertex;
|
return vertex;
|
||||||
@ -56,8 +51,8 @@ fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput {
|
||||||
var out: VertexOutput;
|
var out: MeshVertexOutput;
|
||||||
|
|
||||||
#ifdef MORPH_TARGETS
|
#ifdef MORPH_TARGETS
|
||||||
var vertex = morph_vertex(vertex_no_morph);
|
var vertex = morph_vertex(vertex_no_morph);
|
||||||
@ -66,22 +61,22 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
var model = skin_model(vertex.joint_indices, vertex.joint_weights);
|
var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
|
||||||
#else
|
#else
|
||||||
var model = mesh.model;
|
var model = mesh.model;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_NORMALS
|
#ifdef VERTEX_NORMALS
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
out.world_normal = skin_normals(model, vertex.normal);
|
out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
|
||||||
#else
|
#else
|
||||||
out.world_normal = mesh_normal_local_to_world(vertex.normal);
|
out.world_normal = mesh_functions::mesh_normal_local_to_world(vertex.normal);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_POSITIONS
|
#ifdef VERTEX_POSITIONS
|
||||||
out.world_position = mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
|
out.world_position = mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
|
||||||
out.clip_position = mesh_position_world_to_clip(out.world_position);
|
out.position = mesh_functions::mesh_position_world_to_clip(out.world_position);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
@ -89,7 +84,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent);
|
out.world_tangent = mesh_functions::mesh_tangent_local_to_world(model, vertex.tangent);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
@ -99,14 +94,12 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FragmentInput {
|
|
||||||
#import bevy_pbr::mesh_vertex_output
|
|
||||||
};
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
fn fragment(
|
||||||
|
mesh: MeshVertexOutput,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
return in.color;
|
return mesh.color;
|
||||||
#else
|
#else
|
||||||
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
|
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
#define_import_path bevy_pbr::mesh_bindings
|
#define_import_path bevy_pbr::mesh_bindings
|
||||||
|
|
||||||
#import bevy_pbr::mesh_types
|
#import bevy_pbr::mesh_types Mesh
|
||||||
|
|
||||||
|
#ifdef MESH_BINDGROUP_1
|
||||||
|
|
||||||
|
@group(1) @binding(0)
|
||||||
|
var<uniform> mesh: Mesh;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var<uniform> mesh: Mesh;
|
var<uniform> mesh: Mesh;
|
||||||
|
|
||||||
#ifdef SKINNED
|
|
||||||
@group(2) @binding(1)
|
|
||||||
var<uniform> joint_matrices: SkinnedMesh;
|
|
||||||
#import bevy_pbr::skinning
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef MORPH_TARGETS
|
|
||||||
@group(2) @binding(2)
|
|
||||||
var<uniform> morph_weights: MorphWeights;
|
|
||||||
@group(2) @binding(3)
|
|
||||||
var morph_targets: texture_3d<f32>;
|
|
||||||
#import bevy_pbr::morph
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#define_import_path bevy_pbr::mesh_functions
|
#define_import_path bevy_pbr::mesh_functions
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_view_bindings view
|
||||||
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
|
#import bevy_pbr::mesh_types MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT
|
||||||
|
|
||||||
fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
|
fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
|
||||||
return model * vertex_position;
|
return model * vertex_position;
|
||||||
}
|
}
|
||||||
@ -34,7 +38,7 @@ fn mesh_normal_local_to_world(vertex_normal: vec3<f32>) -> vec3<f32> {
|
|||||||
|
|
||||||
// Calculates the sign of the determinant of the 3x3 model matrix based on a
|
// Calculates the sign of the determinant of the 3x3 model matrix based on a
|
||||||
// mesh flag
|
// mesh flag
|
||||||
fn sign_determinant_model_3x3() -> f32 {
|
fn sign_determinant_model_3x3m() -> f32 {
|
||||||
// bool(u32) is false if 0u else true
|
// bool(u32) is false if 0u else true
|
||||||
// f32(bool) is 1.0 if true else 0.0
|
// f32(bool) is 1.0 if true else 0.0
|
||||||
// * 2.0 - 1.0 remaps 0.0 or 1.0 to -1.0 or 1.0 respectively
|
// * 2.0 - 1.0 remaps 0.0 or 1.0 to -1.0 or 1.0 respectively
|
||||||
@ -58,6 +62,6 @@ fn mesh_tangent_local_to_world(model: mat4x4<f32>, vertex_tangent: vec4<f32>) ->
|
|||||||
),
|
),
|
||||||
// NOTE: Multiplying by the sign of the determinant of the 3x3 model matrix accounts for
|
// NOTE: Multiplying by the sign of the determinant of the 3x3 model matrix accounts for
|
||||||
// situations such as negative scaling.
|
// situations such as negative scaling.
|
||||||
vertex_tangent.w * sign_determinant_model_3x3()
|
vertex_tangent.w * sign_determinant_model_3x3m()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#define_import_path bevy_pbr::mesh_vertex_output
|
#define_import_path bevy_pbr::mesh_vertex_output
|
||||||
|
|
||||||
|
struct MeshVertexOutput {
|
||||||
|
// this is `clip position` when the struct is used as a vertex stage output
|
||||||
|
// and `frag coord` when used as a fragment stage input
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) world_position: vec4<f32>,
|
@location(0) world_position: vec4<f32>,
|
||||||
@location(1) world_normal: vec3<f32>,
|
@location(1) world_normal: vec3<f32>,
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
@ -11,3 +15,4 @@
|
|||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
@location(4) color: vec4<f32>,
|
@location(4) color: vec4<f32>,
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#define_import_path bevy_pbr::mesh_view_bindings
|
#define_import_path bevy_pbr::mesh_view_bindings
|
||||||
|
|
||||||
#import bevy_pbr::mesh_view_types
|
#import bevy_pbr::mesh_view_types as types
|
||||||
|
#import bevy_render::view View
|
||||||
|
#import bevy_render::globals Globals
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var<uniform> view: View;
|
var<uniform> view: View;
|
||||||
@group(0) @binding(1)
|
@group(0) @binding(1)
|
||||||
var<uniform> lights: Lights;
|
var<uniform> lights: types::Lights;
|
||||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||||
@group(0) @binding(2)
|
@group(0) @binding(2)
|
||||||
var point_shadow_textures: texture_depth_cube;
|
var point_shadow_textures: texture_depth_cube;
|
||||||
@ -27,24 +29,24 @@ var directional_shadow_textures_sampler: sampler_comparison;
|
|||||||
|
|
||||||
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
|
||||||
@group(0) @binding(6)
|
@group(0) @binding(6)
|
||||||
var<storage> point_lights: PointLights;
|
var<storage> point_lights: types::PointLights;
|
||||||
@group(0) @binding(7)
|
@group(0) @binding(7)
|
||||||
var<storage> cluster_light_index_lists: ClusterLightIndexLists;
|
var<storage> cluster_light_index_lists: types::ClusterLightIndexLists;
|
||||||
@group(0) @binding(8)
|
@group(0) @binding(8)
|
||||||
var<storage> cluster_offsets_and_counts: ClusterOffsetsAndCounts;
|
var<storage> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts;
|
||||||
#else
|
#else
|
||||||
@group(0) @binding(6)
|
@group(0) @binding(6)
|
||||||
var<uniform> point_lights: PointLights;
|
var<uniform> point_lights: types::PointLights;
|
||||||
@group(0) @binding(7)
|
@group(0) @binding(7)
|
||||||
var<uniform> cluster_light_index_lists: ClusterLightIndexLists;
|
var<uniform> cluster_light_index_lists: types::ClusterLightIndexLists;
|
||||||
@group(0) @binding(8)
|
@group(0) @binding(8)
|
||||||
var<uniform> cluster_offsets_and_counts: ClusterOffsetsAndCounts;
|
var<uniform> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@group(0) @binding(9)
|
@group(0) @binding(9)
|
||||||
var<uniform> globals: Globals;
|
var<uniform> globals: Globals;
|
||||||
@group(0) @binding(10)
|
@group(0) @binding(10)
|
||||||
var<uniform> fog: Fog;
|
var<uniform> fog: types::Fog;
|
||||||
|
|
||||||
@group(0) @binding(11)
|
@group(0) @binding(11)
|
||||||
var screen_space_ambient_occlusion_texture: texture_2d<f32>;
|
var screen_space_ambient_occlusion_texture: texture_2d<f32>;
|
||||||
|
@ -7,6 +7,27 @@
|
|||||||
|
|
||||||
#define_import_path bevy_pbr::morph
|
#define_import_path bevy_pbr::morph
|
||||||
|
|
||||||
|
#ifdef MORPH_TARGETS
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_types MorphWeights
|
||||||
|
|
||||||
|
#ifdef MESH_BINDGROUP_1
|
||||||
|
|
||||||
|
@group(1) @binding(2)
|
||||||
|
var<uniform> morph_weights: MorphWeights;
|
||||||
|
@group(1) @binding(3)
|
||||||
|
var morph_targets: texture_3d<f32>;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
@group(2) @binding(2)
|
||||||
|
var<uniform> morph_weights: MorphWeights;
|
||||||
|
@group(2) @binding(3)
|
||||||
|
var morph_targets: texture_3d<f32>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct
|
// NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct
|
||||||
// in crates/bevy_render/src/mesh/morph/visitors.rs
|
// in crates/bevy_render/src/mesh/morph/visitors.rs
|
||||||
// In an ideal world, the offsets are established dynamically and passed as #defines
|
// In an ideal world, the offsets are established dynamically and passed as #defines
|
||||||
@ -43,3 +64,5 @@ fn morph(vertex_index: u32, component_offset: u32, weight_index: u32) -> vec3<f3
|
|||||||
morph_pixel(vertex_index, component_offset + 2u, weight_index),
|
morph_pixel(vertex_index, component_offset + 2u, weight_index),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // MORPH_TARGETS
|
@ -1,5 +1,7 @@
|
|||||||
#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
|
||||||
|
|
||||||
fn sample_depth_map(uv: vec2<f32>) -> f32 {
|
fn sample_depth_map(uv: vec2<f32>) -> f32 {
|
||||||
// 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.
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
#import bevy_pbr::mesh_view_bindings
|
#define_import_path bevy_pbr::fragment
|
||||||
#import bevy_pbr::pbr_bindings
|
|
||||||
#import bevy_pbr::mesh_bindings
|
|
||||||
|
|
||||||
#import bevy_pbr::utils
|
#import bevy_pbr::pbr_functions as pbr_functions
|
||||||
#import bevy_pbr::clustered_forward
|
#import bevy_pbr::pbr_bindings as pbr_bindings
|
||||||
#import bevy_pbr::lighting
|
#import bevy_pbr::pbr_types as pbr_types
|
||||||
#import bevy_pbr::pbr_ambient
|
#import bevy_pbr::prepass_utils
|
||||||
#import bevy_pbr::shadows
|
|
||||||
#import bevy_pbr::fog
|
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||||
#import bevy_pbr::pbr_functions
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
#import bevy_pbr::parallax_mapping
|
#import bevy_pbr::mesh_view_bindings view, fog, screen_space_ambient_occlusion_texture
|
||||||
|
#import bevy_pbr::mesh_view_types FOG_MODE_OFF
|
||||||
|
#import bevy_core_pipeline::tonemapping screen_space_dither, powsafe, tone_mapping
|
||||||
|
#import bevy_pbr::parallax_mapping parallaxed_uv
|
||||||
|
|
||||||
#import bevy_pbr::prepass_utils
|
#import bevy_pbr::prepass_utils
|
||||||
|
|
||||||
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
|
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
|
||||||
#import bevy_pbr::gtao_utils
|
#import bevy_pbr::gtao_utils gtao_multibounce
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct FragmentInput {
|
|
||||||
@builtin(front_facing) is_front: bool,
|
|
||||||
@builtin(position) frag_coord: vec4<f32>,
|
|
||||||
#import bevy_pbr::mesh_vertex_output
|
|
||||||
};
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
fn fragment(
|
||||||
|
in: MeshVertexOutput,
|
||||||
|
@builtin(front_facing) is_front: bool,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
|
var output_color: vec4<f32> = pbr_bindings::material.base_color;
|
||||||
|
|
||||||
let is_orthographic = view.projection[3].w == 1.0;
|
let is_orthographic = view.projection[3].w == 1.0;
|
||||||
let V = calculate_view(in.world_position, is_orthographic);
|
let V = pbr_functions::calculate_view(in.world_position, is_orthographic);
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
var uv = in.uv;
|
var uv = in.uv;
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) {
|
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) {
|
||||||
let N = in.world_normal;
|
let N = in.world_normal;
|
||||||
let T = in.world_tangent.xyz;
|
let T = in.world_tangent.xyz;
|
||||||
let B = in.world_tangent.w * cross(N, T);
|
let B = in.world_tangent.w * cross(N, T);
|
||||||
// Transform V from fragment to camera in world space to tangent space.
|
// Transform V from fragment to camera in world space to tangent space.
|
||||||
let Vt = vec3(dot(V, T), dot(V, B), dot(V, N));
|
let Vt = vec3(dot(V, T), dot(V, B), dot(V, N));
|
||||||
uv = parallaxed_uv(
|
uv = parallaxed_uv(
|
||||||
material.parallax_depth_scale,
|
pbr_bindings::material.parallax_depth_scale,
|
||||||
material.max_parallax_layer_count,
|
pbr_bindings::material.max_parallax_layer_count,
|
||||||
material.max_relief_mapping_search_steps,
|
pbr_bindings::material.max_relief_mapping_search_steps,
|
||||||
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
|
||||||
@ -49,41 +49,41 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
var output_color: vec4<f32> = material.base_color;
|
|
||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
output_color = output_color * in.color;
|
output_color = output_color * in.color;
|
||||||
#endif
|
#endif
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
||||||
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, uv, view.mip_bias);
|
output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, in.uv, view.mip_bias);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 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 ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {
|
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {
|
||||||
// Prepare a 'processed' StandardMaterial by sampling all textures to resolve
|
// Prepare a 'processed' StandardMaterial by sampling all textures to resolve
|
||||||
// the material members
|
// the material members
|
||||||
var pbr_input: PbrInput;
|
var pbr_input: pbr_functions::PbrInput;
|
||||||
|
|
||||||
pbr_input.material.base_color = output_color;
|
pbr_input.material.base_color = output_color;
|
||||||
pbr_input.material.reflectance = material.reflectance;
|
pbr_input.material.reflectance = pbr_bindings::material.reflectance;
|
||||||
pbr_input.material.flags = material.flags;
|
pbr_input.material.flags = pbr_bindings::material.flags;
|
||||||
pbr_input.material.alpha_cutoff = material.alpha_cutoff;
|
pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff;
|
||||||
|
|
||||||
// TODO use .a for exposure compensation in HDR
|
// TODO use .a for exposure compensation in HDR
|
||||||
var emissive: vec4<f32> = material.emissive;
|
var emissive: vec4<f32> = pbr_bindings::material.emissive;
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
||||||
emissive = vec4<f32>(emissive.rgb * textureSampleBias(emissive_texture, emissive_sampler, uv, view.mip_bias).rgb, 1.0);
|
emissive = vec4<f32>(emissive.rgb * textureSampleBias(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, in.uv, view.mip_bias).rgb, 1.0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
pbr_input.material.emissive = emissive;
|
pbr_input.material.emissive = emissive;
|
||||||
|
|
||||||
var metallic: f32 = material.metallic;
|
var metallic: f32 = pbr_bindings::material.metallic;
|
||||||
var perceptual_roughness: f32 = material.perceptual_roughness;
|
var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness;
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
||||||
let metallic_roughness = textureSampleBias(metallic_roughness_texture, metallic_roughness_sampler, uv, view.mip_bias);
|
let metallic_roughness = textureSampleBias(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, in.uv, view.mip_bias);
|
||||||
// Sampling from GLTF standard channels for now
|
// Sampling from GLTF standard channels for now
|
||||||
metallic = metallic * metallic_roughness.b;
|
metallic = metallic * metallic_roughness.b;
|
||||||
perceptual_roughness = perceptual_roughness * metallic_roughness.g;
|
perceptual_roughness = perceptual_roughness * metallic_roughness.g;
|
||||||
@ -95,33 +95,33 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
// TODO: Split into diffuse/specular occlusion?
|
// TODO: Split into diffuse/specular occlusion?
|
||||||
var occlusion: vec3<f32> = vec3(1.0);
|
var occlusion: vec3<f32> = vec3(1.0);
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
||||||
occlusion = vec3(textureSampleBias(occlusion_texture, occlusion_sampler, in.uv, view.mip_bias).r);
|
occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, in.uv, view.mip_bias).r);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
|
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
|
||||||
let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.frag_coord.xy), 0i).r;
|
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);
|
let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb);
|
||||||
occlusion = min(occlusion, ssao_multibounce);
|
occlusion = min(occlusion, ssao_multibounce);
|
||||||
#endif
|
#endif
|
||||||
pbr_input.occlusion = occlusion;
|
pbr_input.occlusion = occlusion;
|
||||||
|
|
||||||
pbr_input.frag_coord = in.frag_coord;
|
pbr_input.frag_coord = in.position;
|
||||||
pbr_input.world_position = in.world_position;
|
pbr_input.world_position = in.world_position;
|
||||||
|
|
||||||
pbr_input.world_normal = prepare_world_normal(
|
pbr_input.world_normal = pbr_functions::prepare_world_normal(
|
||||||
in.world_normal,
|
in.world_normal,
|
||||||
(material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
(pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
||||||
in.is_front,
|
is_front,
|
||||||
);
|
);
|
||||||
|
|
||||||
pbr_input.is_orthographic = is_orthographic;
|
pbr_input.is_orthographic = is_orthographic;
|
||||||
|
|
||||||
#ifdef LOAD_PREPASS_NORMALS
|
#ifdef LOAD_PREPASS_NORMALS
|
||||||
pbr_input.N = prepass_normal(in.frag_coord, 0u);
|
pbr_input.N = bevy_pbr::prepass_utils::prepass_normal(in.position, 0u);
|
||||||
#else
|
#else
|
||||||
pbr_input.N = apply_normal_mapping(
|
pbr_input.N = pbr_functions::apply_normal_mapping(
|
||||||
material.flags,
|
pbr_bindings::material.flags,
|
||||||
pbr_input.world_normal,
|
pbr_input.world_normal,
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
@ -139,22 +139,22 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
|
|
||||||
pbr_input.flags = mesh.flags;
|
pbr_input.flags = mesh.flags;
|
||||||
|
|
||||||
output_color = pbr(pbr_input);
|
output_color = pbr_functions::pbr(pbr_input);
|
||||||
} else {
|
} else {
|
||||||
output_color = alpha_discard(material, output_color);
|
output_color = pbr_functions::alpha_discard(pbr_bindings::material, output_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fog
|
// fog
|
||||||
if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
|
if (fog.mode != FOG_MODE_OFF && (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
|
||||||
output_color = apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz);
|
output_color = pbr_functions::apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
output_color = tone_mapping(output_color);
|
output_color = tone_mapping(output_color, view.color_grading);
|
||||||
#ifdef DEBAND_DITHER
|
#ifdef DEBAND_DITHER
|
||||||
var output_rgb = output_color.rgb;
|
var output_rgb = output_color.rgb;
|
||||||
output_rgb = powsafe(output_rgb, 1.0 / 2.2);
|
output_rgb = powsafe(output_rgb, 1.0 / 2.2);
|
||||||
output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy);
|
output_rgb = output_rgb + screen_space_dither(in.position.xy);
|
||||||
// This conversion back to linear space is required because our output texture format is
|
// This conversion back to linear space is required because our output texture format is
|
||||||
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
|
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
|
||||||
output_rgb = powsafe(output_rgb, 2.2);
|
output_rgb = powsafe(output_rgb, 2.2);
|
||||||
@ -162,7 +162,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef PREMULTIPLY_ALPHA
|
#ifdef PREMULTIPLY_ALPHA
|
||||||
output_color = premultiply_alpha(material.flags, output_color);
|
output_color = pbr_functions::premultiply_alpha(pbr_bindings::material.flags, output_color);
|
||||||
#endif
|
#endif
|
||||||
return output_color;
|
return output_color;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#define_import_path bevy_pbr::pbr_ambient
|
#define_import_path bevy_pbr::ambient
|
||||||
|
|
||||||
|
#import bevy_pbr::lighting EnvBRDFApprox, F_AB
|
||||||
|
#import bevy_pbr::mesh_view_bindings lights
|
||||||
|
|
||||||
// A precomputed `NdotV` is provided because it is computed regardless,
|
// A precomputed `NdotV` is provided because it is computed regardless,
|
||||||
// but `world_normal` and the view vector `V` are provided separately for more advanced uses.
|
// but `world_normal` and the view vector `V` are provided separately for more advanced uses.
|
||||||
@ -12,8 +15,8 @@ fn ambient_light(
|
|||||||
perceptual_roughness: f32,
|
perceptual_roughness: f32,
|
||||||
occlusion: vec3<f32>,
|
occlusion: vec3<f32>,
|
||||||
) -> vec3<f32> {
|
) -> vec3<f32> {
|
||||||
let diffuse_ambient = EnvBRDFApprox(diffuse_color, F_AB(1.0, NdotV)) * occlusion;
|
let diffuse_ambient = EnvBRDFApprox(diffuse_color, F_AB(1.0, NdotV));
|
||||||
let specular_ambient = EnvBRDFApprox(specular_color, F_AB(perceptual_roughness, NdotV));
|
let specular_ambient = EnvBRDFApprox(specular_color, F_AB(perceptual_roughness, NdotV));
|
||||||
|
|
||||||
return (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb;
|
return (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb * occlusion;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#define_import_path bevy_pbr::pbr_bindings
|
#define_import_path bevy_pbr::pbr_bindings
|
||||||
|
|
||||||
#import bevy_pbr::pbr_types
|
#import bevy_pbr::pbr_types StandardMaterial
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> material: StandardMaterial;
|
var<uniform> material: StandardMaterial;
|
||||||
|
@ -4,20 +4,32 @@
|
|||||||
#import bevy_core_pipeline::tonemapping
|
#import bevy_core_pipeline::tonemapping
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#import bevy_pbr::pbr_types as pbr_types
|
||||||
|
#import bevy_pbr::pbr_bindings as pbr_bindings
|
||||||
|
#import bevy_pbr::mesh_view_bindings as view_bindings
|
||||||
|
#import bevy_pbr::mesh_view_types as mesh_view_types
|
||||||
|
#import bevy_pbr::lighting as lighting
|
||||||
|
#import bevy_pbr::clustered_forward as clustering
|
||||||
|
#import bevy_pbr::shadows as shadows
|
||||||
|
#import bevy_pbr::fog as fog
|
||||||
|
#import bevy_pbr::ambient as ambient
|
||||||
#ifdef ENVIRONMENT_MAP
|
#ifdef ENVIRONMENT_MAP
|
||||||
#import bevy_pbr::environment_map
|
#import bevy_pbr::environment_map
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fn alpha_discard(material: StandardMaterial, output_color: vec4<f32>) -> vec4<f32> {
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
|
#import bevy_pbr::mesh_types MESH_FLAGS_SHADOW_RECEIVER_BIT
|
||||||
|
|
||||||
|
fn alpha_discard(material: pbr_types::StandardMaterial, output_color: vec4<f32>) -> vec4<f32> {
|
||||||
var color = output_color;
|
var color = output_color;
|
||||||
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
let alpha_mode = material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
|
if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
|
||||||
// NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
|
// NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
|
||||||
color.a = 1.0;
|
color.a = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MAY_DISCARD
|
#ifdef MAY_DISCARD
|
||||||
else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
else if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
||||||
if color.a >= material.alpha_cutoff {
|
if color.a >= material.alpha_cutoff {
|
||||||
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
|
// NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
|
||||||
color.a = 1.0;
|
color.a = 1.0;
|
||||||
@ -82,8 +94,8 @@ fn apply_normal_mapping(
|
|||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
// Nt is the tangent-space normal.
|
// Nt is the tangent-space normal.
|
||||||
var Nt = textureSampleBias(normal_map_texture, normal_map_sampler, uv, view.mip_bias).rgb;
|
var Nt = textureSampleBias(pbr_bindings::normal_map_texture, pbr_bindings::normal_map_sampler, uv, view_bindings::view.mip_bias).rgb;
|
||||||
if (standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
|
if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
|
||||||
// Only use the xy components and derive z for 2-component normal maps.
|
// Only use the xy components and derive z for 2-component normal maps.
|
||||||
Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
|
Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
|
||||||
Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y);
|
Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y);
|
||||||
@ -91,7 +103,7 @@ fn apply_normal_mapping(
|
|||||||
Nt = Nt * 2.0 - 1.0;
|
Nt = Nt * 2.0 - 1.0;
|
||||||
}
|
}
|
||||||
// Normal maps authored for DirectX require flipping the y component
|
// Normal maps authored for DirectX require flipping the y component
|
||||||
if (standard_material_flags & STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u {
|
if (standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u {
|
||||||
Nt.y = -Nt.y;
|
Nt.y = -Nt.y;
|
||||||
}
|
}
|
||||||
// NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from
|
// NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from
|
||||||
@ -116,16 +128,16 @@ fn calculate_view(
|
|||||||
var V: vec3<f32>;
|
var V: vec3<f32>;
|
||||||
if is_orthographic {
|
if is_orthographic {
|
||||||
// Orthographic view vector
|
// Orthographic view vector
|
||||||
V = normalize(vec3<f32>(view.view_proj[0].z, view.view_proj[1].z, view.view_proj[2].z));
|
V = normalize(vec3<f32>(view_bindings::view.view_proj[0].z, view_bindings::view.view_proj[1].z, view_bindings::view.view_proj[2].z));
|
||||||
} else {
|
} else {
|
||||||
// Only valid for a perpective projection
|
// Only valid for a perpective projection
|
||||||
V = normalize(view.world_position.xyz - world_position.xyz);
|
V = normalize(view_bindings::view.world_position.xyz - world_position.xyz);
|
||||||
}
|
}
|
||||||
return V;
|
return V;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PbrInput {
|
struct PbrInput {
|
||||||
material: StandardMaterial,
|
material: pbr_types::StandardMaterial,
|
||||||
occlusion: vec3<f32>,
|
occlusion: vec3<f32>,
|
||||||
frag_coord: vec4<f32>,
|
frag_coord: vec4<f32>,
|
||||||
world_position: vec4<f32>,
|
world_position: vec4<f32>,
|
||||||
@ -145,7 +157,7 @@ struct PbrInput {
|
|||||||
fn pbr_input_new() -> PbrInput {
|
fn pbr_input_new() -> PbrInput {
|
||||||
var pbr_input: PbrInput;
|
var pbr_input: PbrInput;
|
||||||
|
|
||||||
pbr_input.material = standard_material_new();
|
pbr_input.material = pbr_types::standard_material_new();
|
||||||
pbr_input.occlusion = vec3<f32>(1.0);
|
pbr_input.occlusion = vec3<f32>(1.0);
|
||||||
|
|
||||||
pbr_input.frag_coord = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
pbr_input.frag_coord = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
||||||
@ -174,7 +186,7 @@ fn pbr(
|
|||||||
// calculate non-linear roughness from linear perceptualRoughness
|
// calculate non-linear roughness from linear perceptualRoughness
|
||||||
let metallic = in.material.metallic;
|
let metallic = in.material.metallic;
|
||||||
let perceptual_roughness = in.material.perceptual_roughness;
|
let perceptual_roughness = in.material.perceptual_roughness;
|
||||||
let roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness);
|
||||||
|
|
||||||
let occlusion = in.occlusion;
|
let occlusion = in.occlusion;
|
||||||
|
|
||||||
@ -193,64 +205,65 @@ fn pbr(
|
|||||||
|
|
||||||
let R = reflect(-in.V, in.N);
|
let R = reflect(-in.V, in.N);
|
||||||
|
|
||||||
let f_ab = F_AB(perceptual_roughness, NdotV);
|
let f_ab = lighting::F_AB(perceptual_roughness, NdotV);
|
||||||
|
|
||||||
var direct_light: vec3<f32> = vec3<f32>(0.0);
|
var direct_light: vec3<f32> = vec3<f32>(0.0);
|
||||||
|
|
||||||
let view_z = dot(vec4<f32>(
|
let view_z = dot(vec4<f32>(
|
||||||
view.inverse_view[0].z,
|
view_bindings::view.inverse_view[0].z,
|
||||||
view.inverse_view[1].z,
|
view_bindings::view.inverse_view[1].z,
|
||||||
view.inverse_view[2].z,
|
view_bindings::view.inverse_view[2].z,
|
||||||
view.inverse_view[3].z
|
view_bindings::view.inverse_view[3].z
|
||||||
), in.world_position);
|
), in.world_position);
|
||||||
let cluster_index = fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic);
|
let cluster_index = clustering::fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic);
|
||||||
let offset_and_counts = unpack_offset_and_counts(cluster_index);
|
let offset_and_counts = clustering::unpack_offset_and_counts(cluster_index);
|
||||||
|
|
||||||
// Point lights (direct)
|
// Point lights (direct)
|
||||||
for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) {
|
for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) {
|
||||||
let light_id = get_light_id(i);
|
let light_id = clustering::get_light_id(i);
|
||||||
var shadow: f32 = 1.0;
|
var shadow: f32 = 1.0;
|
||||||
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||||
&& (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
&& (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||||
shadow = fetch_point_shadow(light_id, in.world_position, in.world_normal);
|
shadow = shadows::fetch_point_shadow(light_id, in.world_position, in.world_normal);
|
||||||
}
|
}
|
||||||
let light_contrib = point_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
|
let light_contrib = lighting::point_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
|
||||||
direct_light += light_contrib * shadow;
|
direct_light += light_contrib * shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spot lights (direct)
|
// Spot lights (direct)
|
||||||
for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) {
|
for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) {
|
||||||
let light_id = get_light_id(i);
|
let light_id = clustering::get_light_id(i);
|
||||||
|
|
||||||
var shadow: f32 = 1.0;
|
var shadow: f32 = 1.0;
|
||||||
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||||
&& (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
&& (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||||
shadow = fetch_spot_shadow(light_id, in.world_position, in.world_normal);
|
shadow = shadows::fetch_spot_shadow(light_id, in.world_position, in.world_normal);
|
||||||
}
|
}
|
||||||
let light_contrib = spot_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
|
let light_contrib = lighting::spot_light(in.world_position.xyz, light_id, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
|
||||||
direct_light += light_contrib * shadow;
|
direct_light += light_contrib * shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directional lights (direct)
|
// directional lights (direct)
|
||||||
let n_directional_lights = lights.n_directional_lights;
|
let n_directional_lights = view_bindings::lights.n_directional_lights;
|
||||||
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
|
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
|
||||||
var shadow: f32 = 1.0;
|
var shadow: f32 = 1.0;
|
||||||
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
|
||||||
&& (lights.directional_lights[i].flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
&& (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
|
||||||
shadow = fetch_directional_shadow(i, in.world_position, in.world_normal, view_z);
|
shadow = shadows::fetch_directional_shadow(i, in.world_position, in.world_normal, view_z);
|
||||||
}
|
}
|
||||||
var light_contrib = directional_light(i, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
|
var light_contrib = lighting::directional_light(i, roughness, NdotV, in.N, in.V, R, F0, f_ab, diffuse_color);
|
||||||
#ifdef DIRECTIONAL_LIGHT_SHADOW_MAP_DEBUG_CASCADES
|
#ifdef DIRECTIONAL_LIGHT_SHADOW_MAP_DEBUG_CASCADES
|
||||||
light_contrib = cascade_debug_visualization(light_contrib, i, view_z);
|
light_contrib = shadows::cascade_debug_visualization(light_contrib, i, view_z);
|
||||||
#endif
|
#endif
|
||||||
direct_light += light_contrib * shadow;
|
direct_light += light_contrib * shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ambient light (indirect)
|
// Ambient light (indirect)
|
||||||
var indirect_light = ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, occlusion);
|
var indirect_light = ambient::ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, occlusion);
|
||||||
|
|
||||||
// Environment map light (indirect)
|
// Environment map light (indirect)
|
||||||
#ifdef ENVIRONMENT_MAP
|
#ifdef ENVIRONMENT_MAP
|
||||||
let environment_light = environment_map_light(perceptual_roughness, roughness, diffuse_color, NdotV, f_ab, in.N, R, F0);
|
let environment_light = bevy_pbr::environment_map::environment_map_light(perceptual_roughness, roughness, diffuse_color, NdotV, f_ab, in.N, R, F0);
|
||||||
indirect_light += (environment_light.diffuse * occlusion) + environment_light.specular;
|
indirect_light += (environment_light.diffuse * occlusion) + environment_light.specular;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -262,7 +275,7 @@ fn pbr(
|
|||||||
output_color.a
|
output_color.a
|
||||||
);
|
);
|
||||||
|
|
||||||
output_color = cluster_debug_visualization(
|
output_color = clustering::cluster_debug_visualization(
|
||||||
output_color,
|
output_color,
|
||||||
view_z,
|
view_z,
|
||||||
in.is_orthographic,
|
in.is_orthographic,
|
||||||
@ -275,7 +288,7 @@ fn pbr(
|
|||||||
#endif // PREPASS_FRAGMENT
|
#endif // PREPASS_FRAGMENT
|
||||||
|
|
||||||
#ifndef PREPASS_FRAGMENT
|
#ifndef PREPASS_FRAGMENT
|
||||||
fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
|
fn apply_fog(fog_params: mesh_view_types::Fog, input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
|
||||||
let view_to_world = fragment_world_position.xyz - view_world_position.xyz;
|
let view_to_world = fragment_world_position.xyz - view_world_position.xyz;
|
||||||
|
|
||||||
// `length()` is used here instead of just `view_to_world.z` since that produces more
|
// `length()` is used here instead of just `view_to_world.z` since that produces more
|
||||||
@ -287,9 +300,9 @@ fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: v
|
|||||||
var scattering = vec3<f32>(0.0);
|
var scattering = vec3<f32>(0.0);
|
||||||
if fog_params.directional_light_color.a > 0.0 {
|
if fog_params.directional_light_color.a > 0.0 {
|
||||||
let view_to_world_normalized = view_to_world / distance;
|
let view_to_world_normalized = view_to_world / distance;
|
||||||
let n_directional_lights = lights.n_directional_lights;
|
let n_directional_lights = view_bindings::lights.n_directional_lights;
|
||||||
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
|
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
|
||||||
let light = lights.directional_lights[i];
|
let light = view_bindings::lights.directional_lights[i];
|
||||||
scattering += pow(
|
scattering += pow(
|
||||||
max(
|
max(
|
||||||
dot(view_to_world_normalized, light.direction_to_light),
|
dot(view_to_world_normalized, light.direction_to_light),
|
||||||
@ -300,14 +313,14 @@ fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: v
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fog_params.mode == FOG_MODE_LINEAR {
|
if fog_params.mode == mesh_view_types::FOG_MODE_LINEAR {
|
||||||
return linear_fog(fog_params, input_color, distance, scattering);
|
return fog::linear_fog(fog_params, input_color, distance, scattering);
|
||||||
} else if fog_params.mode == FOG_MODE_EXPONENTIAL {
|
} else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL {
|
||||||
return exponential_fog(fog_params, input_color, distance, scattering);
|
return fog::exponential_fog(fog_params, input_color, distance, scattering);
|
||||||
} else if fog_params.mode == FOG_MODE_EXPONENTIAL_SQUARED {
|
} else if fog_params.mode == mesh_view_types::FOG_MODE_EXPONENTIAL_SQUARED {
|
||||||
return exponential_squared_fog(fog_params, input_color, distance, scattering);
|
return fog::exponential_squared_fog(fog_params, input_color, distance, scattering);
|
||||||
} else if fog_params.mode == FOG_MODE_ATMOSPHERIC {
|
} else if fog_params.mode == mesh_view_types::FOG_MODE_ATMOSPHERIC {
|
||||||
return atmospheric_fog(fog_params, input_color, distance, scattering);
|
return fog::atmospheric_fog(fog_params, input_color, distance, scattering);
|
||||||
} else {
|
} else {
|
||||||
return input_color;
|
return input_color;
|
||||||
}
|
}
|
||||||
@ -324,8 +337,8 @@ fn premultiply_alpha(standard_material_flags: u32, color: vec4<f32>) -> vec4<f32
|
|||||||
// For `BlendState::PREMULTIPLIED_ALPHA_BLENDING` the blend function is:
|
// For `BlendState::PREMULTIPLIED_ALPHA_BLENDING` the blend function is:
|
||||||
//
|
//
|
||||||
// result = 1 * src_color + (1 - src_alpha) * dst_color
|
// result = 1 * src_color + (1 - src_alpha) * dst_color
|
||||||
let alpha_mode = standard_material_flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
let alpha_mode = standard_material_flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD {
|
if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD {
|
||||||
// Here, we premultiply `src_color` by `src_alpha`, and replace `src_alpha` with 0.0:
|
// Here, we premultiply `src_color` by `src_alpha`, and replace `src_alpha` with 0.0:
|
||||||
//
|
//
|
||||||
// src_color *= src_alpha
|
// src_color *= src_alpha
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#define_import_path bevy_pbr::lighting
|
#define_import_path bevy_pbr::lighting
|
||||||
|
|
||||||
|
#import bevy_pbr::utils PI
|
||||||
|
#import bevy_pbr::mesh_view_types as view_types
|
||||||
|
#import bevy_pbr::mesh_view_bindings as view_bindings
|
||||||
|
|
||||||
// From the Filament design doc
|
// From the Filament design doc
|
||||||
// https://google.github.io/filament/Filament.html#table_symbols
|
// https://google.github.io/filament/Filament.html#table_symbols
|
||||||
// Symbol Definition
|
// Symbol Definition
|
||||||
@ -180,7 +184,7 @@ fn point_light(
|
|||||||
f_ab: vec2<f32>,
|
f_ab: vec2<f32>,
|
||||||
diffuseColor: vec3<f32>
|
diffuseColor: vec3<f32>
|
||||||
) -> vec3<f32> {
|
) -> vec3<f32> {
|
||||||
let light = &point_lights.data[light_id];
|
let light = &view_bindings::point_lights.data[light_id];
|
||||||
let light_to_frag = (*light).position_radius.xyz - world_position.xyz;
|
let light_to_frag = (*light).position_radius.xyz - world_position.xyz;
|
||||||
let distance_square = dot(light_to_frag, light_to_frag);
|
let distance_square = dot(light_to_frag, light_to_frag);
|
||||||
let rangeAttenuation = getDistanceAttenuation(distance_square, (*light).color_inverse_square_range.w);
|
let rangeAttenuation = getDistanceAttenuation(distance_square, (*light).color_inverse_square_range.w);
|
||||||
@ -244,12 +248,12 @@ fn spot_light(
|
|||||||
// reuse the point light calculations
|
// reuse the point light calculations
|
||||||
let point_light = point_light(world_position, light_id, roughness, NdotV, N, V, R, F0, f_ab, diffuseColor);
|
let point_light = point_light(world_position, light_id, roughness, NdotV, N, V, R, F0, f_ab, diffuseColor);
|
||||||
|
|
||||||
let light = &point_lights.data[light_id];
|
let light = &view_bindings::point_lights.data[light_id];
|
||||||
|
|
||||||
// reconstruct spot dir from x/z and y-direction flag
|
// reconstruct spot dir from x/z and y-direction flag
|
||||||
var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y);
|
var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y);
|
||||||
spot_dir.y = sqrt(max(0.0, 1.0 - spot_dir.x * spot_dir.x - spot_dir.z * spot_dir.z));
|
spot_dir.y = sqrt(max(0.0, 1.0 - spot_dir.x * spot_dir.x - spot_dir.z * spot_dir.z));
|
||||||
if ((*light).flags & POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE) != 0u {
|
if ((*light).flags & view_types::POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE) != 0u {
|
||||||
spot_dir.y = -spot_dir.y;
|
spot_dir.y = -spot_dir.y;
|
||||||
}
|
}
|
||||||
let light_to_frag = (*light).position_radius.xyz - world_position.xyz;
|
let light_to_frag = (*light).position_radius.xyz - world_position.xyz;
|
||||||
@ -265,7 +269,7 @@ fn spot_light(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn directional_light(light_id: u32, roughness: f32, NdotV: f32, normal: vec3<f32>, view: vec3<f32>, R: vec3<f32>, F0: vec3<f32>, f_ab: vec2<f32>, diffuseColor: vec3<f32>) -> vec3<f32> {
|
fn directional_light(light_id: u32, roughness: f32, NdotV: f32, normal: vec3<f32>, view: vec3<f32>, R: vec3<f32>, F0: vec3<f32>, f_ab: vec2<f32>, diffuseColor: vec3<f32>) -> vec3<f32> {
|
||||||
let light = &lights.directional_lights[light_id];
|
let light = &view_bindings::lights.directional_lights[light_id];
|
||||||
|
|
||||||
let incident_light = (*light).direction_to_light.xyz;
|
let incident_light = (*light).direction_to_light.xyz;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#import bevy_pbr::prepass_bindings
|
#import bevy_pbr::prepass_bindings
|
||||||
#import bevy_pbr::pbr_bindings
|
#import bevy_pbr::pbr_bindings
|
||||||
|
#import bevy_pbr::pbr_types
|
||||||
#ifdef NORMAL_PREPASS
|
#ifdef NORMAL_PREPASS
|
||||||
#import bevy_pbr::pbr_functions
|
#import bevy_pbr::pbr_functions
|
||||||
#endif // NORMAL_PREPASS
|
#endif // NORMAL_PREPASS
|
||||||
@ -35,24 +36,24 @@ const PREMULTIPLIED_ALPHA_CUTOFF = 0.05;
|
|||||||
fn prepass_alpha_discard(in: FragmentInput) {
|
fn prepass_alpha_discard(in: FragmentInput) {
|
||||||
|
|
||||||
#ifdef MAY_DISCARD
|
#ifdef MAY_DISCARD
|
||||||
var output_color: vec4<f32> = material.base_color;
|
var output_color: vec4<f32> = bevy_pbr::pbr_bindings::material.base_color;
|
||||||
|
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if (material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
|
if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
|
||||||
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, in.uv, view.mip_bias);
|
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
|
#endif // VERTEX_UVS
|
||||||
|
|
||||||
let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
let alpha_mode = bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
|
||||||
if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
if alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK {
|
||||||
if output_color.a < material.alpha_cutoff {
|
if output_color.a < bevy_pbr::pbr_bindings::material.alpha_cutoff {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
} else if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) {
|
} 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 {
|
if output_color.a < PREMULTIPLIED_ALPHA_CUTOFF {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
} else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED {
|
} else if alpha_mode == bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED {
|
||||||
if all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
|
if all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
@ -88,15 +89,15 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
|
|||||||
|
|
||||||
#ifdef NORMAL_PREPASS
|
#ifdef NORMAL_PREPASS
|
||||||
// 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 (material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
|
if (bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
|
||||||
let world_normal = prepare_world_normal(
|
let world_normal = bevy_pbr::pbr_functions::prepare_world_normal(
|
||||||
in.world_normal,
|
in.world_normal,
|
||||||
(material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
(bevy_pbr::pbr_bindings::material.flags & bevy_pbr::pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
||||||
in.is_front,
|
in.is_front,
|
||||||
);
|
);
|
||||||
|
|
||||||
let normal = apply_normal_mapping(
|
let normal = bevy_pbr::pbr_functions::apply_normal_mapping(
|
||||||
material.flags,
|
bevy_pbr::pbr_bindings::material.flags,
|
||||||
world_normal,
|
world_normal,
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
@ -115,9 +116,9 @@ fn fragment(in: FragmentInput) -> FragmentOutput {
|
|||||||
#endif // NORMAL_PREPASS
|
#endif // NORMAL_PREPASS
|
||||||
|
|
||||||
#ifdef MOTION_VECTOR_PREPASS
|
#ifdef MOTION_VECTOR_PREPASS
|
||||||
let clip_position_t = view.unjittered_view_proj * in.world_position;
|
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 clip_position = clip_position_t.xy / clip_position_t.w;
|
||||||
let previous_clip_position_t = previous_view_proj * in.previous_world_position;
|
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;
|
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
|
// 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
|
// in the range -1,1 to allow offsetting from the one corner to the
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#define_import_path bevy_pbr::shadows
|
#define_import_path bevy_pbr::shadows
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_view_types POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE
|
||||||
|
#import bevy_pbr::mesh_view_bindings as view_bindings
|
||||||
|
#import bevy_pbr::utils hsv2rgb
|
||||||
const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0);
|
const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0);
|
||||||
|
|
||||||
fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
||||||
let light = &point_lights.data[light_id];
|
let light = &view_bindings::point_lights.data[light_id];
|
||||||
|
|
||||||
// because the shadow maps align with the axes and the frustum planes are at 45 degrees
|
// because the shadow maps align with the axes and the frustum planes are at 45 degrees
|
||||||
// we can get the worldspace depth by taking the largest absolute axis
|
// we can get the worldspace depth by taking the largest absolute axis
|
||||||
@ -38,14 +41,14 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: v
|
|||||||
// mip-mapping functionality. The shadow maps have no mipmaps so Level just samples
|
// mip-mapping functionality. The shadow maps have no mipmaps so Level just samples
|
||||||
// from LOD 0.
|
// from LOD 0.
|
||||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||||
return textureSampleCompare(point_shadow_textures, point_shadow_textures_sampler, frag_ls * flip_z, depth);
|
return textureSampleCompare(view_bindings::point_shadow_textures, view_bindings::point_shadow_textures_sampler, frag_ls * flip_z, depth);
|
||||||
#else
|
#else
|
||||||
return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls * flip_z, i32(light_id), depth);
|
return textureSampleCompareLevel(view_bindings::point_shadow_textures, view_bindings::point_shadow_textures_sampler, frag_ls * flip_z, i32(light_id), depth);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
||||||
let light = &point_lights.data[light_id];
|
let light = &view_bindings::point_lights.data[light_id];
|
||||||
|
|
||||||
let surface_to_light = (*light).position_radius.xyz - frag_position.xyz;
|
let surface_to_light = (*light).position_radius.xyz - frag_position.xyz;
|
||||||
|
|
||||||
@ -93,16 +96,16 @@ fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: ve
|
|||||||
let depth = 0.1 / -projected_position.z;
|
let depth = 0.1 / -projected_position.z;
|
||||||
|
|
||||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||||
return textureSampleCompare(directional_shadow_textures, directional_shadow_textures_sampler,
|
return textureSampleCompare(view_bindings::directional_shadow_textures, view_bindings::directional_shadow_textures_sampler,
|
||||||
shadow_uv, depth);
|
shadow_uv, depth);
|
||||||
#else
|
#else
|
||||||
return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler,
|
return textureSampleCompareLevel(view_bindings::directional_shadow_textures, view_bindings::directional_shadow_textures_sampler,
|
||||||
shadow_uv, i32(light_id) + lights.spot_light_shadowmap_offset, depth);
|
shadow_uv, i32(light_id) + view_bindings::lights.spot_light_shadowmap_offset, depth);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
|
fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
|
||||||
let light = &lights.directional_lights[light_id];
|
let light = &view_bindings::lights.directional_lights[light_id];
|
||||||
|
|
||||||
for (var i: u32 = 0u; i < (*light).num_cascades; i = i + 1u) {
|
for (var i: u32 = 0u; i < (*light).num_cascades; i = i + 1u) {
|
||||||
if (-view_z < (*light).cascades[i].far_bound) {
|
if (-view_z < (*light).cascades[i].far_bound) {
|
||||||
@ -113,7 +116,7 @@ fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
||||||
let light = &lights.directional_lights[light_id];
|
let light = &view_bindings::lights.directional_lights[light_id];
|
||||||
let cascade = &(*light).cascades[cascade_index];
|
let cascade = &(*light).cascades[cascade_index];
|
||||||
|
|
||||||
// The normal bias is scaled to the texel size.
|
// The normal bias is scaled to the texel size.
|
||||||
@ -143,15 +146,15 @@ fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, s
|
|||||||
// sampler to avoid use of implicit derivatives causing possible undefined behavior.
|
// sampler to avoid use of implicit derivatives causing possible undefined behavior.
|
||||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||||
return textureSampleCompareLevel(
|
return textureSampleCompareLevel(
|
||||||
directional_shadow_textures,
|
view_bindings::directional_shadow_textures,
|
||||||
directional_shadow_textures_sampler,
|
view_bindings::directional_shadow_textures_sampler,
|
||||||
light_local,
|
light_local,
|
||||||
depth
|
depth
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
return textureSampleCompareLevel(
|
return textureSampleCompareLevel(
|
||||||
directional_shadow_textures,
|
view_bindings::directional_shadow_textures,
|
||||||
directional_shadow_textures_sampler,
|
view_bindings::directional_shadow_textures_sampler,
|
||||||
light_local,
|
light_local,
|
||||||
i32((*light).depth_texture_base_index + cascade_index),
|
i32((*light).depth_texture_base_index + cascade_index),
|
||||||
depth
|
depth
|
||||||
@ -160,7 +163,7 @@ fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>, view_z: f32) -> f32 {
|
fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>, view_z: f32) -> f32 {
|
||||||
let light = &lights.directional_lights[light_id];
|
let light = &view_bindings::lights.directional_lights[light_id];
|
||||||
let cascade_index = get_cascade_index(light_id, view_z);
|
let cascade_index = get_cascade_index(light_id, view_z);
|
||||||
|
|
||||||
if (cascade_index >= (*light).num_cascades) {
|
if (cascade_index >= (*light).num_cascades) {
|
||||||
|
@ -1,9 +1,22 @@
|
|||||||
// If using this WGSL snippet as an #import, a dedicated
|
|
||||||
// "joint_matricies" uniform of type SkinnedMesh must be added in the
|
|
||||||
// main shader.
|
|
||||||
|
|
||||||
#define_import_path bevy_pbr::skinning
|
#define_import_path bevy_pbr::skinning
|
||||||
|
|
||||||
|
#import bevy_pbr::mesh_types SkinnedMesh
|
||||||
|
|
||||||
|
#ifdef SKINNED
|
||||||
|
|
||||||
|
#ifdef MESH_BINDGROUP_1
|
||||||
|
|
||||||
|
@group(1) @binding(1)
|
||||||
|
var<uniform> joint_matrices: SkinnedMesh;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
@group(2) @binding(1)
|
||||||
|
var<uniform> joint_matrices: SkinnedMesh;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
fn skin_model(
|
fn skin_model(
|
||||||
indexes: vec4<u32>,
|
indexes: vec4<u32>,
|
||||||
weights: vec4<f32>,
|
weights: vec4<f32>,
|
||||||
@ -14,7 +27,7 @@ fn skin_model(
|
|||||||
+ weights.w * joint_matrices.data[indexes.w];
|
+ weights.w * joint_matrices.data[indexes.w];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inverse_transpose_3x3(in: mat3x3<f32>) -> mat3x3<f32> {
|
fn inverse_transpose_3x3m(in: mat3x3<f32>) -> mat3x3<f32> {
|
||||||
let x = cross(in[1], in[2]);
|
let x = cross(in[1], in[2]);
|
||||||
let y = cross(in[2], in[0]);
|
let y = cross(in[2], in[0]);
|
||||||
let z = cross(in[0], in[1]);
|
let z = cross(in[0], in[1]);
|
||||||
@ -31,7 +44,7 @@ fn skin_normals(
|
|||||||
normal: vec3<f32>,
|
normal: vec3<f32>,
|
||||||
) -> vec3<f32> {
|
) -> vec3<f32> {
|
||||||
return normalize(
|
return normalize(
|
||||||
inverse_transpose_3x3(
|
inverse_transpose_3x3m(
|
||||||
mat3x3<f32>(
|
mat3x3<f32>(
|
||||||
model[0].xyz,
|
model[0].xyz,
|
||||||
model[1].xyz,
|
model[1].xyz,
|
||||||
@ -40,3 +53,5 @@ fn skin_normals(
|
|||||||
) * normal
|
) * normal
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
#import bevy_pbr::mesh_types
|
#import bevy_pbr::mesh_bindings mesh
|
||||||
#import bevy_pbr::mesh_view_bindings
|
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||||
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> mesh: Mesh;
|
|
||||||
|
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
@group(1) @binding(1)
|
|
||||||
var<uniform> joint_matrices: SkinnedMesh;
|
|
||||||
#import bevy_pbr::skinning
|
#import bevy_pbr::skinning
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: Bindings must come before functions that use them!
|
|
||||||
#import bevy_pbr::mesh_functions
|
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
@ -28,7 +20,7 @@ struct VertexOutput {
|
|||||||
@vertex
|
@vertex
|
||||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||||
#ifdef SKINNED
|
#ifdef SKINNED
|
||||||
let model = skin_model(vertex.joint_indexes, vertex.joint_weights);
|
let model = bevy_pbr::skinning::skin_model(vertex.joint_indexes, vertex.joint_weights);
|
||||||
#else
|
#else
|
||||||
let model = mesh.model;
|
let model = mesh.model;
|
||||||
#endif
|
#endif
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
// Source code heavily based on XeGTAO v1.30 from Intel
|
// Source code heavily based on XeGTAO v1.30 from Intel
|
||||||
// https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli
|
// https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli
|
||||||
|
|
||||||
#import bevy_pbr::gtao_utils
|
#import bevy_pbr::gtao_utils fast_acos
|
||||||
#import bevy_pbr::utils
|
#import bevy_pbr::utils PI, HALF_PI
|
||||||
#import bevy_render::view
|
#import bevy_render::view View
|
||||||
#import bevy_render::globals
|
#import bevy_render::globals Globals
|
||||||
|
|
||||||
@group(0) @binding(0) var preprocessed_depth: texture_2d<f32>;
|
@group(0) @binding(0) var preprocessed_depth: texture_2d<f32>;
|
||||||
@group(0) @binding(1) var normals: texture_2d<f32>;
|
@group(0) @binding(1) var normals: texture_2d<f32>;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#define_import_path bevy_pbr::gtao_utils
|
#define_import_path bevy_pbr::gtao_utils
|
||||||
|
|
||||||
|
#import bevy_pbr::utils PI, HALF_PI
|
||||||
|
|
||||||
// Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion
|
// Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion
|
||||||
// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78
|
// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78
|
||||||
fn gtao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> {
|
fn gtao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
// Reference: https://research.nvidia.com/sites/default/files/pubs/2012-06_Scalable-Ambient-Obscurance/McGuire12SAO.pdf, section 2.2
|
// Reference: https://research.nvidia.com/sites/default/files/pubs/2012-06_Scalable-Ambient-Obscurance/McGuire12SAO.pdf, section 2.2
|
||||||
|
|
||||||
#import bevy_render::view
|
#import bevy_render::view View
|
||||||
|
|
||||||
@group(0) @binding(0) var input_depth: texture_depth_2d;
|
@group(0) @binding(0) var input_depth: texture_depth_2d;
|
||||||
@group(0) @binding(1) var preprocessed_depth_mip0: texture_storage_2d<r16float, write>;
|
@group(0) @binding(1) var preprocessed_depth_mip0: texture_storage_2d<r16float, write>;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
// XeGTAO does a 3x3 filter, on two pixels at a time per compute thread, applied twice
|
// XeGTAO does a 3x3 filter, on two pixels at a time per compute thread, applied twice
|
||||||
// We do a 3x3 filter, on 1 pixel per compute thread, applied once
|
// We do a 3x3 filter, on 1 pixel per compute thread, applied once
|
||||||
|
|
||||||
#import bevy_render::view
|
#import bevy_render::view View
|
||||||
|
|
||||||
@group(0) @binding(0) var ambient_occlusion_noisy: texture_2d<f32>;
|
@group(0) @binding(0) var ambient_occlusion_noisy: texture_2d<f32>;
|
||||||
@group(0) @binding(1) var depth_differences: texture_2d<u32>;
|
@group(0) @binding(1) var depth_differences: texture_2d<u32>;
|
||||||
|
@ -96,6 +96,10 @@ impl SpecializedMeshPipeline for WireframePipeline {
|
|||||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||||
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
|
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
|
||||||
descriptor.vertex.shader = self.shader.clone_weak();
|
descriptor.vertex.shader = self.shader.clone_weak();
|
||||||
|
descriptor
|
||||||
|
.vertex
|
||||||
|
.shader_defs
|
||||||
|
.push("MESH_BINDGROUP_1".into());
|
||||||
descriptor.fragment.as_mut().unwrap().shader = self.shader.clone_weak();
|
descriptor.fragment.as_mut().unwrap().shader = self.shader.clone_weak();
|
||||||
descriptor.primitive.polygon_mode = PolygonMode::Line;
|
descriptor.primitive.polygon_mode = PolygonMode::Line;
|
||||||
descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0;
|
descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0;
|
||||||
|
@ -57,7 +57,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
|
|||||||
image = { version = "0.24", default-features = false }
|
image = { version = "0.24", default-features = false }
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
wgpu = { version = "0.16.0" }
|
wgpu = { version = "0.16.0", features=["naga"] }
|
||||||
wgpu-hal = "0.16.0"
|
wgpu-hal = "0.16.0"
|
||||||
codespan-reporting = "0.11.0"
|
codespan-reporting = "0.11.0"
|
||||||
naga = { version = "0.12.0", features = ["wgsl-in"] }
|
naga = { version = "0.12.0", features = ["wgsl-in"] }
|
||||||
@ -76,6 +76,7 @@ parking_lot = "0.12.1"
|
|||||||
regex = "1.5"
|
regex = "1.5"
|
||||||
ddsfile = { version = "0.5.0", optional = true }
|
ddsfile = { version = "0.5.0", optional = true }
|
||||||
ktx2 = { version = "0.3.0", optional = true }
|
ktx2 = { version = "0.3.0", optional = true }
|
||||||
|
naga_oil = "0.8"
|
||||||
# For ktx2 supercompression
|
# For ktx2 supercompression
|
||||||
flate2 = { version = "1.0.22", optional = true }
|
flate2 = { version = "1.0.22", optional = true }
|
||||||
ruzstd = { version = "0.4.0", optional = true }
|
ruzstd = { version = "0.4.0", optional = true }
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
render_resource::{
|
render_resource::{
|
||||||
AsModuleDescriptorError, BindGroupLayout, BindGroupLayoutId, ComputePipeline,
|
BindGroupLayout, BindGroupLayoutId, ComputePipeline, ComputePipelineDescriptor,
|
||||||
ComputePipelineDescriptor, ProcessShaderError, ProcessedShader,
|
|
||||||
RawComputePipelineDescriptor, RawFragmentState, RawRenderPipelineDescriptor,
|
RawComputePipelineDescriptor, RawFragmentState, RawRenderPipelineDescriptor,
|
||||||
RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport,
|
RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport, Source,
|
||||||
ShaderProcessor, ShaderReflectError,
|
|
||||||
},
|
},
|
||||||
renderer::RenderDevice,
|
renderer::RenderDevice,
|
||||||
Extract,
|
Extract,
|
||||||
@ -17,11 +15,15 @@ use bevy_utils::{
|
|||||||
tracing::{debug, error},
|
tracing::{debug, error},
|
||||||
Entry, HashMap, HashSet,
|
Entry, HashMap, HashSet,
|
||||||
};
|
};
|
||||||
|
use naga::valid::Capabilities;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref};
|
use std::{borrow::Cow, hash::Hash, mem, ops::Deref};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
#[cfg(feature = "shader_format_spirv")]
|
||||||
|
use wgpu::util::make_spirv;
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
PipelineLayoutDescriptor, PushConstantRange, VertexBufferLayout as RawVertexBufferLayout,
|
Features, PipelineLayoutDescriptor, PushConstantRange, ShaderModuleDescriptor,
|
||||||
|
VertexBufferLayout as RawVertexBufferLayout,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::render_resource::resource_macros::*;
|
use crate::render_resource::resource_macros::*;
|
||||||
@ -123,13 +125,12 @@ struct ShaderData {
|
|||||||
dependents: HashSet<Handle<Shader>>,
|
dependents: HashSet<Handle<Shader>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ShaderCache {
|
struct ShaderCache {
|
||||||
data: HashMap<Handle<Shader>, ShaderData>,
|
data: HashMap<Handle<Shader>, ShaderData>,
|
||||||
shaders: HashMap<Handle<Shader>, Shader>,
|
shaders: HashMap<Handle<Shader>, Shader>,
|
||||||
import_path_shaders: HashMap<ShaderImport, Handle<Shader>>,
|
import_path_shaders: HashMap<ShaderImport, Handle<Shader>>,
|
||||||
waiting_on_import: HashMap<ShaderImport, Vec<Handle<Shader>>>,
|
waiting_on_import: HashMap<ShaderImport, Vec<Handle<Shader>>>,
|
||||||
processor: ShaderProcessor,
|
composer: naga_oil::compose::Composer,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
@ -162,6 +163,78 @@ impl ShaderDefVal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ShaderCache {
|
impl ShaderCache {
|
||||||
|
fn new(render_device: &RenderDevice) -> Self {
|
||||||
|
const CAPABILITIES: &[(Features, Capabilities)] = &[
|
||||||
|
(Features::PUSH_CONSTANTS, Capabilities::PUSH_CONSTANT),
|
||||||
|
(Features::SHADER_F64, Capabilities::FLOAT64),
|
||||||
|
(
|
||||||
|
Features::SHADER_PRIMITIVE_INDEX,
|
||||||
|
Capabilities::PRIMITIVE_INDEX,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
|
Capabilities::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
|
Capabilities::SAMPLER_NON_UNIFORM_INDEXING,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
|
Capabilities::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let features = render_device.features();
|
||||||
|
let mut capabilities = Capabilities::empty();
|
||||||
|
for (feature, capability) in CAPABILITIES {
|
||||||
|
if features.contains(*feature) {
|
||||||
|
capabilities |= *capability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let composer = naga_oil::compose::Composer::default();
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
let composer = naga_oil::compose::Composer::non_validating();
|
||||||
|
|
||||||
|
let composer = composer.with_capabilities(capabilities);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
composer,
|
||||||
|
data: Default::default(),
|
||||||
|
shaders: Default::default(),
|
||||||
|
import_path_shaders: Default::default(),
|
||||||
|
waiting_on_import: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_import_to_composer(
|
||||||
|
composer: &mut naga_oil::compose::Composer,
|
||||||
|
import_path_shaders: &HashMap<ShaderImport, Handle<Shader>>,
|
||||||
|
shaders: &HashMap<Handle<Shader>, Shader>,
|
||||||
|
import: &ShaderImport,
|
||||||
|
) -> Result<(), PipelineCacheError> {
|
||||||
|
if !composer.contains_module(import.as_str()) {
|
||||||
|
if let Some(shader_handle) = import_path_shaders.get(import) {
|
||||||
|
if let Some(shader) = shaders.get(shader_handle) {
|
||||||
|
for import in &shader.imports {
|
||||||
|
Self::add_import_to_composer(
|
||||||
|
composer,
|
||||||
|
import_path_shaders,
|
||||||
|
shaders,
|
||||||
|
import,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
composer.add_composable_module(shader.into())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we fail to add a module the composer will tell us what is missing
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::result_large_err)]
|
#[allow(clippy::result_large_err)]
|
||||||
fn get(
|
fn get(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -210,19 +283,54 @@ impl ShaderCache {
|
|||||||
"processing shader {:?}, with shader defs {:?}",
|
"processing shader {:?}, with shader defs {:?}",
|
||||||
handle, shader_defs
|
handle, shader_defs
|
||||||
);
|
);
|
||||||
let processed = self.processor.process(
|
let shader_source = match &shader.source {
|
||||||
shader,
|
#[cfg(feature = "shader_format_spirv")]
|
||||||
&shader_defs,
|
Source::SpirV(data) => make_spirv(data),
|
||||||
&self.shaders,
|
#[cfg(not(feature = "shader_format_spirv"))]
|
||||||
&self.import_path_shaders,
|
Source::SpirV(_) => {
|
||||||
)?;
|
unimplemented!(
|
||||||
let module_descriptor = match processed
|
"Enable feature \"shader_format_spirv\" to use SPIR-V shaders"
|
||||||
.get_module_descriptor(render_device.features())
|
)
|
||||||
{
|
|
||||||
Ok(module_descriptor) => module_descriptor,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(PipelineCacheError::AsModuleDescriptorError(err, processed));
|
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
for import in shader.imports() {
|
||||||
|
Self::add_import_to_composer(
|
||||||
|
&mut self.composer,
|
||||||
|
&self.import_path_shaders,
|
||||||
|
&self.shaders,
|
||||||
|
import,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let shader_defs = shader_defs
|
||||||
|
.into_iter()
|
||||||
|
.map(|def| match def {
|
||||||
|
ShaderDefVal::Bool(k, v) => {
|
||||||
|
(k, naga_oil::compose::ShaderDefValue::Bool(v))
|
||||||
|
}
|
||||||
|
ShaderDefVal::Int(k, v) => {
|
||||||
|
(k, naga_oil::compose::ShaderDefValue::Int(v))
|
||||||
|
}
|
||||||
|
ShaderDefVal::UInt(k, v) => {
|
||||||
|
(k, naga_oil::compose::ShaderDefValue::UInt(v))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<std::collections::HashMap<_, _>>();
|
||||||
|
|
||||||
|
let naga = self.composer.make_naga_module(
|
||||||
|
naga_oil::compose::NagaModuleDescriptor {
|
||||||
|
shader_defs,
|
||||||
|
..shader.into()
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
wgpu::ShaderSource::Naga(Cow::Owned(naga))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let module_descriptor = ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: shader_source,
|
||||||
};
|
};
|
||||||
|
|
||||||
render_device
|
render_device
|
||||||
@ -256,6 +364,10 @@ impl ShaderCache {
|
|||||||
data.processed_shaders.clear();
|
data.processed_shaders.clear();
|
||||||
pipelines_to_queue.extend(data.pipelines.iter().cloned());
|
pipelines_to_queue.extend(data.pipelines.iter().cloned());
|
||||||
shaders_to_clear.extend(data.dependents.iter().map(|h| h.clone_weak()));
|
shaders_to_clear.extend(data.dependents.iter().map(|h| h.clone_weak()));
|
||||||
|
|
||||||
|
if let Some(Shader { import_path, .. }) = self.shaders.get(&handle) {
|
||||||
|
self.composer.remove_composable_module(import_path.as_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +376,7 @@ impl ShaderCache {
|
|||||||
|
|
||||||
fn set_shader(&mut self, handle: &Handle<Shader>, shader: Shader) -> Vec<CachedPipelineId> {
|
fn set_shader(&mut self, handle: &Handle<Shader>, shader: Shader) -> Vec<CachedPipelineId> {
|
||||||
let pipelines_to_queue = self.clear(handle);
|
let pipelines_to_queue = self.clear(handle);
|
||||||
if let Some(path) = shader.import_path() {
|
let path = shader.import_path();
|
||||||
self.import_path_shaders
|
self.import_path_shaders
|
||||||
.insert(path.clone(), handle.clone_weak());
|
.insert(path.clone(), handle.clone_weak());
|
||||||
if let Some(waiting_shaders) = self.waiting_on_import.get_mut(path) {
|
if let Some(waiting_shaders) = self.waiting_on_import.get_mut(path) {
|
||||||
@ -278,7 +390,6 @@ impl ShaderCache {
|
|||||||
data.dependents.insert(waiting_shader.clone_weak());
|
data.dependents.insert(waiting_shader.clone_weak());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for import in shader.imports() {
|
for import in shader.imports() {
|
||||||
if let Some(import_handle) = self.import_path_shaders.get(import) {
|
if let Some(import_handle) = self.import_path_shaders.get(import) {
|
||||||
@ -302,9 +413,7 @@ impl ShaderCache {
|
|||||||
fn remove(&mut self, handle: &Handle<Shader>) -> Vec<CachedPipelineId> {
|
fn remove(&mut self, handle: &Handle<Shader>) -> Vec<CachedPipelineId> {
|
||||||
let pipelines_to_queue = self.clear(handle);
|
let pipelines_to_queue = self.clear(handle);
|
||||||
if let Some(shader) = self.shaders.remove(handle) {
|
if let Some(shader) = self.shaders.remove(handle) {
|
||||||
if let Some(import_path) = shader.import_path() {
|
self.import_path_shaders.remove(shader.import_path());
|
||||||
self.import_path_shaders.remove(import_path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelines_to_queue
|
pipelines_to_queue
|
||||||
@ -373,9 +482,9 @@ impl PipelineCache {
|
|||||||
/// Create a new pipeline cache associated with the given render device.
|
/// Create a new pipeline cache associated with the given render device.
|
||||||
pub fn new(device: RenderDevice) -> Self {
|
pub fn new(device: RenderDevice) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
shader_cache: ShaderCache::new(&device),
|
||||||
device,
|
device,
|
||||||
layout_cache: default(),
|
layout_cache: default(),
|
||||||
shader_cache: default(),
|
|
||||||
waiting_pipelines: default(),
|
waiting_pipelines: default(),
|
||||||
new_pipelines: default(),
|
new_pipelines: default(),
|
||||||
pipelines: default(),
|
pipelines: default(),
|
||||||
@ -697,11 +806,8 @@ impl PipelineCache {
|
|||||||
}
|
}
|
||||||
// shader could not be processed ... retrying won't help
|
// shader could not be processed ... retrying won't help
|
||||||
PipelineCacheError::ProcessShaderError(err) => {
|
PipelineCacheError::ProcessShaderError(err) => {
|
||||||
error!("failed to process shader: {}", err);
|
let error_detail = err.emit_to_string(&self.shader_cache.composer);
|
||||||
continue;
|
error!("failed to process shader:\n{}", error_detail);
|
||||||
}
|
|
||||||
PipelineCacheError::AsModuleDescriptorError(err, source) => {
|
|
||||||
log_shader_error(source, err);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
PipelineCacheError::CreateShaderModule(description) => {
|
PipelineCacheError::CreateShaderModule(description) => {
|
||||||
@ -737,101 +843,6 @@ impl PipelineCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {
|
|
||||||
use codespan_reporting::{
|
|
||||||
diagnostic::{Diagnostic, Label},
|
|
||||||
files::SimpleFile,
|
|
||||||
term,
|
|
||||||
};
|
|
||||||
|
|
||||||
match error {
|
|
||||||
AsModuleDescriptorError::ShaderReflectError(error) => match error {
|
|
||||||
ShaderReflectError::WgslParse(error) => {
|
|
||||||
let source = source
|
|
||||||
.get_wgsl_source()
|
|
||||||
.expect("non-wgsl source for wgsl error");
|
|
||||||
let msg = error.emit_to_string(source);
|
|
||||||
error!("failed to process shader:\n{}", msg);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "shader_format_glsl")]
|
|
||||||
ShaderReflectError::GlslParse(errors) => {
|
|
||||||
let source = source
|
|
||||||
.get_glsl_source()
|
|
||||||
.expect("non-glsl source for glsl error");
|
|
||||||
let files = SimpleFile::new("glsl", source);
|
|
||||||
let config = codespan_reporting::term::Config::default();
|
|
||||||
let mut writer = term::termcolor::Ansi::new(Vec::new());
|
|
||||||
|
|
||||||
for err in errors {
|
|
||||||
let mut diagnostic = Diagnostic::error().with_message(err.kind.to_string());
|
|
||||||
|
|
||||||
if let Some(range) = err.meta.to_range() {
|
|
||||||
diagnostic = diagnostic.with_labels(vec![Label::primary((), range)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
term::emit(&mut writer, &config, &files, &diagnostic)
|
|
||||||
.expect("cannot write error");
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = writer.into_inner();
|
|
||||||
let msg = String::from_utf8_lossy(&msg);
|
|
||||||
|
|
||||||
error!("failed to process shader: \n{}", msg);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "shader_format_spirv")]
|
|
||||||
ShaderReflectError::SpirVParse(error) => {
|
|
||||||
error!("failed to process shader:\n{}", error);
|
|
||||||
}
|
|
||||||
ShaderReflectError::Validation(error) => {
|
|
||||||
let (filename, source) = match source {
|
|
||||||
ProcessedShader::Wgsl(source) => ("wgsl", source.as_ref()),
|
|
||||||
ProcessedShader::Glsl(source, _) => ("glsl", source.as_ref()),
|
|
||||||
ProcessedShader::SpirV(_) => {
|
|
||||||
error!("failed to process shader:\n{}", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let files = SimpleFile::new(filename, source);
|
|
||||||
let config = term::Config::default();
|
|
||||||
let mut writer = term::termcolor::Ansi::new(Vec::new());
|
|
||||||
|
|
||||||
let diagnostic = Diagnostic::error()
|
|
||||||
.with_message(error.to_string())
|
|
||||||
.with_labels(
|
|
||||||
error
|
|
||||||
.spans()
|
|
||||||
.map(|(span, desc)| {
|
|
||||||
Label::primary((), span.to_range().unwrap())
|
|
||||||
.with_message(desc.to_owned())
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.with_notes(
|
|
||||||
ErrorSources::of(error)
|
|
||||||
.map(|source| source.to_string())
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
term::emit(&mut writer, &config, &files, &diagnostic).expect("cannot write error");
|
|
||||||
|
|
||||||
let msg = writer.into_inner();
|
|
||||||
let msg = String::from_utf8_lossy(&msg);
|
|
||||||
|
|
||||||
error!("failed to process shader: \n{}", msg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(feature = "shader_format_glsl")]
|
|
||||||
AsModuleDescriptorError::WgslConversion(error) => {
|
|
||||||
error!("failed to convert shader to wgsl: \n{}", error);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "shader_format_spirv")]
|
|
||||||
AsModuleDescriptorError::SpirVConversion(error) => {
|
|
||||||
error!("failed to convert shader to spirv: \n{}", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type of error returned by a [`PipelineCache`] when the creation of a GPU pipeline object failed.
|
/// Type of error returned by a [`PipelineCache`] when the creation of a GPU pipeline object failed.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum PipelineCacheError {
|
pub enum PipelineCacheError {
|
||||||
@ -840,35 +851,9 @@ pub enum PipelineCacheError {
|
|||||||
)]
|
)]
|
||||||
ShaderNotLoaded(Handle<Shader>),
|
ShaderNotLoaded(Handle<Shader>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ProcessShaderError(#[from] ProcessShaderError),
|
ProcessShaderError(#[from] naga_oil::compose::ComposerError),
|
||||||
#[error("{0}")]
|
|
||||||
AsModuleDescriptorError(AsModuleDescriptorError, ProcessedShader),
|
|
||||||
#[error("Shader import not yet available.")]
|
#[error("Shader import not yet available.")]
|
||||||
ShaderImportNotYetAvailable,
|
ShaderImportNotYetAvailable,
|
||||||
#[error("Could not create shader module: {0}")]
|
#[error("Could not create shader module: {0}")]
|
||||||
CreateShaderModule(String),
|
CreateShaderModule(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ErrorSources<'a> {
|
|
||||||
current: Option<&'a (dyn std::error::Error + 'static)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ErrorSources<'a> {
|
|
||||||
fn of(error: &'a dyn std::error::Error) -> Self {
|
|
||||||
Self {
|
|
||||||
current: error.source(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for ErrorSources<'a> {
|
|
||||||
type Item = &'a (dyn std::error::Error + 'static);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let current = self.current;
|
|
||||||
self.current = self.current.and_then(std::error::Error::source);
|
|
||||||
current
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FusedIterator for ErrorSources<'a> {}
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,7 @@ pub use texture_atlas::*;
|
|||||||
pub use texture_atlas_builder::*;
|
pub use texture_atlas_builder::*;
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_asset::{AddAsset, Assets, Handle, HandleUntyped};
|
use bevy_asset::{load_internal_asset, AddAsset, Assets, Handle, HandleUntyped};
|
||||||
use bevy_core_pipeline::core_2d::Transparent2d;
|
use bevy_core_pipeline::core_2d::Transparent2d;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
@ -56,9 +56,12 @@ pub enum SpriteSystem {
|
|||||||
|
|
||||||
impl Plugin for SpritePlugin {
|
impl Plugin for SpritePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
let mut shaders = app.world.resource_mut::<Assets<Shader>>();
|
load_internal_asset!(
|
||||||
let sprite_shader = Shader::from_wgsl(include_str!("render/sprite.wgsl"));
|
app,
|
||||||
shaders.set_untracked(SPRITE_SHADER_HANDLE, sprite_shader);
|
SPRITE_SHADER_HANDLE,
|
||||||
|
"render/sprite.wgsl",
|
||||||
|
Shader::from_wgsl
|
||||||
|
);
|
||||||
app.add_asset::<TextureAtlas>()
|
app.add_asset::<TextureAtlas>()
|
||||||
.register_asset_reflect::<TextureAtlas>()
|
.register_asset_reflect::<TextureAtlas>()
|
||||||
.register_type::<Sprite>()
|
.register_type::<Sprite>()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#import bevy_sprite::mesh2d_types
|
#import bevy_sprite::mesh2d_types Mesh2d
|
||||||
#import bevy_sprite::mesh2d_view_bindings
|
#import bevy_sprite::mesh2d_vertex_output MeshVertexOutput
|
||||||
|
#import bevy_sprite::mesh2d_view_bindings view
|
||||||
|
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
#import bevy_core_pipeline::tonemapping
|
#import bevy_core_pipeline::tonemapping
|
||||||
@ -19,24 +20,19 @@ var texture: texture_2d<f32>;
|
|||||||
@group(1) @binding(2)
|
@group(1) @binding(2)
|
||||||
var texture_sampler: sampler;
|
var texture_sampler: sampler;
|
||||||
|
|
||||||
@group(2) @binding(0)
|
|
||||||
var<uniform> mesh: Mesh2d;
|
|
||||||
|
|
||||||
struct FragmentInput {
|
|
||||||
#import bevy_sprite::mesh2d_vertex_output
|
|
||||||
};
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
fn fragment(
|
||||||
|
mesh: MeshVertexOutput,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
var output_color: vec4<f32> = material.color;
|
var output_color: vec4<f32> = material.color;
|
||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
output_color = output_color * in.color;
|
output_color = output_color * mesh.color;
|
||||||
#endif
|
#endif
|
||||||
if ((material.flags & COLOR_MATERIAL_FLAGS_TEXTURE_BIT) != 0u) {
|
if ((material.flags & COLOR_MATERIAL_FLAGS_TEXTURE_BIT) != 0u) {
|
||||||
output_color = output_color * textureSample(texture, texture_sampler, in.uv);
|
output_color = output_color * textureSample(texture, texture_sampler, mesh.uv);
|
||||||
}
|
}
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
output_color = tone_mapping(output_color);
|
output_color = bevy_core_pipeline::tonemapping::tone_mapping(output_color, view.color_grading);
|
||||||
#endif
|
#endif
|
||||||
return output_color;
|
return output_color;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
#import bevy_sprite::mesh2d_view_bindings
|
#import bevy_sprite::mesh2d_functions as mesh_functions
|
||||||
#import bevy_sprite::mesh2d_bindings
|
#import bevy_sprite::mesh2d_bindings mesh
|
||||||
|
#import bevy_sprite::mesh2d_vertex_output MeshVertexOutput
|
||||||
// NOTE: Bindings must come before functions that use them!
|
#import bevy_sprite::mesh2d_view_bindings view
|
||||||
#import bevy_sprite::mesh2d_functions
|
|
||||||
|
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
#import bevy_core_pipeline::tonemapping
|
#import bevy_core_pipeline::tonemapping
|
||||||
@ -26,30 +25,30 @@ struct Vertex {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
#import bevy_sprite::mesh2d_vertex_output
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
fn vertex(vertex: Vertex) -> MeshVertexOutput {
|
||||||
var out: VertexOutput;
|
var out: MeshVertexOutput;
|
||||||
|
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
out.uv = vertex.uv;
|
out.uv = vertex.uv;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_POSITIONS
|
#ifdef VERTEX_POSITIONS
|
||||||
out.world_position = mesh2d_position_local_to_world(mesh.model, vec4<f32>(vertex.position, 1.0));
|
out.world_position = mesh_functions::mesh2d_position_local_to_world(
|
||||||
out.clip_position = mesh2d_position_world_to_clip(out.world_position);
|
mesh.model,
|
||||||
|
vec4<f32>(vertex.position, 1.0)
|
||||||
|
);
|
||||||
|
out.position = mesh_functions::mesh2d_position_world_to_clip(out.world_position);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_NORMALS
|
#ifdef VERTEX_NORMALS
|
||||||
out.world_normal = mesh2d_normal_local_to_world(vertex.normal);
|
out.world_normal = mesh_functions::mesh2d_normal_local_to_world(vertex.normal);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_TANGENTS
|
#ifdef VERTEX_TANGENTS
|
||||||
out.world_tangent = mesh2d_tangent_local_to_world(mesh.model, vertex.tangent);
|
out.world_tangent = mesh_functions::mesh2d_tangent_local_to_world(
|
||||||
|
mesh.model,
|
||||||
|
vertex.tangent
|
||||||
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
@ -58,16 +57,14 @@ fn vertex(vertex: Vertex) -> VertexOutput {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FragmentInput {
|
|
||||||
#import bevy_sprite::mesh2d_vertex_output
|
|
||||||
};
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
fn fragment(
|
||||||
|
in: MeshVertexOutput,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
var color = in.color;
|
var color = in.color;
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
color = tone_mapping(color);
|
color = bevy_core_pipeline::tonemapping::tone_mapping(color, view.color_grading);
|
||||||
#endif
|
#endif
|
||||||
return color;
|
return color;
|
||||||
#else
|
#else
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
#import bevy_sprite::mesh2d_types
|
#import bevy_sprite::mesh2d_types
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var<uniform> mesh: Mesh2d;
|
var<uniform> mesh: bevy_sprite::mesh2d_types::Mesh2d;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#define_import_path bevy_sprite::mesh2d_functions
|
#define_import_path bevy_sprite::mesh2d_functions
|
||||||
|
|
||||||
|
#import bevy_sprite::mesh2d_view_bindings view
|
||||||
|
#import bevy_sprite::mesh2d_bindings mesh
|
||||||
|
|
||||||
fn mesh2d_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
|
fn mesh2d_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
|
||||||
return model * vertex_position;
|
return model * vertex_position;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#define_import_path bevy_sprite::mesh2d_vertex_output
|
#define_import_path bevy_sprite::mesh2d_vertex_output
|
||||||
|
|
||||||
|
struct MeshVertexOutput {
|
||||||
|
// this is `clip position` when the struct is used as a vertex stage output
|
||||||
|
// and `frag coord` when used as a fragment stage input
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) world_position: vec4<f32>,
|
@location(0) world_position: vec4<f32>,
|
||||||
@location(1) world_normal: vec3<f32>,
|
@location(1) world_normal: vec3<f32>,
|
||||||
@location(2) uv: vec2<f32>,
|
@location(2) uv: vec2<f32>,
|
||||||
@ -9,3 +13,4 @@
|
|||||||
#ifdef VERTEX_COLORS
|
#ifdef VERTEX_COLORS
|
||||||
@location(4) color: vec4<f32>,
|
@location(4) color: vec4<f32>,
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#define_import_path bevy_sprite::mesh2d_view_bindings
|
#define_import_path bevy_sprite::mesh2d_view_bindings
|
||||||
|
|
||||||
#import bevy_sprite::mesh2d_view_types
|
#import bevy_render::view View
|
||||||
|
#import bevy_render::globals Globals
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var<uniform> view: View;
|
var<uniform> view: View;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#import bevy_core_pipeline::tonemapping
|
#import bevy_core_pipeline::tonemapping
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import bevy_render::view
|
#import bevy_render::view View
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var<uniform> view: View;
|
var<uniform> view: View;
|
||||||
@ -45,7 +45,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
color = tone_mapping(color);
|
color = bevy_core_pipeline::tonemapping::tone_mapping(color, view.color_grading);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return color;
|
return color;
|
||||||
|
@ -111,7 +111,7 @@ impl Plugin for TextPlugin {
|
|||||||
app,
|
app,
|
||||||
DEFAULT_FONT_HANDLE,
|
DEFAULT_FONT_HANDLE,
|
||||||
"FiraMono-subset.ttf",
|
"FiraMono-subset.ttf",
|
||||||
|bytes: &[u8]| { Font::try_from_bytes(bytes.to_vec()).unwrap() }
|
|bytes: &[u8], _path: String| { Font::try_from_bytes(bytes.to_vec()).unwrap() }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#import bevy_render::view
|
#import bevy_render::view View
|
||||||
|
|
||||||
const TEXTURED_QUAD: u32 = 0u;
|
const TEXTURED_QUAD: u32 = 0u;
|
||||||
|
|
||||||
|
@ -214,14 +214,11 @@ type DrawColoredMesh2d = (
|
|||||||
// using `include_str!()`, or loaded like any other asset with `asset_server.load()`.
|
// using `include_str!()`, or loaded like any other asset with `asset_server.load()`.
|
||||||
const COLORED_MESH2D_SHADER: &str = r"
|
const COLORED_MESH2D_SHADER: &str = r"
|
||||||
// Import the standard 2d mesh uniforms and set their bind groups
|
// Import the standard 2d mesh uniforms and set their bind groups
|
||||||
#import bevy_sprite::mesh2d_types
|
#import bevy_sprite::mesh2d_types as MeshTypes
|
||||||
#import bevy_sprite::mesh2d_view_bindings
|
#import bevy_sprite::mesh2d_functions as MeshFunctions
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> mesh: Mesh2d;
|
var<uniform> mesh: MeshTypes::Mesh2d;
|
||||||
|
|
||||||
// NOTE: Bindings must come before functions that use them!
|
|
||||||
#import bevy_sprite::mesh2d_functions
|
|
||||||
|
|
||||||
// The structure of the vertex buffer is as specified in `specialize()`
|
// The structure of the vertex buffer is as specified in `specialize()`
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@ -241,7 +238,7 @@ struct VertexOutput {
|
|||||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
// Project the world position of the mesh into screen position
|
// Project the world position of the mesh into screen position
|
||||||
out.clip_position = mesh2d_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
|
out.clip_position = MeshFunctions::mesh2d_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
|
||||||
// Unpack the `u32` from the vertex buffer into the `vec4<f32>` used by the fragment shader
|
// Unpack the `u32` from the vertex buffer into the `vec4<f32>` used by the fragment shader
|
||||||
out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0;
|
out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0;
|
||||||
return out;
|
return out;
|
||||||
@ -273,7 +270,7 @@ impl Plugin for ColoredMesh2dPlugin {
|
|||||||
let mut shaders = app.world.resource_mut::<Assets<Shader>>();
|
let mut shaders = app.world.resource_mut::<Assets<Shader>>();
|
||||||
shaders.set_untracked(
|
shaders.set_untracked(
|
||||||
COLORED_MESH2D_SHADER_HANDLE,
|
COLORED_MESH2D_SHADER_HANDLE,
|
||||||
Shader::from_wgsl(COLORED_MESH2D_SHADER),
|
Shader::from_wgsl(COLORED_MESH2D_SHADER, file!()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Register our custom draw function, and add our render systems
|
// Register our custom draw function, and add our render systems
|
||||||
|
@ -194,6 +194,15 @@ impl SpecializedMeshPipeline for CustomPipeline {
|
|||||||
layout: &MeshVertexBufferLayout,
|
layout: &MeshVertexBufferLayout,
|
||||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||||
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
|
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
|
||||||
|
|
||||||
|
// meshes typically live in bind group 2. because we are using bindgroup 1
|
||||||
|
// we need to add MESH_BINDGROUP_1 shader def so that the bindings are correctly
|
||||||
|
// linked in the shader
|
||||||
|
descriptor
|
||||||
|
.vertex
|
||||||
|
.shader_defs
|
||||||
|
.push("MESH_BINDGROUP_1".into());
|
||||||
|
|
||||||
descriptor.vertex.shader = self.shader.clone();
|
descriptor.vertex.shader = self.shader.clone();
|
||||||
descriptor.vertex.buffers.push(VertexBufferLayout {
|
descriptor.vertex.buffers.push(VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<InstanceData>() as u64,
|
array_stride: std::mem::size_of::<InstanceData>() as u64,
|
||||||
|
Loading…
Reference in New Issue
Block a user