docs: add SSGI quality (bilateral blur + temporal accumulation) design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
107
docs/superpowers/specs/2026-03-26-ssgi-quality-design.md
Normal file
107
docs/superpowers/specs/2026-03-26-ssgi-quality-design.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# SSGI Quality Improvement Design (Bilateral Blur + Temporal Accumulation)
|
||||
|
||||
## Overview
|
||||
|
||||
SSGI 출력의 노이즈를 줄이기 위해 bilateral blur와 temporal accumulation 필터를 추가한다.
|
||||
|
||||
## Scope
|
||||
|
||||
- Bilateral blur 셰이더: depth/normal 기반 edge-aware blur
|
||||
- Temporal accumulation: 이전 프레임 SSGI와 현재 프레임을 블렌딩
|
||||
- 각각 독립적 풀스크린 패스
|
||||
|
||||
## Bilateral Blur
|
||||
|
||||
Depth와 normal 차이를 가중치로 사용하여 edge를 보존하면서 노이즈를 줄이는 블러.
|
||||
|
||||
### 셰이더 (bilateral_blur.wgsl)
|
||||
|
||||
```wgsl
|
||||
@group(0) @binding(0) var input_tex: texture_2d<f32>; // SSGI 출력
|
||||
@group(0) @binding(1) var depth_tex: texture_2d<f32>; // G-Buffer depth
|
||||
@group(0) @binding(2) var normal_tex: texture_2d<f32>; // G-Buffer normal
|
||||
@group(0) @binding(3) var output_tex: texture_storage_2d<rgba16float, write>;
|
||||
|
||||
// 5x5 bilateral kernel
|
||||
// weight = gaussian(spatial_dist) * exp(-|depth_diff|/sigma_d) * max(dot(n1,n2),0)^sigma_n
|
||||
```
|
||||
|
||||
컴퓨트 셰이더로 구현. 워크그룹 16x16.
|
||||
|
||||
### BilateralBlur 구조체
|
||||
|
||||
```rust
|
||||
pub struct BilateralBlur {
|
||||
pipeline: wgpu::ComputePipeline,
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
```
|
||||
|
||||
- `new(device)` — 파이프라인 생성
|
||||
- `dispatch(encoder, ssgi_view, depth_view, normal_view, output_view, width, height)` — 실행
|
||||
|
||||
### 파라미터
|
||||
- kernel_size: 5 (하드코딩)
|
||||
- sigma_spatial: 2.0
|
||||
- sigma_depth: 0.1
|
||||
- sigma_normal: 16.0
|
||||
|
||||
## Temporal Accumulation
|
||||
|
||||
이전 프레임의 결과와 현재 프레임을 블렌딩하여 시간에 따라 노이즈를 줄임.
|
||||
|
||||
### 셰이더 (temporal_accum.wgsl)
|
||||
|
||||
```wgsl
|
||||
@group(0) @binding(0) var current_tex: texture_2d<f32>; // 현재 프레임 (blurred SSGI)
|
||||
@group(0) @binding(1) var history_tex: texture_2d<f32>; // 이전 프레임 결과
|
||||
@group(0) @binding(2) var output_tex: texture_storage_2d<rgba16float, write>;
|
||||
|
||||
// blend_factor = 0.1 (10% 새 프레임, 90% 히스토리)
|
||||
// output = mix(history, current, blend_factor)
|
||||
```
|
||||
|
||||
### TemporalAccumulation 구조체
|
||||
|
||||
```rust
|
||||
pub struct TemporalAccumulation {
|
||||
pipeline: wgpu::ComputePipeline,
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
history_texture: wgpu::Texture, // 이전 프레임 저장
|
||||
history_view: wgpu::TextureView,
|
||||
pub blend_factor: f32, // 0.1
|
||||
}
|
||||
```
|
||||
|
||||
- `new(device, width, height)` — 히스토리 텍스처 + 파이프라인
|
||||
- `dispatch(encoder, current_view, output_view, width, height)` — 블렌딩 실행
|
||||
- `swap_history(new_view)` — 출력을 히스토리로 복사 (또는 ping-pong)
|
||||
|
||||
### Ping-Pong 패턴
|
||||
텍스처 2개를 교대로 사용:
|
||||
- Frame N: read history_A, write history_B → output = history_B
|
||||
- Frame N+1: read history_B, write history_A → output = history_A
|
||||
|
||||
## 적용 순서
|
||||
|
||||
```
|
||||
SSGI Pass → [Bilateral Blur] → [Temporal Accumulation] → Lighting Pass (기존 SSGI 텍스처 대체)
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
- `crates/voltex_renderer/src/bilateral_blur.rs` — BilateralBlur
|
||||
- `crates/voltex_renderer/src/bilateral_blur.wgsl` — 컴퓨트 셰이더
|
||||
- `crates/voltex_renderer/src/temporal_accum.rs` — TemporalAccumulation
|
||||
- `crates/voltex_renderer/src/temporal_accum.wgsl` — 컴퓨트 셰이더
|
||||
- `crates/voltex_renderer/src/lib.rs` — 모듈 추가
|
||||
|
||||
## Testing
|
||||
|
||||
### Bilateral Blur (순수 수학)
|
||||
- gaussian weight 계산 검증
|
||||
- bilateral weight: depth 차이 클수록 weight 작아지는지
|
||||
|
||||
### Temporal Accumulation (순수 수학)
|
||||
- blend: factor=0.0 → history 그대로, factor=1.0 → current 그대로
|
||||
- factor=0.1 → 90% history + 10% current
|
||||
Reference in New Issue
Block a user