mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
bevy_pbr2: Add support for not casting/receiving shadows (#2726)
# Objective Allow marking meshes as not casting / receiving shadows. ## Solution - Added `NotShadowCaster` and `NotShadowReceiver` zero-sized type components. - Extract these components into `bool`s in `ExtractedMesh` - Only generate `DrawShadowMesh` `Drawable`s for meshes _without_ `NotShadowCaster` - Add a `u32` bit `flags` member to `MeshUniform` with one flag indicating whether the mesh is a shadow receiver - If a mesh does _not_ have the `NotShadowReceiver` component, then it is a shadow receiver, and so the bit in the `MeshUniform` is set, otherwise it is not set. - Added an example illustrating the functionality. NOTE: I wanted to have the default state of a mesh as being a shadow caster and shadow receiver, hence the `Not*` components. However, I am on the fence about this. I don't want to have a negative performance impact, nor have people wondering why their custom meshes don't have shadows because they forgot to add `ShadowCaster` and `ShadowReceiver` components, but I also really don't like the double negatives the `Not*` approach incurs. What do you think? Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
f368bf7fc7
commit
f4aa3284a8
6 changed files with 286 additions and 35 deletions
|
@ -199,6 +199,10 @@ path = "examples/3d/render_to_texture.rs"
|
|||
name = "shadow_biases_pipelined"
|
||||
path = "examples/3d/shadow_biases_pipelined.rs"
|
||||
|
||||
[[example]]
|
||||
name = "shadow_caster_receiver_pipelined"
|
||||
path = "examples/3d/shadow_caster_receiver_pipelined.rs"
|
||||
|
||||
[[example]]
|
||||
name = "spawner"
|
||||
path = "examples/3d/spawner.rs"
|
||||
|
|
182
examples/3d/shadow_caster_receiver_pipelined.rs
Normal file
182
examples/3d/shadow_caster_receiver_pipelined.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
use bevy::{
|
||||
ecs::prelude::*,
|
||||
input::Input,
|
||||
math::{EulerRot, Mat4, Vec3},
|
||||
pbr2::{
|
||||
DirectionalLight, DirectionalLightBundle, NotShadowCaster, NotShadowReceiver, PbrBundle,
|
||||
PointLight, PointLightBundle, StandardMaterial,
|
||||
},
|
||||
prelude::{App, Assets, Handle, KeyCode, Transform},
|
||||
render2::{
|
||||
camera::{OrthographicProjection, PerspectiveCameraBundle},
|
||||
color::Color,
|
||||
mesh::{shape, Mesh},
|
||||
},
|
||||
PipelinedDefaultPlugins,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
println!(
|
||||
"Controls:
|
||||
C - toggle shadow casters (i.e. casters become not, and not casters become casters)
|
||||
R - toggle shadow receivers (i.e. receivers become not, and not receivers become receivers)
|
||||
L - switch between directional and point lights"
|
||||
);
|
||||
App::new()
|
||||
.add_plugins(PipelinedDefaultPlugins)
|
||||
.add_startup_system(setup)
|
||||
.add_system(toggle_light)
|
||||
.add_system(toggle_shadows)
|
||||
.run();
|
||||
}
|
||||
|
||||
/// set up a 3D scene to test shadow biases and perspective projections
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
let spawn_plane_depth = 500.0f32;
|
||||
let spawn_height = 2.0;
|
||||
let sphere_radius = 0.25;
|
||||
|
||||
let white_handle = materials.add(StandardMaterial {
|
||||
base_color: Color::WHITE,
|
||||
perceptual_roughness: 1.0,
|
||||
..Default::default()
|
||||
});
|
||||
let sphere_handle = meshes.add(Mesh::from(shape::Icosphere {
|
||||
radius: sphere_radius,
|
||||
..Default::default()
|
||||
}));
|
||||
|
||||
// sphere - initially a caster
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: sphere_handle.clone(),
|
||||
material: materials.add(Color::RED.into()),
|
||||
transform: Transform::from_xyz(-1.0, spawn_height, 0.0),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// sphere - initially not a caster
|
||||
commands
|
||||
.spawn_bundle(PbrBundle {
|
||||
mesh: sphere_handle,
|
||||
material: materials.add(Color::BLUE.into()),
|
||||
transform: Transform::from_xyz(1.0, spawn_height, 0.0),
|
||||
..Default::default()
|
||||
})
|
||||
.insert(NotShadowCaster);
|
||||
|
||||
// floating plane - initially not a shadow receiver and not a caster
|
||||
commands
|
||||
.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 20.0 })),
|
||||
material: materials.add(Color::GREEN.into()),
|
||||
transform: Transform::from_xyz(0.0, 1.0, -10.0),
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle((NotShadowCaster, NotShadowReceiver));
|
||||
|
||||
// lower ground plane - initially a shadow receiver
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 20.0 })),
|
||||
material: white_handle,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
println!("Using DirectionalLight");
|
||||
|
||||
commands.spawn_bundle(PointLightBundle {
|
||||
transform: Transform::from_xyz(5.0, 5.0, 0.0),
|
||||
point_light: PointLight {
|
||||
intensity: 0.0,
|
||||
range: spawn_plane_depth,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let theta = std::f32::consts::FRAC_PI_4;
|
||||
let light_transform = Mat4::from_euler(EulerRot::ZYX, 0.0, std::f32::consts::FRAC_PI_2, -theta);
|
||||
commands.spawn_bundle(DirectionalLightBundle {
|
||||
directional_light: DirectionalLight {
|
||||
illuminance: 100000.0,
|
||||
shadow_projection: OrthographicProjection {
|
||||
left: -10.0,
|
||||
right: 10.0,
|
||||
bottom: -10.0,
|
||||
top: 10.0,
|
||||
near: -50.0,
|
||||
far: 50.0,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
transform: Transform::from_matrix(light_transform),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// camera
|
||||
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||
transform: Transform::from_xyz(-5.0, 5.0, 5.0)
|
||||
.looking_at(Vec3::new(-1.0, 1.0, 0.0), Vec3::Y),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
fn toggle_light(
|
||||
input: Res<Input<KeyCode>>,
|
||||
mut point_lights: Query<&mut PointLight>,
|
||||
mut directional_lights: Query<&mut DirectionalLight>,
|
||||
) {
|
||||
if input.just_pressed(KeyCode::L) {
|
||||
for mut light in point_lights.iter_mut() {
|
||||
light.intensity = if light.intensity == 0.0 {
|
||||
println!("Using PointLight");
|
||||
100000000.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
}
|
||||
for mut light in directional_lights.iter_mut() {
|
||||
light.illuminance = if light.illuminance == 0.0 {
|
||||
println!("Using DirectionalLight");
|
||||
100000.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_shadows(
|
||||
mut commands: Commands,
|
||||
input: Res<Input<KeyCode>>,
|
||||
queries: QuerySet<(
|
||||
Query<Entity, (With<Handle<Mesh>>, With<NotShadowCaster>)>,
|
||||
Query<Entity, (With<Handle<Mesh>>, With<NotShadowReceiver>)>,
|
||||
Query<Entity, (With<Handle<Mesh>>, Without<NotShadowCaster>)>,
|
||||
Query<Entity, (With<Handle<Mesh>>, Without<NotShadowReceiver>)>,
|
||||
)>,
|
||||
) {
|
||||
if input.just_pressed(KeyCode::C) {
|
||||
println!("Toggling casters");
|
||||
for entity in queries.q0().iter() {
|
||||
commands.entity(entity).remove::<NotShadowCaster>();
|
||||
}
|
||||
for entity in queries.q2().iter() {
|
||||
commands.entity(entity).insert(NotShadowCaster);
|
||||
}
|
||||
}
|
||||
if input.just_pressed(KeyCode::R) {
|
||||
println!("Toggling receivers");
|
||||
for entity in queries.q1().iter() {
|
||||
commands.entity(entity).remove::<NotShadowReceiver>();
|
||||
}
|
||||
for entity in queries.q3().iter() {
|
||||
commands.entity(entity).insert(NotShadowReceiver);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,6 +107,7 @@ Example | File | Description
|
|||
`pbr` | [`3d/pbr.rs`](./3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
|
||||
`pbr_pipelined` | [`3d/pbr_pipelined.rs`](./3d/pbr_pipelined.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
|
||||
`render_to_texture` | [`3d/render_to_texture.rs`](./3d/render_to_texture.rs) | Shows how to render to texture
|
||||
`shadow_caster_receiver_pipelined` | [`3d/shadow_caster_receiver_pipelined.rs`](./3d/shadow_caster_receiver_pipelined.rs) | Demonstrates how to prevent meshes from casting/receiving shadows in a 3d scene
|
||||
`shadow_biases_pipelined` | [`3d/shadow_biases_pipelined.rs`](./3d/shadow_biases_pipelined.rs) | Demonstrates how shadow biases affect shadows in a 3d scene
|
||||
`spawner` | [`3d/spawner.rs`](./3d/spawner.rs) | Renders a large number of cubes with changing position and material
|
||||
`texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials
|
||||
|
|
|
@ -151,3 +151,8 @@ impl Default for AmbientLight {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add this component to make a `Mesh` not cast shadows
|
||||
pub struct NotShadowCaster;
|
||||
/// Add this component to make a `Mesh` not receive shadows
|
||||
pub struct NotShadowReceiver;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
mod light;
|
||||
pub use light::*;
|
||||
|
||||
use crate::{StandardMaterial, StandardMaterialUniformData};
|
||||
use crate::{NotShadowCaster, NotShadowReceiver, StandardMaterial, StandardMaterialUniformData};
|
||||
use bevy_asset::{Assets, Handle};
|
||||
use bevy_core_pipeline::Transparent3dPhase;
|
||||
use bevy_ecs::{prelude::*, system::SystemState};
|
||||
|
@ -120,11 +120,11 @@ impl FromWorld for PbrShaders {
|
|||
let mesh_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStage::VERTEX,
|
||||
visibility: ShaderStage::VERTEX | ShaderStage::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(Mat4::std140_size_static() as u64),
|
||||
min_binding_size: BufferSize::new(80),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
@ -374,6 +374,8 @@ struct ExtractedMesh {
|
|||
mesh: Handle<Mesh>,
|
||||
transform_binding_offset: u32,
|
||||
material_handle: Handle<StandardMaterial>,
|
||||
casts_shadows: bool,
|
||||
receives_shadows: bool,
|
||||
}
|
||||
|
||||
pub struct ExtractedMeshes {
|
||||
|
@ -385,10 +387,23 @@ pub fn extract_meshes(
|
|||
meshes: Res<Assets<Mesh>>,
|
||||
materials: Res<Assets<StandardMaterial>>,
|
||||
images: Res<Assets<Image>>,
|
||||
query: Query<(&GlobalTransform, &Handle<Mesh>, &Handle<StandardMaterial>)>,
|
||||
query: Query<(
|
||||
&GlobalTransform,
|
||||
&Handle<Mesh>,
|
||||
&Handle<StandardMaterial>,
|
||||
Option<&NotShadowCaster>,
|
||||
Option<&NotShadowReceiver>,
|
||||
)>,
|
||||
) {
|
||||
let mut extracted_meshes = Vec::new();
|
||||
for (transform, mesh_handle, material_handle) in query.iter() {
|
||||
for (
|
||||
transform,
|
||||
mesh_handle,
|
||||
material_handle,
|
||||
maybe_not_shadow_caster,
|
||||
maybe_not_shadow_receiver,
|
||||
) in query.iter()
|
||||
{
|
||||
if !meshes.contains(mesh_handle) {
|
||||
continue;
|
||||
}
|
||||
|
@ -419,6 +434,10 @@ pub fn extract_meshes(
|
|||
mesh: mesh_handle.clone_weak(),
|
||||
transform_binding_offset: 0,
|
||||
material_handle: material_handle.clone_weak(),
|
||||
// NOTE: Double-negative is so that meshes cast and receive shadows by default
|
||||
// Not not shadow caster means that this mesh is a shadow caster
|
||||
casts_shadows: maybe_not_shadow_caster.is_none(),
|
||||
receives_shadows: maybe_not_shadow_receiver.is_none(),
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
|
@ -435,9 +454,25 @@ struct MeshDrawInfo {
|
|||
material_bind_group_key: FrameSlabMapKey<BufferId, BindGroup>,
|
||||
}
|
||||
|
||||
#[derive(Debug, AsStd140)]
|
||||
pub struct MeshUniform {
|
||||
model: Mat4,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.wgsl!
|
||||
bitflags::bitflags! {
|
||||
#[repr(transparent)]
|
||||
struct MeshFlags: u32 {
|
||||
const SHADOW_RECEIVER = (1 << 0);
|
||||
const NONE = 0;
|
||||
const UNINITIALIZED = 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MeshMeta {
|
||||
transform_uniforms: DynamicUniformVec<Mat4>,
|
||||
transform_uniforms: DynamicUniformVec<MeshUniform>,
|
||||
material_bind_groups: FrameSlabMap<BufferId, BindGroup>,
|
||||
mesh_transform_bind_group: FrameSlabMap<BufferId, BindGroup>,
|
||||
mesh_transform_bind_group_key: Option<FrameSlabMapKey<BufferId, BindGroup>>,
|
||||
|
@ -453,8 +488,15 @@ pub fn prepare_meshes(
|
|||
.transform_uniforms
|
||||
.reserve_and_clear(extracted_meshes.meshes.len(), &render_device);
|
||||
for extracted_mesh in extracted_meshes.meshes.iter_mut() {
|
||||
extracted_mesh.transform_binding_offset =
|
||||
mesh_meta.transform_uniforms.push(extracted_mesh.transform);
|
||||
let flags = if extracted_mesh.receives_shadows {
|
||||
MeshFlags::SHADOW_RECEIVER
|
||||
} else {
|
||||
MeshFlags::NONE
|
||||
};
|
||||
extracted_mesh.transform_binding_offset = mesh_meta.transform_uniforms.push(MeshUniform {
|
||||
model: extracted_mesh.transform,
|
||||
flags: flags.bits,
|
||||
});
|
||||
}
|
||||
|
||||
mesh_meta
|
||||
|
@ -694,12 +736,14 @@ pub fn queue_meshes(
|
|||
for view_light_entity in view_lights.lights.iter().copied() {
|
||||
let mut shadow_phase = view_light_shadow_phases.get_mut(view_light_entity).unwrap();
|
||||
// TODO: this should only queue up meshes that are actually visible by each "light view"
|
||||
for i in 0..extracted_meshes.meshes.len() {
|
||||
shadow_phase.add(Drawable {
|
||||
draw_function: draw_shadow_mesh,
|
||||
draw_key: i,
|
||||
sort_key: 0, // TODO: sort back-to-front
|
||||
})
|
||||
for (i, mesh) in extracted_meshes.meshes.iter().enumerate() {
|
||||
if mesh.casts_shadows {
|
||||
shadow_phase.add(Drawable {
|
||||
draw_function: draw_shadow_mesh,
|
||||
draw_key: i,
|
||||
sort_key: 0, // TODO: sort back-to-front
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,19 @@ struct View {
|
|||
view_proj: mat4x4<f32>;
|
||||
world_position: vec3<f32>;
|
||||
};
|
||||
[[group(0), binding(0)]]
|
||||
var view: View;
|
||||
|
||||
|
||||
[[block]]
|
||||
struct Mesh {
|
||||
transform: mat4x4<f32>;
|
||||
model: mat4x4<f32>;
|
||||
// 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options.
|
||||
flags: u32;
|
||||
};
|
||||
|
||||
let MESH_FLAGS_SHADOW_RECEIVER_BIT: u32 = 1u;
|
||||
|
||||
[[group(0), binding(0)]]
|
||||
var view: View;
|
||||
[[group(1), binding(0)]]
|
||||
var mesh: Mesh;
|
||||
|
||||
|
@ -30,7 +35,7 @@ struct VertexOutput {
|
|||
|
||||
[[stage(vertex)]]
|
||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
let world_position = mesh.transform * vec4<f32>(vertex.position, 1.0);
|
||||
let world_position = mesh.model * vec4<f32>(vertex.position, 1.0);
|
||||
|
||||
var out: VertexOutput;
|
||||
out.uv = vertex.uv;
|
||||
|
@ -38,7 +43,7 @@ fn vertex(vertex: Vertex) -> VertexOutput {
|
|||
out.clip_position = view.view_proj * world_position;
|
||||
// FIXME: The inverse transpose of the model matrix should be used to correctly handle scaling
|
||||
// of normals
|
||||
out.world_normal = mat3x3<f32>(mesh.transform.x.xyz, mesh.transform.y.xyz, mesh.transform.z.xyz) * vertex.normal;
|
||||
out.world_normal = mat3x3<f32>(mesh.model.x.xyz, mesh.model.y.xyz, mesh.model.z.xyz) * vertex.normal;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -83,10 +88,17 @@ struct StandardMaterial {
|
|||
perceptual_roughness: f32;
|
||||
metallic: f32;
|
||||
reflectance: f32;
|
||||
// 'flags' is a bit field indicating various option. uint is 32 bits so we have up to 32 options.
|
||||
// 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options.
|
||||
flags: u32;
|
||||
};
|
||||
|
||||
let STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT: u32 = 1u;
|
||||
let STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT: u32 = 2u;
|
||||
let STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT: u32 = 4u;
|
||||
let STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT: u32 = 8u;
|
||||
let STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT: u32 = 16u;
|
||||
let STANDARD_MATERIAL_FLAGS_UNLIT_BIT: u32 = 32u;
|
||||
|
||||
struct PointLight {
|
||||
color: vec4<f32>;
|
||||
// projection: mat4x4<f32>;
|
||||
|
@ -118,13 +130,6 @@ struct Lights {
|
|||
n_directional_lights: u32;
|
||||
};
|
||||
|
||||
let FLAGS_BASE_COLOR_TEXTURE_BIT: u32 = 1u;
|
||||
let FLAGS_EMISSIVE_TEXTURE_BIT: u32 = 2u;
|
||||
let FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT: u32 = 4u;
|
||||
let FLAGS_OCCLUSION_TEXTURE_BIT: u32 = 8u;
|
||||
let FLAGS_DOUBLE_SIDED_BIT: u32 = 16u;
|
||||
let FLAGS_UNLIT_BIT: u32 = 32u;
|
||||
|
||||
|
||||
[[group(0), binding(1)]]
|
||||
var lights: Lights;
|
||||
|
@ -463,22 +468,22 @@ struct FragmentInput {
|
|||
[[stage(fragment)]]
|
||||
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
||||
var output_color: vec4<f32> = material.base_color;
|
||||
if ((material.flags & FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
||||
output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv);
|
||||
}
|
||||
|
||||
// // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
|
||||
if ((material.flags & FLAGS_UNLIT_BIT) == 0u) {
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) {
|
||||
// TODO use .a for exposure compensation in HDR
|
||||
var emissive: vec4<f32> = material.emissive;
|
||||
if ((material.flags & FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
||||
emissive = vec4<f32>(emissive.rgb * textureSample(emissive_texture, emissive_sampler, in.uv).rgb, 1.0);
|
||||
}
|
||||
|
||||
// calculate non-linear roughness from linear perceptualRoughness
|
||||
var metallic: f32 = material.metallic;
|
||||
var perceptual_roughness: f32 = material.perceptual_roughness;
|
||||
if ((material.flags & FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
||||
let metallic_roughness = textureSample(metallic_roughness_texture, metallic_roughness_sampler, in.uv);
|
||||
// Sampling from GLTF standard channels for now
|
||||
metallic = metallic * metallic_roughness.b;
|
||||
|
@ -487,7 +492,7 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
|||
let roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
||||
|
||||
var occlusion: f32 = 1.0;
|
||||
if ((material.flags & FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
||||
occlusion = textureSample(occlusion_texture, occlusion_sampler, in.uv).r;
|
||||
}
|
||||
|
||||
|
@ -500,7 +505,7 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
|||
// vec3 B = cross(N, T) * v_WorldTangent.w;
|
||||
// # endif
|
||||
|
||||
if ((material.flags & FLAGS_DOUBLE_SIDED_BIT) != 0u) {
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u) {
|
||||
if (!in.is_front) {
|
||||
N = -N;
|
||||
}
|
||||
|
@ -543,13 +548,23 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
|||
let n_directional_lights = i32(lights.n_directional_lights);
|
||||
for (var i: i32 = 0; i < n_point_lights; i = i + 1) {
|
||||
let light = lights.point_lights[i];
|
||||
let shadow = fetch_point_shadow(i, in.world_position, in.world_normal);
|
||||
var shadow: f32;
|
||||
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u) {
|
||||
shadow = fetch_point_shadow(i, in.world_position, in.world_normal);
|
||||
} else {
|
||||
shadow = 1.0;
|
||||
}
|
||||
let light_contrib = point_light(in.world_position.xyz, light, roughness, NdotV, N, V, R, F0, diffuse_color);
|
||||
light_accum = light_accum + light_contrib * shadow;
|
||||
}
|
||||
for (var i: i32 = 0; i < n_directional_lights; i = i + 1) {
|
||||
let light = lights.directional_lights[i];
|
||||
let shadow = fetch_directional_shadow(i, in.world_position, in.world_normal);
|
||||
var shadow: f32;
|
||||
if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u) {
|
||||
shadow = fetch_directional_shadow(i, in.world_position, in.world_normal);
|
||||
} else {
|
||||
shadow = 1.0;
|
||||
}
|
||||
let light_contrib = directional_light(light, roughness, NdotV, N, V, R, F0, diffuse_color);
|
||||
light_accum = light_accum + light_contrib * shadow;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue