# 延迟渲染

## Deferred Lighting的框架

Deferred Lighting的渲染架构可以分为三个阶段：

1. for each object
{
填充G-Buffer
}
2. for each light
{
Lighting pass
}
3. for each object
{
}


## Lighting pass

Lighting pass在Deferred Lighting框架处于核心地位，在这里我打算先把lighting pass解析清楚。一旦lighting pass表达好了，G-Buffer所需要保存的信息，以及shading pass能得到的信息也都清楚了。

$LaTeX: L_{o}(\mathbf{v})=\pi\rho(\mathbf{l_c}, \mathbf{v})\otimes \mathbf{c}_{light} (\mathbf{n} \cdot \mathbf{l_c})=(\mathbf{c}_{diff} + \frac {\alpha + 2} {8}(\mathbf{n} \cdot \mathbf{h})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_c},\mathbf{h})) \otimes \mathbf{c}_{light} (\mathbf{n} \cdot \mathbf{l_c})$

$LaTeX: L_{o}(\mathbf{v})=\pi\rho(\mathbf{l_{c1}}, \mathbf{v})\otimes \mathbf{c}_{light1} (\mathbf{n} \cdot \mathbf{l_{c1}})$

$LaTeX: +\pi\rho(\mathbf{l_{c2}}, \mathbf{v})\otimes \mathbf{c}_{light2} (\mathbf{n} \cdot \mathbf{l_{c2}})$

$LaTeX: + \ldots$

$LaTeX: +\pi\rho(\mathbf{l_cN}, \mathbf{v})\otimes \mathbf{c}_{lightN} (\mathbf{n} \cdot \mathbf{l_{cN}})$

$LaTeX: \pi\rho(\mathbf{l_cn}, \mathbf{v})\otimes \mathbf{c}_{lightn} (\mathbf{n} \cdot \mathbf{l_cn})$

$LaTeX: L_{o}(\mathbf{v})=(\mathbf{c}_{diff} + \frac {\alpha + 2} {8}(\mathbf{n} \cdot \mathbf{h_1})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_{c1}},\mathbf{h_1})) \otimes \mathbf{c}_{light1} (\mathbf{n} \cdot \mathbf{l_{c1}})$

$LaTeX: +(\mathbf{c}_{diff} + \frac {\alpha + 2} {8}(\mathbf{n} \cdot \mathbf{h_2})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_{c2}},\mathbf{h_2})) \otimes \mathbf{c}_{light2} (\mathbf{n} \cdot \mathbf{l_{c2}})$

$LaTeX: +\ldots$

$LaTeX: +(\mathbf{c}_{diff} + \frac {\alpha + 2} {8}(\mathbf{n} \cdot \mathbf{h_N})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_{cN}},\mathbf{h_N})) \otimes \mathbf{c}_{lightN} (\mathbf{n} \cdot \mathbf{l_{cN}})$

$LaTeX: =\mathbf{c}_{diff}\otimes (\mathbf{c}_{light1} (\mathbf{n} \cdot \mathbf{l_{c1}}) + \mathbf{c}_{light2} (\mathbf{n} \cdot \mathbf{l_{c2}}) + \ldots + \mathbf{c}_{lightN} (\mathbf{n} \cdot \mathbf{l_{cN}}))$

$LaTeX: + \frac {\alpha + 2} {8}(((\mathbf{n} \cdot \mathbf{h_1})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_{c1}},\mathbf{h_1})) \otimes \mathbf{c}_{light1} (\mathbf{n} \cdot \mathbf{l_{c1}})$

$LaTeX: + ((\mathbf{n} \cdot \mathbf{h_2})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_{c2}},\mathbf{h_2})) \otimes \mathbf{c}_{light2} (\mathbf{n} \cdot \mathbf{l_{c2}})$

$LaTeX: + \ldots$

$LaTeX: + ((\mathbf{n} \cdot \mathbf{h_N})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_{cN}},\mathbf{h_N})) \otimes \mathbf{c}_{lightN} (\mathbf{n} \cdot \mathbf{l_{cN}}))$

