Bloom算法介绍
快速预览效果
亮的地方更亮了(这符合我对灯光效果的想象),还会向周围扩散
Bloom是什么
- Bloom,也称辉光,是一种常见的屏幕效果
- 模拟摄像机的一种图像效果,让画面中较亮的区域“扩散”到周围的区域中,造成一种朦胧的效果
- 可以让物体具有真实的明亮效果
- 可以实现光晕效果
Bloom的实现原理
实现思路
1.提取原图较亮区域(利用阈值)
2.模糊提取部分
3.与原图混合/叠加
前置知识
HDR 和 LDR
-
HDR和LDR分别是是高动态范围和低动态范围的缩写
-
LDR
- jpg、png格式图片
- RGB通道范围在[0,1]
-
HDR
- HDR、EXR格式图片
- 可以超过1
- 主要是因为我们生活的世界亮度差异很大,LDR的范围并不能描述所有的颜色亮度
- 常与色调映射一起使用
高斯模糊
- 实现图像模糊的一种方式
- 可以减少图像噪声、降低细节层次
- 利用高斯核进行卷积运算,得到模糊的图像
高斯核
-
通过高斯函数定义的卷积核
-
核中心:(0,0)
-
核大小:3x3(通常使用)
-
标准方差σ:1.5
-
计算步骤:
- 将(x,y)带入公式中,计算出权重值,(权重值代表当前处理像素的影响程度,离中心越近权重越大)
- 为了保证卷积后图像不变暗,需要对高斯核进行归一化处理(每个权重除以所有权重的和)
-
如果使用二维高斯核:
- 计算量大,N×N的高斯核需要 N * N * W * H次纹理采样
-
二维高斯核的可分离性
- 二维高斯核可以拆成两个一维高斯核
- 用两个一维高斯核先后对图像进行两次卷积操作
- 采样次数变成2 * N * W * H,大大提高性能
- 一维高斯核中包括了很多重复的权重(下例中0.0545,0.02442)
- 下大小为5的高斯核,实际上只需要记录三个权重值即可(0.0545、0.2442、0.4026)
卷积
-
一种常用的图像操作手段
-
利用“卷积核”对图像的每个像素进行一系列操作
-
卷积核:
- 通常是由四方形网格结构,该区域内每个放个都有一个权重值
-
当我们对图像中的像素进行卷积时:
- 会把卷积核的中心放置在该像素上
- 翻转核之后再依次计算核中每个元素和其覆盖的图像像素值的乘积并求和
- 得到的结果就是该位置的新像素值
- 周围像素对该像素的影响
滤波
更详细需要去回顾GAMES101-L6
从图像处理看,使用滤波可以抹掉特定频率的信号,从而实现模糊的效果。
- 高通滤波:提取边缘(信号变化大的地方)
- 低通滤波:模糊
卷积定理:时域上的卷积就是频域上的乘积(时域上的乘积=频域上的卷积)
Bloom算法应用
配合自发光效果
配合粒子特效
配合ToneMapping
Bloom效果和ToneMapping结合可以较好的保留暗部和亮部的细节
GodRay效果
使用径向模糊代替高斯模糊,模拟光线往某个方向扩散的效果
GodRay配合ToneMapping
大佬关于GodRay不等距采样的想法
Bloom算法实现
基于Unity Builtin管线的实现方案(作业会给出URP方案)
详细可以看入门精要12章,后期处理部分
C#代码部分
用于屏幕后处理效果的一个基类,增加了检查当前平台是否支持功能,以及创建用于处理渲染纹理的材质
1 | using System; |
具体调用OnRenderImage函数的脚本
1 | using System.Collections; |
Shader cg语言部分
1 | Shader "Unlit/BloomTest" |
参考资料
- https://github.com/keijiro/KinoBloom
- https://github.com/MarcusXie3D/FastBloomForMobiles
- https://learnopengl.com/Advanced-Lighting/Bloom
- Unity Shader入门精要:12.5 Bloom效果
- https://en.wikipedia.org/wiki/Bloom_(shader_effect)
- https://en.wikipedia.org/wiki/High_dynamic_range
- https://zhuanlan.zhihu.com/p/76505536
- 高品质后处理:十种图像模糊算法的总结与实现
- Unity 关于仿崩坏3部分Bloom(部分泛光)效果实现原理
- unity 特效shader下载_二十:Unity 后处理 Bloom mask,基于深度和单个物件的Bloom
- 移动端Bloom优化
- 大佬笔记:
作业
手动实现Bloom效果
简单加了点Bloom,原图地球不是很亮,不敢拉太高。
URP的方案是通过ScriptableRendererFeature来实现的,下面提供几篇参考文章。
https://zhuanlan.zhihu.com/p/161658349
https://zhuanlan.zhihu.com/p/400858121
值得一提的是有个bug找了半天,这里的_Bloom纹理是通过renderPass将RT缓冲传进来的,原先builtin是用的小写"black",但在URP中一直传不过来很奇怪,改成大写就成功了,不知道有没有大佬知道原因。
1 | Properties |
然后在帧调试器中发现Unity自己实现的bloom是先下采样再上采样,很神奇,下次研究下。
代码在Urp包的Runtime/Passes目录
Volume面板显示
1 | [System.Serializable, VolumeComponentMenuForRenderPipeline("Custom/Bloom", typeof(UniversalRenderPipeline))] |
自定义ScriptableRendererFeature
1 | public class CustomBloomRenderFeature : ScriptableRendererFeature |
自定义RendererPass
1 | public class CustomBloomPass : ScriptableRenderPass |
Shader
1 | Shader "URPLearn/BloomTest" |
GodRay效果
径向模糊介绍:
- 径向模糊(Radial Blur)可以给画面带来很好的速度感,是各类游戏中后处理的常客,也常用于Sun Shaft等后处理特效中作为光线投射的模拟。
径向模糊的原理:
- 首先选取一个径向轴心(Radial Center)
- 然后将每一个采样点的uv基于此径向轴心进行偏移(offset)
- 并进行一定次数的迭代采样
- 最终将采样得到的RGB值累加,并除以迭代次数。
shader部分
1 | Shader "URPLearn/GodRayTest" |
Pass中关键的模糊部分,提取和混合和Bloom大差不差
1 | // 通过循环迭代径向模糊 |
思考:如何实现bloom的mask功能?
自己想了想最简单的是用一张额外的Mask记录(为了避免画面变亮,可以存储的alpha值越小,bloom效果越强烈)
查阅了下,有几种方案
- 用Alpha通道来作为Mask,乘上Bloom的结果
- 半透明物体可能有问题,可以采用两个通道来存储原Alpha
- 第一个pass需要加ColorMask RGB(避免影响alpha存储)
- 第二个pass需要加ColorMask A(避免影响第一个pass输出颜色)
- 用SRP渲染一张Mask图
- 用Command-Buffer
- 用模板测试
- 直接用Mask图
- 总结:
- 多用一张Mask图需要将标记物体渲染成Mask图,多一遍渲染开销,但好处是容易接入已有项目,后续维护成本低点
- 使用Alpha通道需要修改已有的shader,并且半透明物体需要两个pass
- 具体要根据项目半透明物体数量来权衡