Skip to content

Commit

Permalink
Some thoughts for a 2d lighting pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkleiny committed Jul 14, 2024
1 parent d90fe7d commit 4b9aa9e
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 189 deletions.
74 changes: 58 additions & 16 deletions crates/graphics/src/lighting.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,72 @@
use common::{Camera, Color, Vec2};
use common::{vec3, Camera, Color, Vec2, Vec3, AABB};

use crate::{RenderFrame, RenderPass, RenderScene, RenderTarget};
use crate::{
CullableObject, ObjectBounds, RenderFrame, RenderPass, RenderScene, RenderTarget, RenderTargetDescriptor,
RenderTextureDescriptor, TargetError, TextureFormat, TextureOptions,
};

/// A point light in 2 space.
pub struct PointLight {
pub position: Vec2,
pub color: Color,
pub intensity: f32,
pub radius: f32,
/// A light in 2 space.
pub enum Light {
PointLight {
position: Vec2,
color: Color,
intensity: f32,
radius: f32,
},
}

/// A render pass that renders all lights in the scene.
pub struct LightPass {
pub lights: Vec<PointLight>,
cascade_1: RenderTarget,
cascade_2: RenderTarget,
cascade_3: RenderTarget,
cascade_4: RenderTarget,
}

impl LightPass {
/// Creates a new light pass.
pub fn new() -> Result<Self, TargetError> {
let base_descriptor = RenderTargetDescriptor {
color_attachment: RenderTextureDescriptor {
width: 1920,
height: 1080,
options: TextureOptions {
format: TextureFormat::RGBA8,
..Default::default()
},
},
depth_attachment: None,
stencil_attachment: None,
};

lighting_cascade_1: RenderTarget,
lighting_cascade_2: RenderTarget,
lighting_cascade_3: RenderTarget,
Ok(Self {
cascade_1: RenderTarget::new(&base_descriptor.with_size(1024, 1024))?,
cascade_2: RenderTarget::new(&base_descriptor.with_size(512, 512))?,
cascade_3: RenderTarget::new(&base_descriptor.with_size(256, 256))?,
cascade_4: RenderTarget::new(&base_descriptor.with_size(128, 128))?,
})
}
}

impl RenderPass for LightPass {
fn begin_frame(&self, scene: &dyn RenderScene, frame: &mut RenderFrame<'_>) {
todo!()
fn render_camera(&self, scene: &dyn RenderScene, camera: &dyn Camera, frame: &mut RenderFrame<'_>) {
let frustum = camera.frustum();

// TODO: render lights
}
}

fn render_camera(&self, scene: &dyn RenderScene, camera: &dyn Camera, frame: &mut RenderFrame<'_>) {
todo!()
impl CullableObject for Light {
fn compute_bounds(&self) -> ObjectBounds {
match self {
Light::PointLight { position, radius, .. } => {
let position = vec3(position.x, 0.0, position.y);

let min = position - Vec3::splat(*radius);
let max = position + Vec3::splat(*radius);

ObjectBounds::AABB(AABB::from_min_max(min, max))
}
}
}
}
2 changes: 0 additions & 2 deletions crates/graphics/src/rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
//! complex render pipelines than using the 'material', 'mesh', 'render targets'
//! etc. do alone.
pub use contexts::*;
pub use culling::*;
pub use pipelines::*;
pub use queue::*;

use super::*;

mod contexts;
mod culling;
mod pipelines;
mod queue;
84 changes: 0 additions & 84 deletions crates/graphics/src/rendering/contexts.rs

This file was deleted.

99 changes: 40 additions & 59 deletions crates/graphics/src/rendering/culling.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
use common::{Camera, Sphere, AABB};

use super::*;

/// Possible bounds of an object.
pub enum ObjectBounds {
AABB(AABB),
Sphere(Sphere),
}

/// Represents an object that can be culled from the scene.
pub trait CullableObject {
/// Gets the bounds of the object.
fn compute_bounds(&self) -> ObjectBounds;
}

/// Represents a scene that can be culled.
pub trait CullableScene {
/// Culls the objects that are visible to the given camera.
fn cull_visible_objects<T>(&self, camera: &dyn Camera) -> VisibleObjectSet<T>;
}

/// A set of visible objects that can be rendered in a scene.
///
/// This is a subset of the objects in a scene that are visible to a specific
/// camera, and can be used to optimize rendering by only rendering the objects
/// that are visible to the camera.
pub struct VisibleObjectSet<'a, T> {
/// The objects that are visible to the camera.
objects: Vec<&'a T>,
}

impl<'a, T: 'static> VisibleObjectSet<'a, T> {
/// An empty set of objects.
pub const EMPTY: VisibleObjectSet<'static, T> = VisibleObjectSet { objects: Vec::new() };

/// Creates a new set of visible objects from the given objects.
pub fn new(objects: Vec<&'a T>) -> Self {
Self { objects }
}
}

/// A key that can be used to uniquely identify the kind of material.
///
/// This is used to sort materials into batches for efficient rendering,
Expand Down Expand Up @@ -40,62 +80,3 @@ impl MaterialSortingKey {
Self(flags << 32 | shader)
}
}

