Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Point light API #535

Merged
merged 10 commits into from
Dec 12, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Per Keep a Changelog there are 6 main categories of changes:
- rend3-routine: Added the option to set a custom primitive topology value when building a forward routine. @setzer22
- rend3-routine: Added a resolution field to the per-frame uniforms. @setzer22
- rend3-routine: Added add_clear_to_graph to make clears explicit and add `clear_color` argument to base rendergraph.
- rend3: Added basic (no shadow maps, no clustering) point light support to the renderer API. @marceline-cramer

### Changes
- rend3: Update to wgpu 0.13, naga 0.9 @garyttierney
Expand Down
21 changes: 19 additions & 2 deletions examples/cube/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const SAMPLE_COUNT: rend3::types::SampleCount = rend3::types::SampleCount::One;
struct CubeExample {
object_handle: Option<rend3::types::ObjectHandle>,
directional_light_handle: Option<rend3::types::DirectionalLightHandle>,
point_lights: Vec<rend3::types::PointLightHandle>,
}

impl rend3_framework::App for CubeExample {
Expand Down Expand Up @@ -87,7 +88,7 @@ impl rend3_framework::App for CubeExample {

// Add PBR material with all defaults except a single color.
let material = rend3_routine::pbr::PbrMaterial {
albedo: rend3_routine::pbr::AlbedoComponent::Value(glam::Vec4::new(0.0, 0.5, 0.5, 1.0)),
albedo: rend3_routine::pbr::AlbedoComponent::Value(glam::Vec4::new(0.5, 0.5, 0.5, 1.0)),
..rend3_routine::pbr::PbrMaterial::default()
};
let material_handle = renderer.add_material(material);
Expand Down Expand Up @@ -119,12 +120,28 @@ impl rend3_framework::App for CubeExample {
// We need to keep the directional light handle alive.
self.directional_light_handle = Some(renderer.add_directional_light(rend3::types::DirectionalLight {
color: glam::Vec3::ONE,
intensity: 10.0,
intensity: 1.0,
// Direction will be normalized
direction: glam::Vec3::new(-1.0, -4.0, 2.0),
distance: 400.0,
resolution: 2048,
}));

let lights = [
// position, color
(glam::vec3(0.1, 1.2, -1.5), glam::vec3(1.0, 0.0, 0.0)),
(glam::vec3(1.5, 1.2, -0.1), glam::vec3(0.0, 1.0, 0.0)),
];

for (position, color) in lights {
self.point_lights
.push(renderer.add_point_light(rend3::types::PointLight {
position,
color,
radius: 2.0,
intensity: 4.0,
}));
}
}

fn handle_event(
Expand Down
2 changes: 0 additions & 2 deletions rend3-routine/shaders/src/depth.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
var primary_sampler: sampler;
@group(0) @binding(3)
var<uniform> uniforms: UniformData;
@group(0) @binding(4)
var<storage> directional_lights: DirectionalLightData;

@group(1) @binding(0)
var<storage> object_buffer: array<Object>;
Expand Down
48 changes: 39 additions & 9 deletions rend3-routine/shaders/src/opaque.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ var<uniform> uniforms: UniformData;
@group(0) @binding(4)
var<storage> directional_lights: DirectionalLightData;
@group(0) @binding(5)
var<storage> point_lights: PointLightData;
@group(0) @binding(6)
var shadows: texture_depth_2d;

@group(1) @binding(0)
Expand Down Expand Up @@ -435,17 +437,14 @@ fn get_pixel_data(material: Material, vs_out: VertexOutput) -> PixelData {
}
{{/if}}

fn surface_shading(light: DirectionalLight, pixel: PixelData, view_pos: vec3<f32>, occlusion: f32) -> vec3<f32> {
let view_mat3 = mat3x3<f32>(uniforms.view[0].xyz, uniforms.view[1].xyz, uniforms.view[2].xyz);
let l = normalize(view_mat3 * -light.direction);

fn surface_shading(light_dir: vec3<f32>, intensity: vec3<f32>, pixel: PixelData, view_pos: vec3<f32>, occlusion: f32) -> vec3<f32> {
let n = pixel.normal;
let h = normalize(view_pos + l);
let h = normalize(view_pos + light_dir);

let nov = abs(dot(n, view_pos)) + 0.00001;
let nol = saturate(dot(n, l));
let nol = saturate(dot(n, light_dir));
let noh = saturate(dot(n, h));
let loh = saturate(dot(l, h));
let loh = saturate(dot(light_dir, h));

let f90 = saturate(dot(pixel.f0, vec3<f32>(50.0 * 0.33)));

Expand All @@ -465,7 +464,7 @@ fn surface_shading(light: DirectionalLight, pixel: PixelData, view_pos: vec3<f32

let light_attenuation = 1.0;

return (color * light.color) * (light_attenuation * nol * occlusion);
return (color * intensity) * (light_attenuation * nol * occlusion);
}

@fragment
Expand All @@ -478,8 +477,12 @@ fn fs_main(vs_out: VertexOutput) -> @location(0) vec4<f32> {
return pixel.albedo;
}

// View vector
let v = -normalize(vs_out.view_position.xyz);

// Transform vectors into view space
let view_mat3 = mat3x3<f32>(uniforms.view[0].xyz, uniforms.view[1].xyz, uniforms.view[2].xyz);

var color = pixel.emissive.rgb;
for (var i = 0; i < i32(directional_lights.count); i += 1) {
let light = directional_lights.data[i];
Expand Down Expand Up @@ -512,7 +515,34 @@ fn fs_main(vs_out: VertexOutput) -> @location(0) vec4<f32> {
shadow_value = shadow_sample_pcf5(shadows, comparison_sampler, shadow_coords, shadow_ndc.z);
}

color += surface_shading(light, pixel, v, shadow_value * pixel.ambient_occlusion);
// Calculate light source vector
let l = normalize(view_mat3 * -light.direction);

color += surface_shading(l, light.color, pixel, v, shadow_value * pixel.ambient_occlusion);
}

for (var i = 0; i < i32(point_lights.count); i += 1) {
let light = point_lights.data[i];

// Delta to light
let delta = (uniforms.view * light.position).xyz - vs_out.view_position.xyz;

// Distance
let d = length(delta);

// Attenuate from light and cusp at radius
// Derivative is 0 at both d = 0 and d = radius
// Source: https://lisyarus.github.io/blog/graphics/2022/07/30/point-light-attenuation.html
let s = saturate(d / light.radius);
let s2 = s * s;
let inv_s2 = 1.0 - s2;
let att = inv_s2 * inv_s2 / (1.0 + s2);
let intensity = light.color * att;

// Calculate light source vector
let l = delta / d;

color += max(surface_shading(l, intensity, pixel, v, pixel.ambient_occlusion), vec3<f32>(0.0));
}

let ambient = uniforms.ambient * pixel.albedo;
Expand Down
16 changes: 15 additions & 1 deletion rend3-routine/shaders/src/structures.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@ struct DirectionalLightData {
data: array<DirectionalLight>,
}

struct PointLight {
/// The position of the light in world space.
position: vec4<f32>,
// Color/intensity of the light.
color: vec3<f32>,
/// The radius of the light.
radius: f32,
}

struct PointLightData {
count: u32,
data: array<PointLight>,
}

struct PixelData {
albedo: vec4<f32>,
diffuse_color: vec3<f32>,
Expand All @@ -108,4 +122,4 @@ struct PixelData {
anisotropy: f32,
ambient_occlusion: f32,
material_flags: u32,
}
}
7 changes: 6 additions & 1 deletion rend3-routine/src/common/interfaces.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::{marker::PhantomData, mem, num::NonZeroU64};

use glam::{Mat4, Vec3};
use rend3::{managers::DirectionalLightManager, types::Material, util::bind_merge::BindGroupLayoutBuilder};
use rend3::{
managers::{DirectionalLightManager, PointLightManager},
types::Material,
util::bind_merge::BindGroupLayoutBuilder,
};
use wgpu::{
BindGroupLayout, BindingType, BufferBindingType, Device, ShaderStages, TextureSampleType, TextureViewDimension,
};
Expand Down Expand Up @@ -39,6 +43,7 @@ impl WholeFrameInterfaces {
);

DirectionalLightManager::add_to_bgl(&mut uniform_bglb);
PointLightManager::add_to_bgl(&mut uniform_bglb);

let shadow_uniform_bgl = uniform_bglb.build(device, Some("shadow uniform bgl"));

Expand Down
1 change: 1 addition & 0 deletions rend3-routine/src/uniforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub fn add_to_graph<'node>(
bgb.append_buffer(&uniform_buffer);

ctx.data_core.directional_light_manager.add_to_bg(&mut bgb);
ctx.data_core.point_light_manager.add_to_bg(&mut bgb);

let shadow_uniform_bg = bgb.build(
&ctx.renderer.device,
Expand Down
21 changes: 21 additions & 0 deletions rend3-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ pub type MaterialHandle = ResourceHandle<MaterialTag>;
pub type ObjectHandle = ResourceHandle<Object>;
/// Refcounted handle to a DirectionalLight
pub type DirectionalLightHandle = ResourceHandle<DirectionalLight>;
/// Refcounted handle to a PointLight
pub type PointLightHandle = ResourceHandle<PointLight>;
/// Refcounted handle to a Skeleton
pub type SkeletonHandle = ResourceHandle<Skeleton>;
/// Refcounted handle to an instance of GraphData with the type erased
Expand Down Expand Up @@ -218,6 +220,8 @@ pub type RawMaterialHandle = RawResourceHandle<MaterialTag>;
pub type RawObjectHandle = RawResourceHandle<Object>;
/// Internal non-owning handle to a DirectionalLight
pub type RawDirectionalLightHandle = RawResourceHandle<DirectionalLight>;
/// Internal non-owning handle to a PointLight
pub type RawPointLightHandle = RawResourceHandle<PointLight>;
/// Internal non-owning handle to a Skeleton
pub type RawSkeletonHandle = RawResourceHandle<Skeleton>;
/// Internal non-owning handle to an instance of GraphData with the type erased
Expand Down Expand Up @@ -1156,6 +1160,23 @@ changeable_struct! {
}
}

changeable_struct! {
/// Describes how point lights and their shadows should be processed.
pub struct PointLight <- PointLightChange {
/// The position of the light in the world.
pub position: Vec3,

/// The color of the light.
pub color: Vec3,

/// The radius of the light.
pub radius: f32,

/// Constant multiplier for the light.
pub intensity: f32,
}
}

/// The sample count when doing multisampling.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u8)]
Expand Down
25 changes: 21 additions & 4 deletions rend3/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use std::{mem, panic::Location};
use glam::Mat4;
use parking_lot::Mutex;
use rend3_types::{
trait_supertrait_alias, MaterialHandle, MeshHandle, ObjectChange, ObjectHandle, RawDirectionalLightHandle,
RawGraphDataHandleUntyped, RawMaterialHandle, RawMeshHandle, RawSkeletonHandle, RawTexture2DHandle,
RawTextureCubeHandle, SkeletonHandle, Texture2DHandle, TextureCubeHandle, TextureFromTexture, WasmNotSend,
WasmNotSync,
trait_supertrait_alias, MaterialHandle, MeshHandle, ObjectChange, ObjectHandle, PointLight, PointLightChange,
PointLightHandle, RawDirectionalLightHandle, RawGraphDataHandleUntyped, RawMaterialHandle, RawMeshHandle,
RawPointLightHandle, RawSkeletonHandle, RawTexture2DHandle, RawTextureCubeHandle, SkeletonHandle, Texture2DHandle,
TextureCubeHandle, TextureFromTexture, WasmNotSend, WasmNotSync,
};
use wgpu::{CommandBuffer, Device};

Expand Down Expand Up @@ -63,6 +63,10 @@ pub enum InstructionKind {
handle: DirectionalLightHandle,
light: DirectionalLight,
},
AddPointLight {
handle: PointLightHandle,
light: PointLight,
},
AddGraphData {
add_invoke: Box<dyn AddGraphDataAddInvoke>,
},
Expand All @@ -74,6 +78,10 @@ pub enum InstructionKind {
handle: RawDirectionalLightHandle,
change: DirectionalLightChange,
},
ChangePointLight {
handle: RawPointLightHandle,
change: PointLightChange,
},
DeleteMesh {
handle: RawMeshHandle,
},
Expand All @@ -95,6 +103,9 @@ pub enum InstructionKind {
DeleteDirectionalLight {
handle: RawDirectionalLightHandle,
},
DeletePointLight {
handle: RawPointLightHandle,
},
DeleteGraphData {
handle: RawGraphDataHandleUntyped,
},
Expand Down Expand Up @@ -190,6 +201,12 @@ impl DeletableRawResourceHandle for RawDirectionalLightHandle {
}
}

impl DeletableRawResourceHandle for RawPointLightHandle {
fn into_delete_instruction_kind(self) -> InstructionKind {
InstructionKind::DeletePointLight { handle: self }
}
}

impl DeletableRawResourceHandle for RawGraphDataHandleUntyped {
fn into_delete_instruction_kind(self) -> InstructionKind {
InstructionKind::DeleteGraphData { handle: self }
Expand Down
2 changes: 2 additions & 0 deletions rend3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub mod managers {
mod material;
mod mesh;
mod object;
mod point;
mod skeleton;
mod texture;

Expand All @@ -114,6 +115,7 @@ pub mod managers {
pub use material::*;
pub use mesh::*;
pub use object::*;
pub use point::*;
pub use skeleton::*;
pub use texture::*;
}
Expand Down
Loading
Loading