08 / 08 · RELIGHTING
Bring relighting into the engine把重打光接进引擎
Separate working capabilities from the next implementation milestones.区分已实现能力与后续实现里程碑。
Define the asset contract first先定义资产契约
Declare version, units, coordinates, center/covariance/opacity, oriented normals, linear material fields, time/deformation binding and visibility convention. State whether color means base color, captured radiance or a learned residual. Never silently reinterpret an old RGB field.声明版本、单位、坐标、中心/协方差/不透明度、有方向法线、线性材质字段、时间/形变绑定与可见性约定。明确颜色代表 base color、捕获辐亮度还是学习残差。绝不能默默改变旧 RGB 字段的含义。
The teaching layout is 96 bytes: six vec4 values. Roughness and metallic are frame uniforms for our single-material object. A real importer validates material attributes, rejects nonfinite values and declares missing capabilities.教学布局是六个 vec4,共 96 字节。单材质物体的粗糙度与金属度存为逐帧 uniform。真实导入器应验证材质属性、拒绝非有限数并声明缺失能力。
Choose where shading happens选择着色阶段
Here we shade each Gaussian then blend radiance. Deferred shading instead splats a G-buffer and shades pixels later. It may reduce repeated BRDF work, but averaging normals/roughness before nonlinear shading is not equivalent to blending shaded surfaces.这里先着色每颗 Gaussian,再合成辐亮度。Deferred 着色先 splat 到 G-buffer,再逐像素着色,可减少重复 BRDF 计算;但非线性着色前先平均法线/粗糙度,不等于合成已经着色的表面。
The host owns the surface, clock and input. RelightPass owns GPU resources and records commands into a host-provided encoder. It runs in browser WebGPU and native offscreen DX12. Separate passes can later handle shadows and environment prefiltering.宿主拥有 surface、时钟和输入。RelightPass 管理 GPU 资源,向宿主 encoder 记录命令,可用于浏览器 WebGPU 与原生离屏 DX12。后续可用独立 pass 处理阴影和环境预过滤。
Five verifiable milestones五个能验收的里程碑
A, implemented: synthetic known materials/normals, direct light, five inspection modes, deformation and HDR composition. B, next: reproduce a released static method, build an explicit asset converter and match its reference renderer under a fixed light.A,已实现:已知合成材质/法线、直射光、五种观察模式、形变与 HDR 合成。B,下一步:复现已发布静态方法,编写明确资产转换器,在固定光照下对齐参考渲染器。
C: environment diffuse SH and specular IBL; compare a white Lambert sphere to numerical integration. D: visibility with a moving blocker. E: deform an imported asset with consistent material/normal binding; measure flicker under a fixed light. B–E are planned, not features of this lab.C:环境漫反射 SH 与镜面 IBL,用白色 Lambert 球对比数值积分。D:加入可见性并测试运动遮挡物。E:对导入资产做一致的材质/法线绑定和形变,测量固定光照下的闪烁。B–E 是计划,不是当前实验已有功能。
Prepare a convincing explanation before the 28th28 日前准备能讲透的闭环
First reproduce the viewer: covariance, sorting and alpha. Then use one session for Lambert/GGX, one for this lab and its two passes, and one comparing inverse rendering with SH transfer. Be ready to explain material-light ambiguity and dynamic normal/visibility updates.先复现 Viewer,讲清协方差、排序与 alpha。再用一次学习 Lambert/GGX,一次解释本实验两个 pass,一次比较逆渲染与 SH 转移。要能说明材质光照歧义,以及动态法线/可见性更新。
Reproducing every recent paper is a much larger project. For this deadline, build evidence: a running core, pixel checks, accurate capability boundaries and a concrete next step.完整复现所有近期论文是更大的项目。面对截止日期,先建立可验证成果:可运行核心、像素检查、准确能力边界与具体下一步。
Predict it, then run it先预测,再运行
Open this experiment →打开对应实验 →The formula meets actual code公式连接真实代码
This excerpt is extracted from the repository during the site build. SH environment lighting and inverse training are future extensions; their chapters point to the current direct-light/data boundary, not an implemented trainer.此片段在网站构建时从仓库提取。SH 环境光照与逆向训练是未来扩展;对应章节指向当前直射光/数据接口,不代表已实现训练器。
src/relight.rs · L298–397
pub fn render(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
size: [u32; 2],
frame: RelightFrame,
) -> Result<(), String> {
frame.validate()?;
if size.contains(&0)
|| size
.iter()
.any(|&v| v > device.limits().max_texture_dimension_2d)
{
return Err("Invalid relight target size".into());
}
if self.hdr.as_ref().is_none_or(|v| v.2 != size) {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("linear-radiance"),
size: wgpu::Extent3d {
width: size[0],
height: size[1],
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba16Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let view = texture.create_view(&Default::default());
let bind = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.hdr_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
}],
});
self.hdr = Some((view, bind, size));
}
let view = Mat4::look_at_rh(frame.eye(), Vec3::ZERO, Vec3::Y);
let projection = Mat4::perspective_rh(
45_f32.to_radians(),
size[0] as f32 / size[1] as f32,
0.05,
50.0,
);
queue.write_buffer(
&self.globals,
0,
bytemuck::bytes_of(&Globals {
vp: (projection * view).to_cols_array_2d(),
eye: frame.eye().extend(0.0).to_array(),
light: frame.light().extend(frame.intensity).to_array(),
material: [frame.roughness, frame.metallic, frame.mode as f32, 0.0],
viewport: [
size[0] as f32,
size[1] as f32,
if self.srgb { 1.0 } else { 0.0 },
0.0,
],
}),
);
queue.write_buffer(&self.splats, 0, bytemuck::cast_slice(&scene(frame, view)));
let (hdr, texture_binding, _) = self.hdr.as_ref().unwrap();
for (view, pipeline, display) in [
(hdr, &self.splat_pipeline, false),
(target, &self.display_pipeline, true),
] {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("relight-pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(pipeline);
pass.set_bind_group(0, &self.bindings, &[]);
if display {
pass.set_bind_group(1, texture_binding, &[]);
pass.draw(0..3, 0..1);
} else {
pass.draw(0..6, 0..COUNT as u32);
}
}
Ok(())
}Check your understanding检查是否真正理解
What if an imported model has only appearance SH and no materials?导入模型只有外观 SH,没有材质,该怎么办?
Reveal the reasoning展开推理
Use its original appearance renderer and declare relighting unavailable, or run an explicitly identified offline estimation/transfer pipeline. Arbitrary RGB multiplication is not recovered physical relighting.使用原外观渲染并声明不可重打光,或运行明确的离线估计/转移管线。随意乘 RGB 不等于恢复了物理重打光。