/// A set of visible objects that can be rendered in a scene.
///
/// This is a subset of the objects in a scene that are visible to a specific
/// camera, and can be used to optimize rendering by only rendering the objects
/// that are visible to the camera.
pub struct VisibleObjectSet<'a> {
/// The objects that are visible to the camera.
objects: Vec<VisibleObject<'a>>,
}

/// Represents an object that is visible to a camera.
///
/// This is a reference to an object in a scene, along with the material that
/// should be used to render it.
///
/// This is used to sort objects into batches for efficient rendering,
/// minimizing state changes between draw calls.
pub struct VisibleObject<'a> {
/// The object itself.
pub object: &'a dyn RenderObject,
/// The material of the object.
pub material: &'a Material,
}

impl<'a> VisibleObjectSet<'a> {
/// An empty set of objects.
pub const EMPTY: VisibleObjectSet<'static> = VisibleObjectSet { objects: Vec::new() };

/// Creates a new set of visible objects from the given objects.
pub fn new(mut objects: Vec<VisibleObject<'a>>) -> Self {
objects.sort_by(|a, b| {
let a = MaterialSortingKey::for_material(a.material);
let b = MaterialSortingKey::for_material(b.material);

a.cmp(&b)
});

Self { objects }
}

/// Groups the objects by material sorting key.
///
/// The flags parameter can be used to filter the materials that are included
/// in the result. Only materials that have all the specified flags will be
/// included in the result.
pub fn group_by_material(&self, flags: MaterialFlags) -> impl Iterator<Item = (&'a Material, &[VisibleObject<'a>])> {
self
.objects
.chunk_by(|a, b| {
let a = MaterialSortingKey::for_material(a.material);
let b = MaterialSortingKey::for_material(b.material);

a == b
})
.filter(move |it| flags.contains(it[0].material.flags()))
.map(|it| (it[0].material, it))
}
}
32 changes: 4 additions & 28 deletions crates/graphics/src/rendering/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,12 @@ use super::*;
pub struct RenderFrame<'a> {
pub delta_time: f32,
pub queue: &'a mut RenderQueue,
pub renderer: &'a mut Renderer,
}

impl<'a> RenderFrame<'a> {
/// Draws the object visible to the given camera.
pub fn draw_camera(&mut self, scene: &dyn RenderScene, camera: &dyn Camera) {
// find all objects visible to the camera
let visible_objects = scene.cull_visible_objects(camera);

// render each object, minimizing state changes by material
for (material, group) in visible_objects.group_by_material(MaterialFlags::all()) {
self.queue.set_material(material);

for entry in group {
entry.object.render(self);
}
}
}
}

/// Represents a scene that can be rendered by a [`RenderPipeline`].
pub trait RenderScene {
/// Gets the cameras that should be used to render this scene.
fn cameras(&self) -> Vec<&dyn Camera>;

/// Gets the objects that should be rendered by the given camera.
fn cull_visible_objects(&self, camera: &dyn Camera) -> VisibleObjectSet;
}

/// Represents an object capable of being rendered.
Expand Down Expand Up @@ -65,7 +44,6 @@ pub trait RenderPass {

/// A [`RenderPipeline`] that executes many [`RenderPass`]es in order.
pub struct MultiPassPipeline {
renderer: Renderer,
queue: RenderQueue,
passes: Vec<Box<dyn RenderPass>>,
}
Expand All @@ -74,7 +52,6 @@ impl MultiPassPipeline {
/// Creates a new [`MultiPassPipeline`] with the given passes.
pub fn new() -> Self {
Self {
renderer: Renderer::new(),
queue: RenderQueue::default(),
passes: Vec::default(),
}
Expand All @@ -95,7 +72,6 @@ impl RenderPipeline for MultiPassPipeline {
let mut frame = RenderFrame {
delta_time,
queue: &mut self.queue,
renderer: &mut self.renderer,
};

// begin the frame
Expand Down Expand Up @@ -136,9 +112,9 @@ pub mod forward {
struct DepthPass {}

impl RenderPass for DepthPass {
fn render_camera(&self, scene: &dyn RenderScene, camera: &dyn Camera, frame: &mut RenderFrame<'_>) {
fn render_camera(&self, _scene: &dyn RenderScene, _camera: &dyn Camera, frame: &mut RenderFrame<'_>) {
frame.queue.clear_color_buffer(Color::BLACK);
frame.draw_camera(scene, camera);
// TODO: render depth
}
}

Expand All @@ -152,9 +128,9 @@ pub mod forward {
frame.queue.set_render_target(&self.color_target);
}

fn render_camera(&self, scene: &dyn RenderScene, camera: &dyn Camera, frame: &mut RenderFrame<'_>) {
fn render_camera(&self, _scene: &dyn RenderScene, _camera: &dyn Camera, frame: &mut RenderFrame<'_>) {
frame.queue.clear_color_buffer(Color::BLACK);
frame.draw_camera(scene, camera);
// TODO: render color
}

fn end_frame(&self, _scene: &dyn RenderScene, frame: &mut RenderFrame<'_>) {
Expand Down
Loading

0 comments on commit 4b9aa9e

Please sign in to comment.