feat: add IBL demo with normal mapping and procedural environment lighting

Fix pbr_demo, multi_light_demo, and shadow_demo to use the new 7-param
create_pbr_pipeline with PBR texture bind group (4-entry: albedo+normal)
and IBL bind group. Create ibl_demo showcasing a 7x7 metallic/roughness
sphere grid with IBL-based ambient lighting via BRDF LUT integration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 21:34:06 +09:00
parent 5232552aa4
commit 9202bfadef
7 changed files with 668 additions and 21 deletions

View File

@@ -11,6 +11,7 @@ use voltex_renderer::{
GpuContext, Camera, FpsController, CameraUniform, LightsUniform, LightData,
Mesh, GpuTexture, MaterialUniform, generate_sphere, create_pbr_pipeline, obj,
ShadowMap, ShadowUniform,
IblResources, pbr_texture_bind_group_layout, create_pbr_texture_bind_group,
};
use wgpu::util::DeviceExt;
@@ -32,10 +33,14 @@ struct AppState {
light_buffer: wgpu::Buffer,
material_buffer: wgpu::Buffer,
camera_light_bind_group: wgpu::BindGroup,
_texture: GpuTexture,
_albedo_tex: GpuTexture,
_normal_tex: (wgpu::Texture, wgpu::TextureView, wgpu::Sampler),
pbr_texture_bind_group: wgpu::BindGroup,
material_bind_group: wgpu::BindGroup,
shadow_bind_group: wgpu::BindGroup,
_shadow_map: ShadowMap,
_ibl: IblResources,
ibl_bind_group: wgpu::BindGroup,
input: InputState,
timer: GameTimer,
cam_aligned_size: u32,
@@ -145,7 +150,7 @@ impl ApplicationHandler for MultiLightApp {
// Bind group layouts
let cl_layout = camera_light_bind_group_layout(&gpu.device);
let tex_layout = GpuTexture::bind_group_layout(&gpu.device);
let pbr_tex_layout = pbr_texture_bind_group_layout(&gpu.device);
let mat_layout = MaterialUniform::bind_group_layout(&gpu.device);
// Camera+Light bind group
@@ -170,8 +175,23 @@ impl ApplicationHandler for MultiLightApp {
],
});
// Texture bind group (white 1x1)
let texture = GpuTexture::white_1x1(&gpu.device, &gpu.queue, &tex_layout);
// PBR texture bind group (albedo + normal)
let old_tex_layout = GpuTexture::bind_group_layout(&gpu.device);
let albedo_tex = GpuTexture::white_1x1(&gpu.device, &gpu.queue, &old_tex_layout);
let normal_tex = GpuTexture::flat_normal_1x1(&gpu.device, &gpu.queue);
let pbr_texture_bind_group = create_pbr_texture_bind_group(
&gpu.device,
&pbr_tex_layout,
&albedo_tex.view,
&albedo_tex.sampler,
&normal_tex.1,
&normal_tex.2,
);
// IBL resources
let ibl = IblResources::new(&gpu.device, &gpu.queue);
let ibl_layout = IblResources::bind_group_layout(&gpu.device);
let ibl_bind_group = ibl.create_bind_group(&gpu.device, &ibl_layout);
// Material bind group
let material_bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
@@ -214,9 +234,10 @@ impl ApplicationHandler for MultiLightApp {
&gpu.device,
gpu.surface_format,
&cl_layout,
&tex_layout,
&pbr_tex_layout,
&mat_layout,
&shadow_layout,
&ibl_layout,
);
self.state = Some(AppState {
@@ -231,10 +252,14 @@ impl ApplicationHandler for MultiLightApp {
light_buffer,
material_buffer,
camera_light_bind_group,
_texture: texture,
_albedo_tex: albedo_tex,
_normal_tex: normal_tex,
pbr_texture_bind_group,
material_bind_group,
shadow_bind_group,
_shadow_map: shadow_map,
_ibl: ibl,
ibl_bind_group,
input: InputState::new(),
timer: GameTimer::new(60),
cam_aligned_size,
@@ -518,8 +543,9 @@ impl ApplicationHandler for MultiLightApp {
});
render_pass.set_pipeline(&state.pipeline);
render_pass.set_bind_group(1, &state._texture.bind_group, &[]);
render_pass.set_bind_group(1, &state.pbr_texture_bind_group, &[]);
render_pass.set_bind_group(3, &state.shadow_bind_group, &[]);
render_pass.set_bind_group(4, &state.ibl_bind_group, &[]);
// Draw 5 spheres (objects 0..4)
render_pass.set_vertex_buffer(0, state.sphere_mesh.vertex_buffer.slice(..));