游戏 Bloom 实现方法
4.1 Bloom
4.1.1 什么是bloom
辉光效果,模拟摄像机图像效果。让舞台有真实明亮效果

4.1.2 Bloom实现原理
1.提取原图较亮区域(阈值设定亮度)
示例:比如大于颜色亮度大于1的区域,都提取出来
2.使用高斯模糊(提取后)图像
3.模糊的结果与原图混合

实现Bloom的效果需要有俩个前提知识
颜色亮度支持大于1(HDR)
使用高斯模糊算法
扩展 Bloom的形成原因
Bloom是什么
https://zhuanlan.zhihu.com/p/76505536

4.1.3 HDR与LDR
正常使用的是LDR模式 颜色的取值范围是(0-1)
LDR(Low Dynamic Range,低动态范围) RGB range in [0,1]
JPG PNG等格式图片

HDR(High Dynamic Range,高动态范围) 可以表示RGB range 可超过 [0,1]的范围
这样可以提取更高亮度(超过1)的区域产生bloom效果
HDR、EXR格式图片

注意:如果不使用HDR那提取的颜色范围只能是在(0-1)的范围

4.1.4 高斯模糊
通常用它来减少图像噪声以及降低细节层次。
数学的角度
图像的高斯模糊过程就是图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以这项技术就叫作**高斯模糊**。

卷积
问题: 什么是卷积?
参考:https://baike.baidu.com/item/%E5%8D%B7%E7%A7%AF/9411006?fr=aladdin
卷积是一种对图像的操作,
卷积是有1纬卷积,2维矩阵,N维矩阵构成

1维卷积
计算过程: 1维卷积 15+24+33=22 14+23+32=16 13+22+3*1=10
2维卷积

简单理解:
就是提取原来的图形的像素RGB,通过卷积把图片模糊掉,在放到现有的像素尺寸。
数学运算如下:
过程,把卷积核放到起始像素,依次计算求和。
注意:计算过程需要把卷积核水平翻转180度。


辅助阅读:卷积究竟卷了啥?——17分钟了解什么是卷积_哔哩哔哩_bilibili
什么!卷积要旋转180度?! - 简书 (jianshu.com)

高斯核

问题 高斯核维数的增加,模糊的程度也会变大,计算量也越大。
假设屏幕图像的宽度和高度为W和H

如果使用一个N*N的一个卷积核,计算那计算量会很大。
怎么解决这个问题
二维高斯函数特点:可分离性,将二维高斯函数拆成两个一维高斯函数以降低计算量
二维高斯核运算:N * N * W * H 次纹理采样
两次一维高斯核运算:2 * N * W * H 次纹理采样


这里对应的权重值就三个。
4.2 算法演示
4.2.0 实现思路
在C#中调用OnRenderImage函数 获取当前的渲染纹理,
然后将实现Bloom效果的参数和渲染出的纹理传入Shader
在Shader里使用4个Pass完成计算
简单理解就是实现上面那个过程
第一个Pass,根据设定的阈值提取图像中高亮的区域
第二个Pass,实现垂直方向的高斯模糊
第三个Pass, 实现水平方向的高斯模糊
第四个Pass,把高斯模糊的图像和原图进行混合,输出最后结果。

扩展知识 OnRenderImage函数
函数介绍
该函数允许我们使用着色器滤波操作来修改最终的图像,输入原图像source,输出的图像放在desitination里。
该脚本必须挂载在有相机组件的游戏对象上,在该相机渲染完成后调用OnRenderImage()函数;

资料参考:https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnRenderImage.html
4.2.1 PostEffectsBase类
创建的新脚本需要继承这个类,新版本删掉这个脚本,可以在网络上找到或者自己写一个。
《入门精要》P245-246 使用
下方提供 这个类的脚本源码
4.2.2 创建C#脚本控制
这个脚本的继承上面使用的类

第一部分把Shader数据传递到脚本控制,前判断传入的数据是否空
这一部分是计算输入的Shader,材质是否为空
在准备一些传给Shader的参数,
注意:
降采样系数,越大获取的图像越小,处理的模糊像素少,计算量小。
越小效果越好,

开始计算
使用OnRenderImage函数
source 代表原图
destination 代表 模糊后的图像
扩展 什么是双线性滤波?
在上面的代码部分,有一段

buffer0的filterMode
在这里引入一个纹理压缩的一个概念。
在导入图片的时候,有一个选项,就是FilterMode

FilterMode
当该纹理由于3D变换进行拉伸时,它将如何被过滤插值。共有三种选择:
Point 单点插值,纹理将变得块状化(blocky up close)
Bilinear 双线性插值,纹理将变得模糊(blurry up close)
Trilinear 三线性插值,类似Bilinear,但是纹理还会在不同的mip水平之间(between the different mip levels)进行模糊;
所以设置储存的这张图片的变形方式, 第一步就算完成。

计算模糊效果
扩展 Graphics.Blit函数
这里使用大量Graphics.Blit函数,那Graphics.Blit函数的功能是什么?

官方这里介绍,简单理解就是输入一个原图,根据材质效果计算出一张新的图。
示例
Graphics.Blit(原图,结果图,调用的材质,使用材质的那个pass)
这里Pass默认-1调用所有pass
脚本部分完成。
全代码

4.3 Shader部分
视频有误地方
CGINCLUDE 和 CGPROGRAM 的区别问题

参考:CGINCLUDE 关键字的作用
资料参考: https://www.jianshu.com/p/d5767c2a4017?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
在 fragBlur 片元着色的时候,这里需要修改成SV_Target

这个阶段的第二个问题,括号错误,会报错。

本人就这俩个地方错误,导致一直没有找到。
修改结果
解析一下Shader的构成
第一部分,Shader需要的属性,
第二部分,设置第一个Pass提取原图的亮度信息。
在SubShader 里设置渲染语言
注意:CGINCLUDE 和 CGPROGRAM的区别 ,
声明 变量。
上面部分定义不同阶段的Pass ,定义里不同的顶点着色器,不同的片元着色器。
现在编写,每个Pass里的顶点,片元着色器计算过程。
第一个Pass里,提取越图的亮度信息。
这里单独输出一下,看一下效果。
默认


越接近1越黑。提取的颜色越少
第二和第三个Pass计算垂直水平方向的模糊,

高斯模糊卷积核

把上面这部分拆成,垂直和水平方向的俩个一维的计算。
这一步是在顶点着色器计算。
定义高斯模糊的片元着色器函数
这里注意,视频有俩个地方出现了,问题,详细参考上面。
效果 数值是0,没有模糊的效果。

调整——数值是8的时候

这个就是模糊后面的结果。
第4个Pass合并模糊图和原图
创建第4个Pass,

构建顶点片元着色器函数
完成
全代码
配置Bloom
把这个C#脚本挂到摄像机上面

Shader拖到BloomShader里,调整数值。
Bloom效果就有了,
有Bloom效果

无Bloon效果

4.4 Bloom应用
HDR到LDR过程称之为色调映射,Tonemapping
资料参考:https://www.jianshu.com/p/8f29baa34bbc
acs模式的色调映射效果


总结
很详细的了解到Bloom的实现方式,以及实现思路。
学习到了,卷积的基础知识,对高斯核的拆分,

资料参考
https://github.com/logic-three-body/Unity_Bloom
https://blog.csdn.net/candycat1992/article/details/22794773
https://www.yuque.com/qingyan-bng85/zagg8x/epl3qs