use crate::{
core::{
algebra::{Matrix4, Point3, Vector3},
color::Color,
math::Rect,
},
renderer::{
bundle::{
BundleRenderContext, ObserverInfo, RenderDataBundleStorage,
RenderDataBundleStorageOptions,
},
cache::{shader::ShaderCache, texture::TextureCache, uniform::UniformMemoryAllocator},
framework::{
error::FrameworkError,
framebuffer::{Attachment, AttachmentKind, FrameBuffer},
gpu_texture::{
CubeMapFace, GpuTexture, GpuTextureDescriptor, GpuTextureKind, MagnificationFilter,
MinificationFilter, PixelKind, WrapMode,
},
server::GraphicsServer,
},
shadow::cascade_size,
FallbackResources, GeometryCache, RenderPassStatistics, ShadowMapPrecision,
POINT_SHADOW_PASS_NAME,
},
scene::graph::Graph,
};
use std::{cell::RefCell, rc::Rc};
pub struct PointShadowMapRenderer {
precision: ShadowMapPrecision,
cascades: [Box<dyn FrameBuffer>; 3],
size: usize,
faces: [PointShadowCubeMapFace; 6],
}
struct PointShadowCubeMapFace {
face: CubeMapFace,
look: Vector3<f32>,
up: Vector3<f32>,
}
pub(crate) struct PointShadowMapRenderContext<'a> {
pub elapsed_time: f32,
pub state: &'a dyn GraphicsServer,
pub graph: &'a Graph,
pub light_pos: Vector3<f32>,
pub light_radius: f32,
pub geom_cache: &'a mut GeometryCache,
pub cascade: usize,
pub shader_cache: &'a mut ShaderCache,
pub texture_cache: &'a mut TextureCache,
pub fallback_resources: &'a FallbackResources,
pub uniform_memory_allocator: &'a mut UniformMemoryAllocator,
}
impl PointShadowMapRenderer {
pub fn new(
server: &dyn GraphicsServer,
size: usize,
precision: ShadowMapPrecision,
) -> Result<Self, FrameworkError> {
fn make_cascade(
server: &dyn GraphicsServer,
size: usize,
precision: ShadowMapPrecision,
) -> Result<Box<dyn FrameBuffer>, FrameworkError> {
let depth = server.create_2d_render_target(
match precision {
ShadowMapPrecision::Full => PixelKind::D32F,
ShadowMapPrecision::Half => PixelKind::D16,
},
size,
size,
)?;
let cube_map = server.create_texture(GpuTextureDescriptor {
kind: GpuTextureKind::Cube {
width: size,
height: size,
},
pixel_kind: PixelKind::R16F,
min_filter: MinificationFilter::Nearest,
mag_filter: MagnificationFilter::Nearest,
s_wrap_mode: WrapMode::ClampToEdge,
t_wrap_mode: WrapMode::ClampToEdge,
r_wrap_mode: WrapMode::ClampToEdge,
..Default::default()
})?;
server.create_frame_buffer(
Some(Attachment {
kind: AttachmentKind::Depth,
texture: depth,
}),
vec![Attachment {
kind: AttachmentKind::Color,
texture: cube_map,
}],
)
}
Ok(Self {
precision,
cascades: [
make_cascade(server, cascade_size(size, 0), precision)?,
make_cascade(server, cascade_size(size, 1), precision)?,
make_cascade(server, cascade_size(size, 2), precision)?,
],
size,
faces: [
PointShadowCubeMapFace {
face: CubeMapFace::PositiveX,
look: Vector3::new(1.0, 0.0, 0.0),
up: Vector3::new(0.0, -1.0, 0.0),
},
PointShadowCubeMapFace {
face: CubeMapFace::NegativeX,
look: Vector3::new(-1.0, 0.0, 0.0),
up: Vector3::new(0.0, -1.0, 0.0),
},
PointShadowCubeMapFace {
face: CubeMapFace::PositiveY,
look: Vector3::new(0.0, 1.0, 0.0),
up: Vector3::new(0.0, 0.0, 1.0),
},
PointShadowCubeMapFace {
face: CubeMapFace::NegativeY,
look: Vector3::new(0.0, -1.0, 0.0),
up: Vector3::new(0.0, 0.0, -1.0),
},
PointShadowCubeMapFace {
face: CubeMapFace::PositiveZ,
look: Vector3::new(0.0, 0.0, 1.0),
up: Vector3::new(0.0, -1.0, 0.0),
},
PointShadowCubeMapFace {
face: CubeMapFace::NegativeZ,
look: Vector3::new(0.0, 0.0, -1.0),
up: Vector3::new(0.0, -1.0, 0.0),
},
],
})
}
pub fn base_size(&self) -> usize {
self.size
}
pub fn precision(&self) -> ShadowMapPrecision {
self.precision
}
pub fn cascade_texture(&self, cascade: usize) -> Rc<RefCell<dyn GpuTexture>> {
self.cascades[cascade].color_attachments()[0]
.texture
.clone()
}
pub(crate) fn render(
&mut self,
args: PointShadowMapRenderContext,
) -> Result<RenderPassStatistics, FrameworkError> {
let mut statistics = RenderPassStatistics::default();
let PointShadowMapRenderContext {
elapsed_time,
state,
graph,
light_pos,
light_radius,
geom_cache,
cascade,
shader_cache,
texture_cache,
fallback_resources,
uniform_memory_allocator,
} = args;
let framebuffer = &mut *self.cascades[cascade];
let cascade_size = cascade_size(self.size, cascade);
let viewport = Rect::new(0, 0, cascade_size as i32, cascade_size as i32);
let z_near = 0.01;
let z_far = light_radius;
let light_projection_matrix =
Matrix4::new_perspective(1.0, std::f32::consts::FRAC_PI_2, z_near, z_far);
for face in self.faces.iter() {
framebuffer.set_cubemap_face(0, face.face);
framebuffer.clear(viewport, Some(Color::WHITE), Some(1.0), None);
let light_look_at = light_pos + face.look;
let light_view_matrix = Matrix4::look_at_rh(
&Point3::from(light_pos),
&Point3::from(light_look_at),
&face.up,
);
let bundle_storage = RenderDataBundleStorage::from_graph(
graph,
elapsed_time,
ObserverInfo {
observer_position: light_pos,
z_near,
z_far,
view_matrix: light_view_matrix,
projection_matrix: light_projection_matrix,
},
POINT_SHADOW_PASS_NAME.clone(),
RenderDataBundleStorageOptions {
collect_lights: false,
},
);
statistics += bundle_storage.render_to_frame_buffer(
state,
geom_cache,
shader_cache,
|_| true,
|_| true,
BundleRenderContext {
texture_cache,
render_pass_name: &POINT_SHADOW_PASS_NAME,
frame_buffer: framebuffer,
viewport,
uniform_memory_allocator,
use_pom: false,
light_position: &light_pos,
fallback_resources,
ambient_light: Color::WHITE, scene_depth: None,
},
)?;
}
Ok(statistics)
}
}