$LaTeX: Diffuse: \mathbf{c}_{lightn} (\mathbf{n} \cdot \mathbf{l_{cn}})$
$LaTeX: Specular: ((\mathbf{n} \cdot \mathbf{h_n})^{\alpha} F(\mathbf{c}_{spec}, \mathbf{l_{cn}},\mathbf{h_n})) \otimes \mathbf{c}_{lightn} (\mathbf{n} \cdot \mathbf{l_{cn}})$

$LaTeX: float4(1, 1, 1, (\mathbf{n} \cdot \mathbf{h_n})^{\alpha} F(c_{spec}, \mathbf{l_{cn}},\mathbf{h_n})) \times \mathbf{c}_{lightn} (\mathbf{n} \cdot \mathbf{l_{cn}})$

## G-Buffer的分配

$LaTeX: float4(1, 1, 1, (\mathbf{n} \cdot \mathbf{h_n})^{\alpha} F(c_{spec}, \mathbf{l_{cn}},\mathbf{h_n})) \times \mathbf{c}_{lightn} (\mathbf{n} \cdot \mathbf{l_{cn}})$

float2 encode(float3 normal)
{
return normalize(normal.xy) * sqrt(normal.z * 0.5 + 0.5);
}
float3 decode(float2 n)
{
float3 normal;
normal.z = dot(n, n) * 2 - 1;
normal.xy = normalize(n) * sqrt(1 - normal.z * normal.z);
return normal;
}


p = view_dir * ((z * far_plane) / view_dir.z);


$LaTeX: \mathbf{c}_{emit} + (lighting.rgb * \mathbf{c}_{diff} + \frac{\alpha + 2}{8} * lighting.a) * ao$

$LaTeX: \mathbf{c}_{emit} + (lighting.rgb * \mathbf{c}_{diff} + \frac{\alpha + 2}{8} * \mathbf{c}_{spec} * lighting.a) * ao$

## Light volume

Spot light volume

### 优化2：Conditional Rendering

D3D10及以上的显卡都支持conditional rendering，基本用法是这样的：

BeginQuery()
EndQuery()
...
BeginConditionalRendering()
EndConditionalRendering()


## Anti-Alias

### Edge AA

1. 边缘检测，得到每个像素“像边缘的程度”

GPU Gems 2的“Deferred Shading in STALKER”一文提供了一种边缘检测的方法，通过把周围像素的法线差和深度差的和来判断边缘，由e_barrier这个参数来定义阈值和比例，而这个参数和分辨率有关。GPU Gems 3的“Deferred Shading in Tabula Rasa”改进了这个过程，只判断法线差和深度差最大和最小的两组。由于只是局部的相对量而已，这样就做到了和分辨率无关的边缘检测。KlayGE目前用的也是这种方法，得到的边缘如下：

Edge

Normal in G-Buffer

Normal in screen space

Screen space normal based edge

## 展望未来

### 彩色的specular

$LaTeX: specular = diffuse(\frac{lum_{spec}}{lum_{diff} + \epsilon})$

### inferred lighting

Lighting pass可以借用inferred lighting的核心思想来加速。也就是说，lighting pass不需要全尺寸，只需要在一个比较小的render target上执行即可（比如3/4大小）。G-Buffer仍是全尺寸的，并在G-Buffer生成后作一次边缘检测。Shading pass也是全尺寸的，在采样lighting pass texture的时候，利用边缘检测的结果进行保边缘的插值（一般称为Discontinuity Sensitive Filtering，DSF），得到全尺寸lighting的近似。

DSF

### 各向异性BRDF

