dof
This commit is contained in:
+134
-6
@@ -170,6 +170,12 @@ pub struct Renderer {
|
||||
msaa_depth_texture: Option<wgpu::Texture>,
|
||||
/// MSAA depth view used as the main pass depth attachment when MSAA is active.
|
||||
msaa_depth_view: Option<wgpu::TextureView>,
|
||||
/// Fog configuration (Étape 25). `None` = fog disabled (zero overhead).
|
||||
fog: Option<super::fog::FogConfig>,
|
||||
/// DoF configuration (Étape 26). `None` = DoF disabled (zero overhead).
|
||||
dof: Option<super::dof::DoFConfig>,
|
||||
/// DoF pipeline (Étape 26). Present only when DoF + HDR are both active.
|
||||
dof_pipeline: Option<super::dof::DoFPipeline>,
|
||||
}
|
||||
|
||||
/// Internal HDR pipeline state: offscreen `Rgba16Float` texture + tone mapping render pipeline.
|
||||
@@ -213,6 +219,8 @@ impl Renderer {
|
||||
hdr: Option<ToneMapper>,
|
||||
bloom_config: Option<BloomConfig>,
|
||||
msaa_config: Option<MsaaConfig>,
|
||||
fog: Option<super::fog::FogConfig>,
|
||||
dof_config: Option<super::dof::DoFConfig>,
|
||||
) -> Self {
|
||||
let queue: wgpu::Queue = context.queue.clone();
|
||||
let device: wgpu::Device = context.device.clone();
|
||||
@@ -220,7 +228,7 @@ impl Renderer {
|
||||
|
||||
// Step 9 (DRAFT 9.1): depth texture + view, allocated once at the initial surface
|
||||
// size (D3). The isolated helper keeps the Phase 4.4 recreate trivial.
|
||||
let (depth_texture, depth_view) = create_depth_texture(&device, width, height);
|
||||
let (depth_texture, depth_view) = create_depth_texture(&device, width, height, dof_config.is_some());
|
||||
|
||||
// Shared frame uniforms: identity camera + white directional light, lit mode by default.
|
||||
// Values become meaningful once an active camera is wired (Step 4.3); for now the default
|
||||
@@ -606,11 +614,16 @@ impl Renderer {
|
||||
hdr: None,
|
||||
bloom: None,
|
||||
bloom_config: bloom_config.clone().unwrap_or_default(),
|
||||
msaa_config: msaa_config.clone().unwrap_or_default(),
|
||||
// When MSAA is not requested (None), store sample_count=1 (disabled).
|
||||
// Using `Default` here would give 4 and incorrectly trigger MSAA allocation.
|
||||
msaa_config: msaa_config.unwrap_or(MsaaConfig { sample_count: 1 }),
|
||||
msaa_color_texture: None,
|
||||
msaa_color_view: None,
|
||||
msaa_depth_texture: None,
|
||||
msaa_depth_view: None,
|
||||
fog,
|
||||
dof: dof_config,
|
||||
dof_pipeline: None,
|
||||
};
|
||||
// Seed the shared frame buffer with an identity camera + current unlit flag so the low-level
|
||||
// `render` path (which has no window/camera) sees coherent values before `render_scene` runs.
|
||||
@@ -657,6 +670,30 @@ impl Renderer {
|
||||
renderer.msaa_depth_texture = Some(msaa_depth_tex);
|
||||
renderer.msaa_depth_view = Some(msaa_depth_view);
|
||||
}
|
||||
// Étape 26: allocate the DoF pipeline when DoF + HDR are both active.
|
||||
if renderer.dof.is_some() {
|
||||
if let Some(hdr) = &mut renderer.hdr {
|
||||
// The color source for DoF is the HDR texture (or bloom composite if bloom is active).
|
||||
let color_tex: &wgpu::Texture = if let Some(bloom) = &renderer.bloom {
|
||||
bloom.composite_texture()
|
||||
} else {
|
||||
&hdr.texture
|
||||
};
|
||||
let color_view = color_tex.create_view(&Default::default());
|
||||
let dof_pipe = super::dof::DoFPipeline::new(
|
||||
&renderer.device, width, height, &renderer.depth_view, &color_view,
|
||||
);
|
||||
// Recreate the TM bind group to read from the DoF output texture.
|
||||
let (bg, _buf) = create_hdr_bind_group(
|
||||
&renderer.device, &hdr.layout, &hdr.sampler, dof_pipe.output_texture(), width, height,
|
||||
);
|
||||
hdr.bind_group = bg;
|
||||
renderer.dof_pipeline = Some(dof_pipe);
|
||||
} else {
|
||||
eprintln!("[WSG] DoF requires HDR: call with_hdr() before with_dof(). DoF disabled.");
|
||||
renderer.dof = None;
|
||||
}
|
||||
}
|
||||
renderer
|
||||
}
|
||||
|
||||
@@ -686,12 +723,26 @@ impl Renderer {
|
||||
self.write_default_frame_uniforms();
|
||||
}
|
||||
|
||||
/// Sets the fog configuration at runtime (Étape 25). `None` disables fog.
|
||||
/// Takes effect on the next `render_scene` call.
|
||||
pub fn set_fog(&mut self, fog: Option<super::fog::FogConfig>) {
|
||||
self.fog = fog;
|
||||
}
|
||||
|
||||
/// Sets the DoF configuration at runtime (Étape 26). `None` disables DoF.
|
||||
/// Only effective when DoF was enabled at construction (pipeline already allocated).
|
||||
pub fn set_dof(&mut self, config: Option<super::dof::DoFConfig>) {
|
||||
if self.dof_pipeline.is_some() {
|
||||
self.dof = config;
|
||||
}
|
||||
}
|
||||
|
||||
/// Recreates the depth texture at a new size, used on window resize (ROADMAP Phase 4.4).
|
||||
/// The previous depth texture is dropped when its field is replaced — no leak, no double
|
||||
/// allocation. The helper `create_depth_texture` (Step 9, D3) is reused so the recreate stays
|
||||
/// trivial. Inputs: width/height — the new surface dimensions in pixels.
|
||||
pub fn resize_depth(&mut self, width: u32, height: u32) {
|
||||
let (depth_texture, depth_view) = create_depth_texture(&self.device, width, height);
|
||||
let (depth_texture, depth_view) = create_depth_texture(&self.device, width, height, self.dof_pipeline.is_some());
|
||||
self._depth_texture = depth_texture;
|
||||
self.depth_view = depth_view;
|
||||
// Step 19 (D9): refresh the viewport height — the unit of the LOD projected-size test.
|
||||
@@ -744,6 +795,23 @@ impl Renderer {
|
||||
self.msaa_depth_texture = Some(msaa_depth_tex);
|
||||
self.msaa_depth_view = Some(msaa_depth_view);
|
||||
}
|
||||
// Étape 26: resize DoF textures + re-point TM bind group at the DoF output.
|
||||
if self.dof_pipeline.is_some() {
|
||||
if let Some(hdr) = &mut self.hdr {
|
||||
let color_tex: &wgpu::Texture = if let Some(bloom) = &self.bloom {
|
||||
bloom.composite_texture()
|
||||
} else {
|
||||
&hdr.texture
|
||||
};
|
||||
let color_view = color_tex.create_view(&Default::default());
|
||||
let dof_pipe = self.dof_pipeline.as_mut().unwrap();
|
||||
dof_pipe.resize(&self.device, width, height, &self.depth_view, &color_view);
|
||||
let (bg, _buf) = create_hdr_bind_group(
|
||||
&self.device, &hdr.layout, &hdr.sampler, dof_pipe.output_texture(), width, height,
|
||||
);
|
||||
hdr.bind_group = bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the stored surface texture format after a surface reconfigure (ROADMAP Phase 4.4).
|
||||
@@ -801,6 +869,9 @@ impl Renderer {
|
||||
light_view_proj,
|
||||
shadow_params,
|
||||
options: [if self.unlit { 1 } else { 0 }, shadow_on, 0, 0],
|
||||
// Étape 25: fog params (disabled by default → fog_a.x = 0).
|
||||
fog_a: self.fog.as_ref().map(|f| f.pack(true).0).unwrap_or(glam::Vec4::ZERO),
|
||||
fog_b: self.fog.as_ref().map(|f| f.pack(true).1).unwrap_or(glam::Vec4::ZERO),
|
||||
};
|
||||
self.queue
|
||||
.write_buffer(&self.frame_buffer, 0, bytemuck::bytes_of(&frame));
|
||||
@@ -1168,9 +1239,61 @@ impl Renderer {
|
||||
bloom.record_passes(&mut encoder, &self.queue, &self.bloom_config);
|
||||
}
|
||||
|
||||
// 8d. Étape 26: DoF passes (CoC → Blur).
|
||||
// Only runs when DoF + HDR are active and DoF config is set.
|
||||
// The DoF output texture becomes the input to the TM pass.
|
||||
if let Some(dof_pipe) = &self.dof_pipeline {
|
||||
if let Some(dof_cfg) = &self.dof {
|
||||
// Update the shared uniform buffer.
|
||||
dof_pipe.update_uniform(&self.queue, dof_cfg, 0.1, 100.0);
|
||||
|
||||
// Pass 1: CoC (depth → R16Float radius texture).
|
||||
{
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("dof coc pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: dof_pipe.coc_view(),
|
||||
resolve_target: None,
|
||||
depth_slice: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
..Default::default()
|
||||
});
|
||||
pass.set_pipeline(dof_pipe.coc_pipeline());
|
||||
pass.set_bind_group(0, dof_pipe.coc_bind_group(), &[]);
|
||||
pass.draw(0..3, 0..1);
|
||||
}
|
||||
|
||||
// Pass 2: Blur (color + CoC → blurred Rgba16Float output).
|
||||
{
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("dof blur pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: dof_pipe.output_view(),
|
||||
resolve_target: None,
|
||||
depth_slice: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
..Default::default()
|
||||
});
|
||||
pass.set_pipeline(dof_pipe.blur_pipeline());
|
||||
pass.set_bind_group(0, dof_pipe.blur_bind_group(), &[]);
|
||||
pass.draw(0..3, 0..1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Étape 20: tone mapping pass — renders a fullscreen triangle that reads the HDR
|
||||
// texture (or the bloom composite when bloom is active), applies exposure + tone
|
||||
// mapping curve, and writes to the surface.
|
||||
// texture (or the bloom composite when bloom is active, or DoF output when DoF is active),
|
||||
// applies exposure + tone mapping curve, and writes to the surface.
|
||||
if let Some(hdr) = &self.hdr {
|
||||
let mut tm_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("tone mapping pass"),
|
||||
@@ -1577,7 +1700,12 @@ fn create_depth_texture(
|
||||
device: &wgpu::Device,
|
||||
width: u32,
|
||||
height: u32,
|
||||
texturable: bool,
|
||||
) -> (wgpu::Texture, wgpu::TextureView) {
|
||||
let mut usage = wgpu::TextureUsages::RENDER_ATTACHMENT;
|
||||
if texturable {
|
||||
usage |= wgpu::TextureUsages::TEXTURE_BINDING;
|
||||
}
|
||||
let depth_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("depth texture"),
|
||||
size: wgpu::Extent3d {
|
||||
@@ -1589,7 +1717,7 @@ fn create_depth_texture(
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: DEPTH_FORMAT,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
usage,
|
||||
view_formats: &[],
|
||||
});
|
||||
let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
Reference in New Issue
Block a user