欢迎光临散文网 会员登陆 & 注册

游戏 Bloom 实现方法

2021-08-21 17:53 作者:那个人真狗  | 我要投稿

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的实现方式,以及实现思路。

  • 学习到了,卷积的基础知识,对高斯核的拆分,



资料参考


游戏 Bloom 实现方法的评论 (共 条)

分享到微博请遵守国家法律