Crytek的“CryENGINE 3: Reaching the speed of light”里提到了在Deferred Lighting框架下加入各向异性BRDF的方法。它用了Spherical Gaussian（SG）来近似出NDF（来自于SIGGRAPH Asia 2009的All-Frequency Rendering of Dynamic, Spatially-Varying Reflectance），但这个SG只是per-object的。在G-Buffer阶段，不保存normal，而保存SG展开成lobe的系数。而 BRDF的其他几个项，Fresnel term、Geometry term，都留到shading pass才计算。这种方法的好处是，对lighting pass来说一切都是透明的，它照样可以按原来的方法累积光照，因为Microfacet BRDF中除了NDF，其他都作为公因数提取出去了（Microfacet BRDF的详细讲解可以参见“基于物理的BRDF”）。实际上，Fresnel term的系数是lh，必须在lighting pass做。这里相信Crytek是用了nv来代替，这样不是物理正确的，只有在高光的中心点，dot(l, h)才等于dot(n, v)，其他地方dot(n, v)会更迅速地衰减，到边缘地方就非常明显了。如果不在乎这个，是可以把NDF都用SG来表示，并用统一的方法进行渲染。

Lobes in G-Buffer

Anisotropic BRDF

## KlayGE 4.0中的改进

KlayGE 4.0中，延迟渲染进入了渲染系统的核心，可以作为更通用更方便的一个渲染封装来使用。

### 流水线

Pipeline

$LaTeX: q=\frac{far}{far-near}$
$LaTeX: depth_{linear}=\frac{near \times q}{q-depth_{non-linear}}$

### G-Buffer布局

Old G-Buffer

New G-Buffer V1

Normal 32-bit

Normal 16-bit

$LaTeX: F_{Schlick}(\mathbf{c}_{spec}, \mathbf{l}, \mathbf{h})=\mathbf{c}_{spec}+(1-\mathbf{c}_{spec})(1-(\mathbf{l} \cdot \mathbf{h})^5)$

New G-Buffer V2

specular被挪到了RT1的A通道，RT0的RGB通道就能都用来存放normal了。那么，在24-bit normal下渲染结果又如何呢？

Normal 24-bit

Normal 24-bit with Best Fit

### 透明物体

Opaque objects's G-Buffer

Transparent object back face's G-Buffer

Transparent object front face's G-Buffer

After blending

Side view

### Post process

#### HDR

KlayGE 3.12用了filmic tone mapping之后，HDR部分就几乎没有别的改变。这里唯一的变化是最终输出的float4，把亮度存在A通道上。这是为了后面FXAA的需要。

### 总结

• 更高的速度。Multiresolution的方法在GI中获得了成功，也许也可以扩展到direct lighting和SSVO中，用于加速整个延迟渲染
• 改进HDR中的bloom filter。学习3DMark11，用FFT的方式在一个pass内完成bloom、lens flare等特效。
• 支持移动平台。精简的Deferred Rendering流水线将会以至到移动平台上。
• 更多例子用延迟渲染实现。目前只有3个例子用到了deferred框架，其他还是forward的。以后会有越来越多的例子转到deferred中。

## KlayGE 4.3中的改进

### GI和Deferred的分离

Indirect Lighting Layer

### 未来

Multiresolution的框架也许也可以从GI的具体实现中分离出来，使得不同的GI方法，以及SSVO甚至direct lighting都可以使用的加速结构。这件事情要等到以后慢慢尝试了。

## 多视口的改进

for each view port:
generate g-buffer.

lighting.



for each shadowed light:

for each view port:
generate g-buffer.

lighting.



## 透明物体和Deep G-Buffer

Deferred的流水线如果考虑了Deep G-Buffer，看起来是这样的：

for each shadowed light:

for each view port:
for each G-Buffer layer:
generate g-buffer.

for each G-Buffer layer:
lighting.

for each G-Buffer layer:

for each G-Buffer layer:


for each shadowed light:

for each view port:
for each G-Buffer layer:
generate g-buffer.

lighting.



G-Buffer 6张RGBA8 2张RGBA8 2张RGBA8
Depth stencil buffer 3张D24S8 1张D24S8 1张D24S8
Linear depth buffer 3张D32F 1张D32F 1张D32F