先了解HDR技术:就是用有限的高度分布模拟更高范围的高亮度分布,实现这样的功能的技术叫做色调映射技术(Tone Mapping)。
Tone Mapping计算像素的亮度:
Lum(P) = 0.27R + 0.67G + 0.06B
Lscale(x,y) = a * Lum(x,y)/Lumave---Lumave平局亮度
Lfinal = Lscale/(1+Lscale)
光晕效果就是抽出场景中色彩比较亮的部分,加以模糊,使较亮的像素扩散到周边的像素中,再把模糊后的图像叠加到Tone Mapping之后的图像上。
那么HDR与bloom效果的差别到底在什么地方呢?
第一,HDR效果就是超亮的光照与超暗的黑暗的某种结合,这个效果是光照产生的,强度、颜色等方面是游戏程序可动态控制的,是一种即时动态光影;bloom效果则是物体本身发出的光照,仅仅是将光照范围调高到过饱和,是游戏程序无法动态控制的,是一种全屏泛光。
第二,bloom效果无需HDR就可以实现,但是bloom效果是很受限的,它只支持8位RGBA,而HDR最高支持到32位RGBA。
第三,bloom效果的实现很简单,比如《半条命2》的MOD就是一个很小的很简单的MOD,而且bloom效果不受显卡的规格的限制,你甚至可以在TNT显卡上实现bloom效果(当然效果很差)!而HDR,必须是6XXX以上的显卡才能够实现,这里的HDR是指nVIDIA的HDR。这时有必要谈nVIDIA和ATI的显卡所实现的HDR,两者还是有区别的,具体区别就很专业了,总之从真实性表现来看,nVIDIA的显卡实现的HDR更好一些。HDR是nVIDIA提出的概念,从技术上来讲,ATI当然无法严格克隆nVIDIA的技术,所以ATI的HDR是另一种途径实现的尽可能接近的HDR,不能算“真”HDR,据传ATI的R520能够真正实现FP16 HDR。
Bloom效果实现的流程与HDR的物理还原不同,它只是一种简单的近似模拟:
第一步:先获取屏幕图像,然后对每个像素进行亮度检测,若大于某个阀值即保留原始颜色值,否则置为黑色;
第二步:对上一步获取的图像,做一个模糊,通常使用高斯模糊。
第三步:将模糊后的图片和原图片做一个加权和。
通过这三步就可以达到一个全屏泛光的效果。
(二)高斯模糊
对于一个五维的高斯核,我们需要的是3个权重值就可以,由于对称性。高斯方程很好的模拟了每个像素对于当前处理像素的影响程度,距离越近,影响越大。
程序部分:
using UnityEngine;
using System.Collections;
public class GaussianBlur : PostEffectsBase {
public Shader gaussianBlurShader;
private Material gaussianBlurMaterial = null;
public Material material {
get {
gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);
return gaussianBlurMaterial;
}
}
// Blur iterations - larger number means more blur.
[Range(0, 4)]
public int iterations = 3;
// Blur spread for each iteration - larger value means more blur
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f;
[Range(1, 8)]
public int downSample = 2;
/// 3rd edition: use iterations for larger blur
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
int rtW = src.width/downSample;//利用缩放对图样进行采样,减少需要处理的像素个数
int rtH = src.height/downSample;
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
buffer0.filterMode = FilterMode.Bilinear;
Graphics.Blit(src, buffer0);
for (int i = 0; i < iterations; i++) {
//多次迭代计算模糊
material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH,