|
前言
Bloom(光晕)是一种计算机图形效果,用于视频游戏,演示和高动态范围衬着(HDRR)中,以再现真实相机的成像伪像。该效果会发生从图像中敞亮区域的边界延伸的条纹(或羽毛),从而造成超亮的光使摄像机或眼睛捕捉场景的幻觉。
效果对比如下:
左边是原图, 右边Bloom措置后的
道理:
Bloom的实现道理非常简单,大致分为三步:
1. 对需要措置的图像颠末亮度提取, 而且通过一个阙值来控制亮度 2. 对颠末亮度提取后的图像进行模糊措置(这里是采用高斯模糊) 3. 最后再叠加原图和模糊措置后的图像,输出即可
实现
下面我们来逐步的通过代码实现一下。
既然是后期特效, 那么基本框架和之前的后期措置一样, 首先在Camera上挂载一个C#脚本来捕捉摄像机衬着后的图像。
一 C#实现
新建一个Bloom.cs
PostEffectsBase 基类可以在这里获取(来自《Unity Shader入门精要》)- using System.Collections;
- using UnityEngine;
- // ---------------------------【Bloom 全屏泛光后期】---------------------------
- //编纂状态下也运行
- [ExecuteInEditMode]
- public class Bloom : PostEffectsBase
- {
- public Shader bloomShader;
- private Material mMaterial;
- //bloom措置的shader
- public Material material
- {
- get
- {
- mMaterial = CheckShaderAndCreateMaterial(bloomShader, mMaterial);
- return mMaterial;
- }
- }
- //迭代次数
- [Range(0, 4)]
- public int iterations = 3;
- //模糊扩散范围
- [Range(0.2f, 3.0f)]
- public float blurSpread = 0.6f;
- // 降频
- private int downSample = 1;
- // 亮度阙值
- [Range(-1.0f, 1.0f)]
- public float luminanceThreshold = 0.6f;
- // bloom 强度
- [Range(0.0f, 5.0f)]
- public float bloomFactor = 1;
- // bloom 颜色值
- public Color bloomColor = new Color(1, 1, 1, 1);
- void Awake()
- {
- bloomShader = Shader.Find(”lcl/screenEffect/Bloom”);
- }
- //-------------------------------------【OnRenderImage函数】------------------------------------
- // 说明:此函数在当完成所有衬着图片后被调用,用来衬着图片后期效果
- //--------------------------------------------------------------------------------------------------------
- private void OnRenderImage(RenderTexture source, RenderTexture destination)
- {
- if (material)
- {
- int rtW = source.width >> downSample;
- int rtH = source.height >> downSample;
- RenderTexture texture1 = RenderTexture.GetTemporary(rtW, rtH, 0);
- RenderTexture texture2 = RenderTexture.GetTemporary(rtW, rtH, 0);
- // 亮度提取 - 通道0
- material.SetFloat(”_LuminanceThreshold”, luminanceThreshold);
- Graphics.Blit(source, texture1, material, 0);
- // 高斯模糊 - 通道1
- for (int i = 0; i < iterations; i++)
- {
- //垂直高斯模糊
- material.SetVector(”_offsets”, new Vector4(0, 1.0f + i * blurSpread, 0, 0));
- Graphics.Blit(texture1, texture2, material, 1);
- //程度高斯模糊
- material.SetVector(”_offsets”, new Vector4(1.0f + i * blurSpread, 0, 0, 0));
- Graphics.Blit(texture2, texture1, material, 1);
- }
- //用模糊图和原始图计算出轮廓图 - 通道2
- material.SetColor(”_BloomColor”, bloomColor);
- material.SetFloat(”_BloomFactor”, bloomFactor);
- material.SetTexture(”_BlurTex”, texture1);
- Graphics.Blit(source, destination, material, 2);
- }
- }
- }
复制代码 二 Shader实现
下面我们重点来看看Shader是如何实现的:
1.首先我们对图像的亮度的提取
亮度的提取非常简单, 通过一个公式即可提取: 0.2125 * r + 0.7154 * g + 0.0721 * b , 这里的道理就不细说了, 感兴趣的可以去Google一下。 亮度提取成功之后我们可以通过一个阙值来控制,而且把值限制在0-1范围内
关键shader代码如下:- // 亮度提取
- fixed luminance(fixed4 color) {
- return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
- }
- // 片元着色器
- fixed4 fragExtractBright(v2fExtBright i) : SV_Target {
- fixed4 color = tex2D(_MainTex, i.uv);
- // clamp 约束到 0 - 1 区间
- fixed val = clamp(luminance(color) - _LuminanceThreshold, 0.0, 1.0);
- return color * val;
- }
复制代码 呈现效果如下:
2.模糊措置
然后我们再对以上得到的图像颠末高斯模糊措置一下, 这里也可以采用简单的均值模糊措置。
这里的模糊措置教程也不细说了,篇幅太长,可以参考我之前的一篇文章:Unity Shader - 均值模糊和高斯模糊
关键代码: - // ---------------------------【高斯模糊 - start】---------------------------
- struct v2fBlur
- {
- float4 pos : SV_POSITION; //顶点位置
- float2 uv : TEXCOORD0; //纹理坐标
- float4 uv01 : TEXCOORD1; //一个vector4存储两个纹理坐标
- float4 uv23 : TEXCOORD2; //一个vector4存储两个纹理坐标
- };
- //高斯模糊顶点着色器
- v2fBlur vertBlur(appdata_img v)
- {
- v2fBlur o;
- o.pos = UnityObjectToClipPos(v.vertex);
- //uv坐标
- o.uv = v.texcoord.xy;
- //计算一个偏移值,offset可能是(1,0,0,0)也可能是(0,1,0,0)这样就暗示了横向或者竖向取像素周围的点
- _offsets *= _MainTex_TexelSize.xyxy;
- //由于uv可以存储4个值,所以一个uv保留两个vector坐标,_offsets.xyxy * float4(1,1,-1,-1)可能暗示(0,1,0-1),暗示像素上下两个
- //坐标,也可能是(1,0,-1,0),暗示像素摆布两个像素点的坐标,下面*2.0,*3.0同理
- o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);
- o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
- return o;
- }
- //高斯模糊片段着色器
- fixed4 fragBlur(v2fBlur i) : SV_Target
- {
- fixed4 color = fixed4(0,0,0,0);
- color += 0.4026 * tex2D(_MainTex, i.uv);
- color += 0.2442 * tex2D(_MainTex, i.uv01.xy);
- color += 0.2442 * tex2D(_MainTex, i.uv01.zw);
- color += 0.0545 * tex2D(_MainTex, i.uv23.xy);
- color += 0.0545 * tex2D(_MainTex, i.uv23.zw);
- return color;
- }
- // ---------------------------【高斯模糊 - end】---------------------------
复制代码 措置后的效果如下:
3.最后叠加原图和模糊措置后的图像
关键代码如下- // 片元着色器
- fixed4 fragBloom(v2fBloom i) : SV_Target {
- //对原图进行uv采样
- fixed4 mainColor = tex2D(_MainTex, i.uv);
- //对模糊措置后的图进行uv采样
- fixed4 blurColor = tex2D(_BlurTex, i.uv);
- //输出 = 原始图像 + 模糊图像 * bloom颜色 * bloom权值
- fixed4 resColor = mainColor + blurColor * _BloomColor * _BloomFactor
- return resColor;
- }
复制代码 原图和Bloom的对比效果如下(左原图,右bloom):
最终的完整Shader代码:
- // ---------------------------【泛光 Bloom】---------------------------
- Shader ”lcl/screenEffect/Bloom”
- {
- // ---------------------------【属性】---------------------------
- Properties
- {
- _MainTex (”Texture”, 2D) = ”white” {}
- }
- // ---------------------------【子着色器】---------------------------
- SubShader
- {
- //后措置效果一般都是这几个状态
- ZTest Always
- Cull Off
- ZWrite Off
- Fog{ Mode Off }
- CGINCLUDE
- #include ”UnityCG.cginc”
- sampler2D _MainTex;
- half4 _MainTex_TexelSize;
- sampler2D _BlurTex;
- float4 _offsets;
- float _LuminanceThreshold;
- fixed4 _BloomColor;
- float _BloomFactor;
- // ---------------------------【亮度提取 - start】---------------------------
- struct v2fExtBright {
- float4 pos : SV_POSITION;
- half2 uv : TEXCOORD0;
- };
- // 顶点着色器
- v2fExtBright vertExtractBright(appdata_img v) {
- v2fExtBright o;
- o.pos = UnityObjectToClipPos(v.vertex);
- o.uv = v.texcoord;
- return o;
- }
- // 亮度提取
- fixed luminance(fixed4 color) {
- return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
- }
- // 片元着色器
- fixed4 fragExtractBright(v2fExtBright i) : SV_Target {
- fixed4 color = tex2D(_MainTex, i.uv);
- // clamp 约束到 0 - 1 区间
- fixed val = clamp(luminance(color) - _LuminanceThreshold, 0.0, 1.0);
- return color * val;
- }
- // ---------------------------【亮度提取 - end】---------------------------
- // ---------------------------【高斯模糊 - start】---------------------------
- struct v2fBlur
- {
- float4 pos : SV_POSITION; //顶点位置
- float2 uv : TEXCOORD0; //纹理坐标
- float4 uv01 : TEXCOORD1; //一个vector4存储两个纹理坐标
- float4 uv23 : TEXCOORD2; //一个vector4存储两个纹理坐标
- };
- //高斯模糊顶点着色器
- v2fBlur vertBlur(appdata_img v)
- {
- v2fBlur o;
- o.pos = UnityObjectToClipPos(v.vertex);
- //uv坐标
- o.uv = v.texcoord.xy;
- //计算一个偏移值,offset可能是(1,0,0,0)也可能是(0,1,0,0)这样就暗示了横向或者竖向取像素周围的点
- _offsets *= _MainTex_TexelSize.xyxy;
- //由于uv可以存储4个值,所以一个uv保留两个vector坐标,_offsets.xyxy * float4(1,1,-1,-1)可能暗示(0,1,0-1),暗示像素上下两个
- //坐标,也可能是(1,0,-1,0),暗示像素摆布两个像素点的坐标,下面*2.0,*3.0同理
- o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);
- o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
- return o;
- }
- //高斯模糊片段着色器
- fixed4 fragBlur(v2fBlur i) : SV_Target
- {
- fixed4 color = fixed4(0,0,0,0);
- color += 0.4026 * tex2D(_MainTex, i.uv);
- color += 0.2442 * tex2D(_MainTex, i.uv01.xy);
- color += 0.2442 * tex2D(_MainTex, i.uv01.zw);
- color += 0.0545 * tex2D(_MainTex, i.uv23.xy);
- color += 0.0545 * tex2D(_MainTex, i.uv23.zw);
- return color;
- }
- // ---------------------------【高斯模糊 - end】---------------------------
- // ---------------------------【Bloom(高斯模糊和原图叠加) - start】---------------------------
- struct v2fBloom {
- float4 pos : SV_POSITION;
- half2 uv : TEXCOORD0;
- };
- // 顶点着色器
- v2fBloom vertBloom(appdata_img v) {
- v2fBloom o;
- o.pos = UnityObjectToClipPos(v.vertex);
- o.uv = v.texcoord;
- return o;
- }
- // 片元着色器
- fixed4 fragBloom(v2fBloom i) : SV_Target {
- //对原图进行uv采样
- fixed4 mainColor = tex2D(_MainTex, i.uv);
- //对模糊措置后的图进行uv采样
- fixed4 blurColor = tex2D(_BlurTex, i.uv);
- //输出 = 原始图像 + 模糊图像 * bloom颜色 * bloom权值
- fixed4 resColor = mainColor + blurColor * _BloomColor * _BloomFactor;
- return resColor;
- }
- // ---------------------------【Bloom - end】---------------------------
- ENDCG
- // 亮度提取
- Pass {
- CGPROGRAM
- #pragma vertex vertExtractBright
- #pragma fragment fragExtractBright
- ENDCG
- }
- //高斯模糊
- Pass {
- CGPROGRAM
- #pragma vertex vertBlur
- #pragma fragment fragBlur
- ENDCG
- }
- // Bloom
- Pass {
- CGPROGRAM
- #pragma vertex vertBloom
- #pragma fragment fragBloom
- ENDCG
- }
- }
- }
复制代码 最后
有兴趣的小伙伴可以来我的GitHub逛逛, 欢迎Star,谢谢! 里面有我平时学习unity shader过程中实现的一些特效demo。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|