C# hash算法 bloom_Unity屏幕后处理——Bloom效果

cd2200f8a30f4c5c452eeddf5c5db14b.png

刚才看了个关于渲染的问题,跟答主吹逼说会写bloom效果。

口说无凭,现在就来写。本文为学习交流目的,请各路大佬批判。

所谓bloom效果,也叫泛光效果,指发光或反光物体周围带光晕的效果。此类效果的实现思路是,在已经渲染好的一帧画面上,把需要泛光效果的区域提取出来,进行一次模糊,再叠加到原图上去。

所以,这个效果分三步,提取区域,模糊,叠加。Shader就按照这样的步骤写,一个步骤一个pass。

一、shader。

1.提取区域。

按亮度提取区域的效果不是很好,会提取我们不需要的区域。我们把需要提取的颜色用透明度通道标记一下,只要不用blend命令,这个透明度也会储存在屏幕图片的像素上,但完全不影响显示效果。在这个场景中,我写了个溶解的shader,溶解边缘的颜色为

0e051f97c196dec2a2d39aabfc54c713.png
不完全透明的颜色

可以看到不完全透明。

我们拿到当前屏幕图片,在片元着色器中判断每个像素的透明度值,如果小于1则返回本颜色,等于1则返回黑色。

Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag_Alpha
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            
            fixed4 frag_Alpha(v2f_img i):SV_Target
            {
                fixed4 result = tex2D(_MainTex,i.uv);
                return result.a<1 ? fixed4(result.rgb,1):fixed4(0,0,0,0);
            }

            ENDCG
        }

2.模糊。

模糊的算法有很多,我们用高斯模糊算法。所有的模糊算法都是利用周遭像素值加权叠加计算得到结果的,权重取决于距离。我们在此不用烂大街的横竖七位十字架,实打实计算像素和周围一圈,共9个像素。根据枯燥的计算,中心像素权重0.1478,四个相邻像素权重0.1183,四个对角像素权重0.0947。在图片上采样到各个像素颜色值,和对应权重相乘,加到一起即得结果。

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #include "UnityCG.cginc"
    sampler2D _AlphaTex;
    fixed4 _AlphaTex_TexelSize;
    struct v2f
    {
        fixed4 pos:SV_POSITION;
        fixed2 uv:TEXCOORD0;
        fixed4 uv1:TEXCOORD1;
        fixed4 uv2:TEXCOORD2;
        fixed4 uv3:TEXCOORD3;
        fixed4 uv4:TEXCOORD4;
    };
    v2f vert(appdata_img v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.texcoord.xy;
        o.uv1.xy = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(-1,-1);
        o.uv1.zw = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(-1,0);
        o.uv2.xy = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(-1,1);
        o.uv2.zw = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(0,-1);
        o.uv3.xy = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(0,1);
        o.uv3.zw = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(1,-1);
        o.uv4.xy = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(1,0);
        o.uv4.zw = v.texcoord.xy + _AlphaTex_TexelSize.xy*fixed2(1,1);
        return o;
    }
    fixed4 frag(v2f i):SV_Target
    {
        fixed3 color = fixed3(0,0,0);
        color += tex2D(_AlphaTex, i.uv)*0.1478;
        color += tex2D(_AlphaTex, i.uv1.xy)*0.0947;
        color += tex2D(_AlphaTex, i.uv2.xy)*0.0947;
        color += tex2D(_AlphaTex, i.uv3.xy)*0.1183;
        color += tex2D(_AlphaTex, i.uv4.xy)*0.1183;
        color += tex2D(_AlphaTex, i.uv1.zw)*0.1183;
        color += tex2D(_AlphaTex, i.uv2.zw)*0.1183;
        color += tex2D(_AlphaTex, i.uv3.zw)*0.0947;
        color += tex2D(_AlphaTex, i.uv4.zw)*0.0947;
        return fixed4(color,1);
    }

3.叠加。

这个没啥技术含量,就是模糊过的图片和原屏幕图片颜色值相加。因为不需要bloom的区域是黑色也就是(0,0,0),所以除了bloom效果的泛光区,叠加是不影响原颜色值的。

Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag_add

            sampler2D _BloomTex;
            sampler2D _MainTex;
            fixed4 frag_add(v2f_img i):SV_Target
            {
                fixed3 color1 = tex2D(_MainTex, i.uv).rgb;
                fixed3 color2 = tex2D(_BloomTex, i.uv).rgb;
                return fixed4(color1+color2, 1);
            }            
            ENDCG
        }

二、C#。

后处理脚本更简单,getTemporary两个RenderTexture,将原屏幕图提取颜色后的区域储存到第一个RenderTexture,将第一个RenderTexture模糊后存储到第二个RenderTexture,然后把第二个RenderTexture和原屏幕图相加,输出到指定的输出纹理上即可。别忘了释放RenderTexture。

using 

泛光程度intensity就是降采样的分母,越大泛光越严重,但是过大的话效果就不堪入目了。

//我再把溶解的shader放上来吧,虽然不难,但如果能帮到新手也是极好的。

Shader "Custom/Dissolved"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Noise ("噪音图", 2D) = "white" {}
        _EdgeColor("边缘颜色",Color)=(1,1,1,1)
        _EdgeWidth("边缘宽度",Range(0,1))=0.2
        _Cut("溶解程度", Range(0,1))=0.5
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            sampler2D _Noise;
            fixed4 _EdgeColor;
            float _EdgeWidth;
            float _Cut;

            struct a2v
            {
                fixed4 vertex:POSITION;
                fixed3 normal:NORMAL;
                fixed4 texcoord:TEXCOORD0;
            };
            struct v2f
            {
                fixed4 pos:SV_POSITION;
                fixed2 uv:TEXCOORD0;
                fixed3 worldNormal:TEXCOORD1;
                fixed3 worldPos:TEXCOORD2;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.uv = v.texcoord.xy;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }
            fixed4 frag(v2f i):SV_Target
            {
                float r = tex2D(_Noise, i.uv).r;
                clip(r-_Cut);
                if(r-_Cut<_EdgeWidth)
                {
                    return _EdgeColor;
                }
                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
                

                fixed3 ambient = albedo*UNITY_LIGHTMODEL_AMBIENT.rgb;

                fixed3 l = normalize(_WorldSpaceLightPos0.xyz - _WorldSpaceLightPos0.w*i.worldPos);
                float NdotL = max(0,dot(l, i.worldNormal));

                fixed3 diffuse = albedo*NdotL;

                return fixed4(ambient + diffuse, 1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值