Skip to content

Commit 827fef2

Browse files
committed
lighting manual additions
1 parent 16c3a27 commit 827fef2

11 files changed

Lines changed: 164 additions & 66 deletions

File tree

crates/examples/src/lighting.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ async fn manual_lighting_ibl() {
162162
types::GpuOnlyArray,
163163
};
164164

165-
let ctx = Context::headless(256, 256).await;
165+
let ctx = Context::headless(512, 512).await;
166166
let stage: Stage = ctx
167167
.new_stage()
168168
.with_background_color(Vec4::new(0.25, 0.25, 0.25, 1.0));
@@ -183,7 +183,7 @@ async fn manual_lighting_ibl() {
183183
.with_projection_and_view(projection, view)
184184
};
185185

186-
let _model: GltfDocument<GpuOnlyArray> = stage
186+
let model: GltfDocument<GpuOnlyArray> = stage
187187
.load_gltf_document_from_path(workspace_dir().join("gltf/marble_bust_1k.glb"))
188188
.unwrap()
189189
.into_gpu_only();
@@ -192,31 +192,38 @@ async fn manual_lighting_ibl() {
192192
.new_skybox_from_path(workspace_dir().join("img/hdr/helipad.hdr"))
193193
.unwrap();
194194
stage.use_skybox(&skybox);
195+
// ANCHOR_END: ibl_setup
196+
197+
// ANCHOR: ibl
198+
use renderling::pbr::ibl::Ibl;
199+
200+
let ibl: Ibl = stage.new_ibl(&skybox);
201+
stage.use_ibl(&ibl);
195202

196203
let frame = ctx.get_next_frame().unwrap();
197204
stage.render(&frame.view());
198205
let img = frame.read_image().await.unwrap();
199-
img.save("lighting/no-lights.png").unwrap();
206+
img.save("lighting/ibl.png").unwrap();
200207
frame.present();
201-
// ANCHOR_END: ibl_setup
208+
// ANCHOR_END: ibl
202209

203-
// ANCHOR: ibl
204-
let ibl =
205-
renderling::capture_gpu_frame(&ctx, "/Users/schell/Desktop/ibl-create.gputrace", || {
206-
let ibl = stage.new_ibl(&skybox);
207-
ctx.get_device().poll(wgpu::PollType::wait()).unwrap();
208-
ibl
209-
});
210-
stage.use_ibl(&ibl);
210+
// ANCHOR: mix
211+
use renderling::{color::css_srgb_color_to_linear, light::Candela};
211212

212-
stage.remove_skybox();
213+
let sunset_amber_sunlight_color = css_srgb_color_to_linear(250, 198, 104);
214+
let _point = stage
215+
.new_point_light()
216+
.with_position({
217+
let bust_aabb = model.bounding_volume().unwrap();
218+
bust_aabb.max
219+
})
220+
.with_color(sunset_amber_sunlight_color)
221+
.with_intensity(Candela(100.0));
213222

214223
let frame = ctx.get_next_frame().unwrap();
215-
renderling::capture_gpu_frame(&ctx, "/Users/schell/Desktop/ibl.gputrace", || {
216-
stage.render(&frame.view());
217-
});
224+
stage.render(&frame.view());
218225
let img = frame.read_image().await.unwrap();
219-
img.save("lighting/ibl.png").unwrap();
226+
img.save("lighting/ibl-analytical-mixed.png").unwrap();
220227
frame.present();
221-
// ANCHOR_END: ibl
228+
// ANCHOR_END: mix
222229
}

