BlueRoseNote/07-Other/Unity/Unity通用渲染管线(URP)系列(十一)——后处理(Bloom).md
2023-06-29 11:55:02 +08:00

103 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Post-FX Stack
为了效率跳过。
`void Draw(RenderTargetIdentifier from,RenderTargetIdentifier to,Pass pass)`
![](https://pic2.zhimg.com/80/v2-9e0c020362ba48d9652de9507acd876d_720w.jpg)
to代表绘制的RT id通过`buffer.SetRenderTarget()`来设置。from为原始渲染结果使用`buffer.SetGlobalTexture()`来向Shader传递贴图资源。
## Bloom
对图像进行双线性采样以生成分辨率不断对半分的Bloom金字塔。
![](https://pic1.zhimg.com/80/v2-263834811ad5f64ae6674ebaae360bc8_720w.jpg)
`_BloomPyramid1`~`_BloomPyramid16`传递id。
![](https://pic1.zhimg.com/80/v2-a2dfea38523980ad31c5e656a181735c_720w.jpg)
创建一个DoBloom方法。首先将摄像机的像素宽度和高度减半然后选择默认的渲染纹理格式。最初我们将从源复制到金字塔中的第一个纹理。追踪那些标识符。
![](https://pic4.zhimg.com/80/v2-8946f3551e373b2a5cc399a360c1e34b_720w.jpg)
## OnBloom
循环整个级别的贴图,并调用`Draw()`绘制之后计算后一级别的贴图的数据准备下一次循环直到循环完成或者贴图分辨为1*1时候。
### Draw
```c#
void Draw (
RenderTargetIdentifier from, RenderTargetIdentifier to, Pass pass
) {
buffer.SetGlobalTexture(fxSourceId, from);
buffer.SetRenderTarget(
to, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store
);
buffer.DrawProcedural(
Matrix4x4.identity, settings.Material, (int)pass,
MeshTopology.Triangles, 3
);
}
```
##
```c#
Pass {
BloomCombine,
BloomHorizontal,
BloomPrefilter,
BloomVertical,
Copy
}
```
Bloom循环的主要内逻辑为
1. copy之前的渲染结果。
2. 进行一次预处理。
3. 取得2个RT首先进行水平高斯模糊之后进行垂直高斯模糊。因为水平模糊已经进行一次采样所以垂直采样的次数可以减半了。
4. 释放中间产生水平高斯的RT。
5. 进行反向叠加产生结果循环。(垂直高斯模糊后的结果)
6. 释放垂直高斯的RT。
## 预处理
Bloom通常在艺术上用于仅使某些东西发光但是我们的效果目前适用于所有对象不管它有多亮。尽管从物理上讲没有意义但是我们可以通过引入亮度阈值来限制影响效果的因素。实际上就是提取亮度高的区域。
c# DoBloom():
```c#
Vector4 threshold;
threshold.x = Mathf.GammaToLinearSpace(bloom.threshold);
threshold.y = threshold.x * bloom.thresholdKnee;
threshold.z = 2f * threshold.y;
threshold.w = 0.25f / (threshold.y + 0.00001f);
threshold.y -= threshold.x;
buffer.SetGlobalVector(bloomThresholdId, threshold);
```
Shader:
```c#
float3 ApplyBloomThreshold (float3 color) {
float brightness = Max3(color.r, color.g, color.b);
float soft = brightness + _BloomThreshold.y;
soft = clamp(soft, 0.0, _BloomThreshold.z);
soft = soft * soft * _BloomThreshold.w;
float contribution = max(soft, brightness - _BloomThreshold.x);
contribution /= max(brightness, 0.00001);
return color * contribution;
}
float4 BloomPrefilterPassFragment (Varyings input) : SV_TARGET {
float3 color = ApplyBloomThreshold(GetSource(input.fxUV).rgb);
return float4(color, 1.0);
}
```
## 解决白色辉光显得块状化问题
使用在Core RP Library的Filtering include文件中定义的SampleTexture2DBicubic函数。
```c#
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Filtering.hlsl"
float4 GetSourceBicubic (float2 fxUV) {
return SampleTexture2DBicubic(
TEXTURE2D_ARGS(_PostFXSource, sampler_linear_clamp), fxUV,
_PostFXSource_TexelSize.zwxy, 1.0, 0.0
);
}
```
之后再合并结果的时候对低分辨的贴图使用该函数进行采样。在案例中被设置为可开启项。
## 强制控制
在`BloomCombinePassFragment`中的给低分辨率结果乘以强度之后在于高分辨率结果叠加。