参考
may佬《技术美术百人计划》
冯乐乐《UnityShader 入门精要》
Unity 关于仿崩坏3部分Bloom(部分泛光)效果实现原理
Y(luma)=0.2126 R + 0.7152 G + 0.0722 B
https://github.com/mattatz/unity-mask-bloom
图形4.1 Bloom算法 辉光效果
Bloom算法介绍
效果展示
画面中发亮的部分扩散到周围的效果。如下图中机甲周围的光晕
下图对比中可以看到,使用了Bloom的画面远处的云更亮了,并且带有一种朦胧的感觉
对于会发光的特效比如烟花来说,是否使用Bloom的差距还是比较大的
定义
Bloom,也称辉光效果,用来模拟真实世界中摄像机的图像效果,可以让画面中比较亮的区域扩散到周围来达到真实的明亮效果
实现思路
通过设定阈值来提取原图像中比较亮的区域,然后利用高斯模糊对提取后的图像进行模糊处理来模拟光线向外扩散的效果,最后将模糊后的图像与原图像进行混合来得到最后的Bloom效果
前置知识
HDR
LDR(Low Dynamic Range,低动态范围)
- jpg、png格式图片
- RGB范围在[0,1]之间
HDR(High Dynamic Range,高动态范围)
- HDR、EXR格式图片
- RGB范围可在[0,1]之外
高斯模糊
高斯模糊(Gaussian Blur)是一种图像模糊的方式,能够减少图像噪声、降低细节层次。本质上是通过高斯函数去定义一个卷积核,再利用这个卷积核对图像进行卷积运算,从而达到平滑效果
卷积:利用一个卷积核对图像中的每个像素进行操作,卷积核通常为正方形,每个格子内会有一个权重值
- 图为不同尺寸的卷积核
将卷积核的中心放在图像的起始像素上,对卷积核翻转180度后依次计算核中每个元素和对应被核覆盖的像素乘积并求和,得到的结果就是这个位置的新像素;之后重复这一步骤直到全部的像素的值被重新计算。
高斯模糊使用的卷积核为高斯核,通过高斯函数计算出核内的权重值,为了保证卷积后的图像不变暗,需要对权重进行归一化(每个权重/权重总和)
- 图为一个大小为3×3,标准差σ=1.5的高斯核计算过程
高斯核的一些性质:
-
高斯核越大,对图像的模糊效果越明显
-
二维高斯核的计算量很大,一张w × h的图片使用n × n的卷积核需要进行n × n × w × h次纹理采样
-
高斯核具有可分离性,二维高斯核可以拆成两个一维高斯核。使用两个一维高斯核先后对图像进行卷积,结果与使用二维高斯核一样,但是计算量会变为2 × n × w × h
高斯核具有对称性,如一个一维的高斯核只需要记录三个权重值
Bloom效果实现
实现思路
- C#:在脚本中调用OnRenderImage函数得到当前的渲染纹理,将实现Bloom效果的参数和这张渲染纹理传到shader
- shader:使用4个Pass来完成Bloom效果
- 第一个Pass用来根据设定的阈值
- 第二三个Pass用来对图像分别进行竖直方向和水平方向的高斯模糊
- 第四个Pass用来将模糊前后的图像与原图进行叠加得到最终的Bloom效果
C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bloom : PostEffectsBase
{
//1.首先声明Bloom效果需要的shader及需要的材质
public Shader BloomShader;
private Material BloomMaterial = null;
//2.定义该material,在get方法里调用这个函数进行检查
public Material material
{
get
{
BloomMaterial = CheckShaderAndCreateMaterial(BloomShader, BloomMaterial);
return BloomMaterial;
}
}
//3.定义需要传给shader的参数
[Range(0, 4)]
public int iterations = 3; //高斯模糊的迭代次数,迭代次数越多越模糊
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f; //高斯模糊的范围
[Range(1, 8)]
public int downSample = 2; //下采样次数,越大则渲染纹理就越小,需要模糊的像素也越少
[Range(0.0f, 4.0f)]
public float luminanceThreshold = 0.6f; //阈值,因为会开启HDR所以可以大于1
//4.使用OnRenderImage函数获取当前的屏幕图像的渲染纹理
void OnRenderImage(RenderTexture src, RenderTexture dest)