crates/examples/src/stage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#[tokio::test]
44
async fn manual_stage() {
5-
env_logger::init();
5+
let _ = env_logger::builder().try_init();
66

77
// ANCHOR: creation
88
use renderling::{context::Context, glam::Vec4, stage::Stage};

crates/renderling/src/pbr/ibl/cpu.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ impl DiffuseIrradianceConvolutionRenderPipeline {
466466

467467
#[cfg(test)]
468468
mod test {
469-
use glam::Vec3;
469+
use glam::{Mat4, Vec3};
470470

471471
use crate::{
472472
context::Context,
@@ -480,17 +480,15 @@ mod test {
480480
/// ensure creation is valid.
481481
fn creates_valid_cubemaps() {
482482
let ctx = Context::headless(600, 400).block();
483-
484483
let proj = crate::camera::perspective(600.0, 400.0);
485484
let view = crate::camera::look_at(Vec3::new(0.0, 0.0, 2.0), Vec3::ZERO, Vec3::Y);
486-
487485
let stage = ctx.new_stage();
488-
489486
let _camera = stage.new_camera().with_projection_and_view(proj, view);
490487
let skybox = stage
491488
.new_skybox_from_path(workspace_dir().join("img/hdr/resting_place.hdr"))
492489
.unwrap();
493490
let ibl = stage.new_ibl(&skybox);
491+
stage.use_ibl(&ibl);
494492
assert_eq!(
495493
wgpu::TextureFormat::Rgba16Float,
496494
ibl.irradiance_cubemap.texture.format()
@@ -499,7 +497,6 @@ mod test {
499497
wgpu::TextureFormat::Rgba16Float,
500498
ibl.prefiltered_environment_cubemap.texture.format()
501499
);
502-
503500
for i in 0..6 {
504501
// save out the irradiance face
505502
let copied_buffer = CopiedTextureBuffer::read_from(
@@ -552,4 +549,47 @@ mod test {
552549
}
553550
}
554551
}
552+
553+
#[test]
554+
/// Creates a Skybox, Ibl, and uses the Ibl to light a mirror cube.
555+
fn mirror_cube_is_lit_by_environment() {
556+
let ctx = Context::headless(256, 256).block();
557+
let stage = ctx.new_stage();
558+
559+
let _camera = stage
560+
.new_camera()
561+
.with_default_perspective(256.0, 256.0)
562+
.with_view(Mat4::look_at_rh(Vec3::ONE * 1.5, Vec3::ZERO, Vec3::Y));
563+
let _model = stage.new_primitive().with_material(
564+
stage
565+
.new_material()
566+
.with_metallic_factor(0.9)
567+
.with_roughness_factor(0.1),
568+
);
569+
570+
let skybox = stage
571+
.new_skybox_from_path(workspace_dir().join("img/hdr/helipad.hdr"))
572+
.unwrap();
573+
stage.use_skybox(&skybox);
574+
575+
// Render once here because we found a bug where rendering before setting
576+
// ibl would cause the primitive bindgroup to *not* be invalidated when
577+
// ibl was set.
578+
//
579+
// This essentially just ensures that `Stage::use_ibl` is invalidating the
580+
// primitive bindgroup.
581+
let frame = ctx.get_next_frame().unwrap();
582+
stage.render(&frame.view());
583+
frame.present();
584+
585+
let ibl = stage.new_ibl(&skybox);
586+
stage.use_ibl(&ibl);
587+
stage.remove_skybox();
588+
589+
let frame = ctx.get_next_frame().unwrap();
590+
stage.render(&frame.view());
591+
let img = frame.read_image().block().unwrap();
592+
img_diff::save("pbr/ibl/mirror_cube_is_lit_by_environment.png", img);
593+
frame.present();
594+
}
555595
}

crates/renderling/src/stage/cpu.rs

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl StageCommitResult {
148148
/// This is the bindgroup that occupies `descriptor_set = 0` in
149149
/// [crate::primitive::shader::primitive_vertex] and
150150
/// [crate::primitive::shader::primitive_fragment].
151-
struct RenderletBindGroup<'a> {
151+
struct PrimitiveBindGroup<'a> {
152152
device: &'a wgpu::Device,
153153
layout: &'a wgpu::BindGroupLayout,
154154
geometry_buffer: &'a wgpu::Buffer,
@@ -166,10 +166,10 @@ struct RenderletBindGroup<'a> {
166166
shadow_map_texture_sampler: &'a wgpu::Sampler,
167167
}
168168

169-
impl RenderletBindGroup<'_> {
169+
impl PrimitiveBindGroup<'_> {
170170
pub fn create(self) -> wgpu::BindGroup {
171171
self.device.create_bind_group(&wgpu::BindGroupDescriptor {
172-
label: Some("renderlet"),
172+
label: Some("primitive"),
173173
layout: self.layout,
174174
entries: &[
175175
wgpu::BindGroupEntry {
@@ -244,25 +244,29 @@ impl StageRendering<'_> {
244244
/// Returns the queue submission index and the indirect draw buffer, if available.
245245
pub fn run(self) -> (wgpu::SubmissionIndex, Option<SlabBuffer<wgpu::Buffer>>) {
246246
let commit_result = self.stage.commit();
247-
let current_renderlet_bind_group_creation_time = commit_result.latest_creation_time();
248-
let previous_renderlet_bind_group_creation_time =
249-
self.stage.renderlet_bind_group_created.swap(
250-
current_renderlet_bind_group_creation_time,
247+
let current_primitive_bind_group_creation_time = commit_result.latest_creation_time();
248+
log::trace!("current_primitive_bind_group_creation_time: {current_primitive_bind_group_creation_time}");
249+
let previous_primitive_bind_group_creation_time =
250+
self.stage.primitive_bind_group_created.swap(
251+
current_primitive_bind_group_creation_time,
251252
std::sync::atomic::Ordering::Relaxed,
252253
);
253-
let should_invalidate_renderlet_bind_group =
254-
commit_result.should_invalidate(previous_renderlet_bind_group_creation_time);
255-
let renderlet_bind_group =
254+
let should_invalidate_primitive_bind_group =
255+
commit_result.should_invalidate(previous_primitive_bind_group_creation_time);
256+
log::trace!(
257+
"should_invalidate_primitive_bind_group: {should_invalidate_primitive_bind_group}"
258+
);
259+
let primitive_bind_group =
256260
self.stage
257-
.renderlet_bind_group
258-
.get(should_invalidate_renderlet_bind_group, || {
259-
log::trace!("recreating renderlet bind group");
261+
.primitive_bind_group
262+
.get(should_invalidate_primitive_bind_group, || {
263+
log::trace!("recreating primitive bind group");
260264
let atlas_texture = self.stage.materials.atlas().get_texture();
261265
let ibl = self.stage.ibl.read().unwrap();
262266
let shadow_map = self.stage.lighting.shadow_map_atlas.get_texture();
263-
RenderletBindGroup {
267+
PrimitiveBindGroup {
264268
device: self.stage.device(),
265-
layout: &Stage::renderlet_pipeline_bindgroup_layout(self.stage.device()),
269+
layout: &Stage::primitive_pipeline_bindgroup_layout(self.stage.device()),
266270
geometry_buffer: &commit_result.geometry_buffer,
267271
material_buffer: &commit_result.materials_buffer,
268272
light_buffer: &commit_result.lighting_buffer,
@@ -301,7 +305,7 @@ impl StageRendering<'_> {
301305
});
302306

303307
render_pass.set_pipeline(self.pipeline);
304-
render_pass.set_bind_group(0, Some(renderlet_bind_group.as_ref()), &[]);
308+
render_pass.set_bind_group(0, Some(primitive_bind_group.as_ref()), &[]);
305309
draw_calls.draw(&mut render_pass);
306310

307311
let has_skybox = self.stage.has_skybox.load(Ordering::Relaxed);
@@ -380,9 +384,9 @@ pub struct Stage {
380384
pub(crate) materials: Materials,
381385
pub(crate) lighting: Lighting,
382386

383-
pub(crate) renderlet_pipeline: Arc<RwLock<wgpu::RenderPipeline>>,
384-
pub(crate) renderlet_bind_group: ManagedBindGroup,
385-
pub(crate) renderlet_bind_group_created: Arc<AtomicUsize>,
387+
pub(crate) primitive_pipeline: Arc<RwLock<wgpu::RenderPipeline>>,
388+
pub(crate) primitive_bind_group: ManagedBindGroup,
389+
pub(crate) primitive_bind_group_created: Arc<AtomicUsize>,
386390

387391
pub(crate) skybox_pipeline: Arc<RwLock<Option<Arc<SkyboxRenderPipeline>>>>,
388392

@@ -846,6 +850,7 @@ impl Stage {
846850
pub fn use_ibl(&self, ibl: &Ibl) -> &Self {
847851
let mut guard = self.ibl.write().unwrap();
848852
*guard = ibl.clone();
853+
self.primitive_bind_group.invalidate();
849854
self
850855
}
851856

@@ -858,7 +863,7 @@ impl Stage {
858863
} else {
859864
let ibl = guard.clone();
860865
*guard = Ibl::new(self.runtime(), &Skybox::empty(self.runtime()));
861-
self.renderlet_bind_group.invalidate();
866+
self.primitive_bind_group.invalidate();
862867
Some(ibl)
863868
}
864869
}
@@ -922,7 +927,7 @@ impl Stage {
922927
pub fn commit(&self) -> StageCommitResult {
923928
let (materials_atlas_texture_was_recreated, materials_buffer) = self.materials.commit();
924929
if materials_atlas_texture_was_recreated {
925-
self.renderlet_bind_group.invalidate();
930+
self.primitive_bind_group.invalidate();
926931
}
927932
let geometry_buffer = self.geometry.commit();
928933
let lighting_buffer = self.lighting.commit();
@@ -933,7 +938,7 @@ impl Stage {
933938
}
934939
}
935940

936-
fn renderlet_pipeline_bindgroup_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
941+
fn primitive_pipeline_bindgroup_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
937942
let geometry_slab = wgpu::BindGroupLayoutEntry {
938943
binding: 0,
939944
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
@@ -1022,7 +1027,7 @@ impl Stage {
10221027
} = LightingBindGroupLayoutEntries::new(10);
10231028

10241029
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1025-
label: Some("renderlet"),
1030+
label: Some("primitive"),
10261031
entries: &[
10271032
geometry_slab,
10281033
material_slab,
@@ -1047,11 +1052,11 @@ impl Stage {
10471052
multisample_count: u32,
10481053
) -> wgpu::RenderPipeline {
10491054
log::trace!("creating stage render pipeline");
1050-
let label = Some("renderlet");
1055+
let label = Some("primitive");
10511056
let vertex_linkage = crate::linkage::primitive_vertex::linkage(device);
10521057
let fragment_linkage = crate::linkage::primitive_fragment::linkage(device);
10531058

1054-
let bind_group_layout = Self::renderlet_pipeline_bindgroup_layout(device);
1059+
let bind_group_layout = Self::primitive_pipeline_bindgroup_layout(device);
10551060
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
10561061
label,
10571062
bind_group_layouts: &[&bind_group_layout],
@@ -1160,9 +1165,9 @@ impl Stage {
11601165
stage_slab_buffer: Arc::new(RwLock::new(geometry_buffer)),
11611166
geometry,
11621167

1163-
renderlet_pipeline: Arc::new(RwLock::new(stage_pipeline)),
1164-
renderlet_bind_group: ManagedBindGroup::default(),
1165-
renderlet_bind_group_created: Arc::new(0.into()),
1168+
primitive_pipeline: Arc::new(RwLock::new(stage_pipeline)),
1169+
primitive_bind_group: ManagedBindGroup::default(),
1170+
primitive_bind_group_created: Arc::new(0.into()),
11661171

11671172
ibl: Arc::new(RwLock::new(ibl)),
11681173
skybox: Arc::new(RwLock::new(skybox)),
@@ -1222,7 +1227,7 @@ impl Stage {
12221227

12231228
log::debug!("setting multisample count to {multisample_count}");
12241229
// UNWRAP: POP
1225-
*self.renderlet_pipeline.write().unwrap() = Self::create_primitive_pipeline(
1230+
*self.primitive_pipeline.write().unwrap() = Self::create_primitive_pipeline(
12261231
self.device(),
12271232
wgpu::TextureFormat::Rgba16Float,
12281233
multisample_count,
@@ -1461,33 +1466,33 @@ impl Stage {
14611466
self
14621467
}
14631468

1464-
/// Adds a primitive to the internal list of renderlets to be drawn each
1469+
/// Adds a primitive to the internal list of primitives to be drawn each
14651470
/// frame.
14661471
///
14671472
/// Returns the number of primitives added.
14681473
///
1469-
/// If you drop the renderlet and no other references are kept, it will be
1474+
/// If you drop the primitive and no other references are kept, it will be
14701475
/// removed automatically from the internal list and will cease to be
14711476
/// drawn each frame.
1472-
pub fn add_primitive(&self, renderlet: &Primitive) -> usize {
1477+
pub fn add_primitive(&self, primitive: &Primitive) -> usize {
14731478
// UNWRAP: if we can't acquire the lock we want to panic.
14741479
let mut draws = self.draw_calls.write().unwrap();
1475-
draws.add_primitive(renderlet)
1480+
draws.add_primitive(primitive)
14761481
}
14771482

14781483
/// Erase the given primitive from the internal list of primitives to be
14791484
/// drawn each frame.
14801485
///
14811486
/// Returns the number of primitives added.
1482-
pub fn remove_primitive(&self, renderlet: &Primitive) -> usize {
1487+
pub fn remove_primitive(&self, primitive: &Primitive) -> usize {
14831488
let mut draws = self.draw_calls.write().unwrap();
1484-
draws.remove_primitive(renderlet)
1489+
draws.remove_primitive(primitive)
14851490
}
14861491

1487-
/// Sort the drawing order of renderlets.
1492+
/// Sort the drawing order of primitives.
14881493
///
14891494
/// This determines the order in which [`Primitive`]s are drawn each frame.
1490-
pub fn sort_renderlets(&self, f: impl Fn(&Primitive, &Primitive) -> std::cmp::Ordering) {
1495+
pub fn sort_primitive(&self, f: impl Fn(&Primitive, &Primitive) -> std::cmp::Ordering) {
14911496
// UNWRAP: panic on purpose
14921497
let mut guard = self.draw_calls.write().unwrap();
14931498
guard.sort_primitives(f);
@@ -1553,7 +1558,7 @@ impl Stage {
15531558
}),
15541559
stencil_ops: None,
15551560
};
1556-
let pipeline_guard = self.renderlet_pipeline.read().unwrap();
1561+
let pipeline_guard = self.primitive_pipeline.read().unwrap();
15571562
let (_submission_index, maybe_indirect_buffer) = StageRendering {
15581563
pipeline: &pipeline_guard,
15591564
stage: self,

crates/renderling/src/ui/cpu.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ impl Ui {
316316
}
317317

318318
fn reorder_renderlets(&self) {
319-
self.stage.sort_renderlets(|a, b| {
319+
self.stage.sort_primitive(|a, b| {
320320
let za = a
321321
.transform()
322322
.as_ref()
290 KB
Loading

manual/src/assets/lighting/ibl.png

284 KB
Loading

manual/src/lighting/analytical.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,4 @@ you can see the effect:
8484

8585
![image of a marble bust lit by a single spot light](/assets/lighting/spot.png)
8686

87-
Good enough! Now on to image based lighting.
87+
Good enough! Now on to image-based lighting, which uses environment maps to simulate complex lighting scenarios. This technique captures real-world lighting conditions and applies them to the scene, providing more realistic reflections and ambient lighting.

0 commit comments

Comments
 (0)