unity,荧光效果(bloom)实现过程

两个月前,刚接触unity的时候费了半天劲儿做了个荧光效果(见:http://www.cnblogs.com/wantnon/p/4430749.html),今天终于抽空整理了一下,把过程写下来。

荧光效果如图:

技术分享 技术分享

 

一,接口:

整理完以后得到三个东西:AE_GroupBloom.cs,Camera_renderBloomRT.prefab,bloomEmitter.shader。

使用方法:

1,将AE_GroupBloom.cs添加到Main Camera上。

2,将Camera_renderBloomRT.prefab添加到场景中。并将Main Camera赋给其中的Main Camera Ref。

3,将发光物体的shader选为Custom/bloomEmitter。

这样就可以实现上面图中的效果。下面说具体代码。

二,实现原理:

(注:我用的是unity5)

AE_GroupBloom.cs代码如下:

using UnityEngine;
using System.Collections;
public class AE_GroupBloom : MonoBehaviour {
    public Material m_groupBloomMaterial;
    void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture){
        ////Debug.Log (destTexture);
        //Copies source texture into destination render texture with a shader.
        Graphics.Blit (sourceTexture, destTexture, m_groupBloomMaterial);
    }
}

Graphics.Blit的官方文档中写道:

 

Copies source texture into destination render texture with a shader.
This is mostly used for implementing image effects.

 

Blit sets dest as the render target, sets source _MainTex property on the material, and draws a full-screen quad.

 

在此处由于脚本加在Main Camera上,所以即sourceTexture就是Main Camera渲染的画面,destTexture就是Inspector中显示的Main Camera的Camera组件中的Target Texture属性,其实默认为None,表示直接渲染到屏幕(否则可以赋一个renderTexture,使此相机渲染到renderTexture上)。Graphics.Blit(sourceTexture, destTexture, m_groupBloomMaterail)的意思就是将Main Camera渲染的画面经过m_groupBloomMaterial中的shader过滤后传送到屏幕(而且sourceTexture会用作m_groupBloomMaterial的shader的_MainTex)。可见,这便是典型的后处理过程,m_groupBloomMaterial中的shder便是后处理shader。

m_groupBloomMaterial是在脚本的setting中事先赋值好的(AE_GroupBloom.mat),如图:

技术分享

 

AE_GroupBloom.mat中的shader用的是Custom/AE_GroupBloom.shader,代码如下:

Shader "Custom/AE_GroupBloom" {
    Properties {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BloomTex ("BloomTex (RGB)", 2D) = "white" {}
        _BloomFactor("Bloom Factor",Range(0,10)) =2.0
    }
    SubShader {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            #include "UnityCG.cginc"
            
            uniform sampler2D _MainTex;
            uniform sampler2D _BloomTex;
            float _BloomFactor;
            v2f_img vert(appdata_img v) : POSITION {
                v2f_img o;
                o.pos=mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv=v.texcoord.xy;
                return o;
            }
            fixed4 frag(v2f_img i):COLOR
            {
                //Get the colors from the RenderTexture and the uv‘s
                //from the v2f_img struct
                fixed4 mainColor = tex2D(_MainTex, i.uv);
                fixed4 bloomColor= tex2D(_BloomTex, i.uv);
                fixed4 finalColor =bloomColor*_BloomFactor+mainColor;
                return finalColor;
            }
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

前面说了_MainTex就是Main Camera渲染的画面,在shader中按如下公式进行合成:

FinalTex=MainTex+BloomTex*BloomFactor

在本例中,我取的是BloomFactor=2.

即:

技术分享 = 技术分享 + 技术分享 *2

于是,现在剩下的问题就是BloomTex如何得到。

1,错误的方法:

最容易想到的办法就是把这些发光物体单独渲染到一个renderTexture上,然后作blur,即得到BloomTex。

只可惜这个方法是错误的,原因在于这种方法没考虑深度遮挡问题,如下图所示:

技术分享   技术分享

当发光物体被其它物体遮挡时,BloomTex上应该有相应的“缺口”。所以上面所述单独只将发光物体渲染一遍是错误的。

2,正确的方法:

知道了前面方法错在哪里,正确的方法自然也就有了。正确的方法是应该将所有物体都渲染一遍,并且对于所有非发光物体使用“纯黑”shader渲染,对于发光物体使用UnlitColor(颜色无光照)shader渲染,最后再blur,这样就能得到带“缺口”的BloomTex了。

具体实现如下:

新建一个RenderTexture命名为bloomRT,新建一个Camera命名为Camera_renderBloomRT。

在Inspector中将Camera_renderBloomRT的Camera组件的Target Texture赋为bloomRT。这样Camera_renderBloomRT就会把图像渲染到bloomRT上了。

为Camera_renderBloomRT添加脚本renderBloomRT(并在Inspector中将Main Camera赋给其中的m_mainCameraRef变量):

using UnityEngine;
using System.Collections;
public class renderBloomRT : MonoBehaviour {
    public Camera m_mainCameraRef;
    public Shader m_renderBloomTexShader;
    void Start () {
        GetComponent<Camera> ().enabled = false;//it is equals to uncheck Camera component in Inspector
        synchronizePosAndRotWithMainCamera ();
    }
    void LateUpdate () {
        synchronizePosAndRotWithMainCamera ();
        //Rendering with Replaced Shaders: http://www.cnblogs.com/wantnon/p/4528677.html
        GetComponent<Camera>().RenderWithShader(m_renderBloomTexShader, "RenderType");
    }
    void synchronizePosAndRotWithMainCamera(){
        transform.position=m_mainCameraRef.transform.position;
        transform.rotation = m_mainCameraRef.transform.rotation;
    }
}

此脚本做了三件事儿:

1,将Camera_renderBloomRT的camera组件disable掉(相当于在inspector中将camera组件前面的勾去掉)。

2,让Camera_renderBloomRT的position和rotation与Main Camera始终保持一致。

3,调用GetComponent<Camera>().RenderWithShader(m_renderBloomTexShader, "RenderType")进行渲染(渲染到bloomRT上)。

m_renderBloomTexShader是在脚本的setting中事先赋好的(Custom/renderBloomTex.shader),如图:

技术分享

 

renderBloomTex.shader代码如下:

Shader "Custom/renderBloomTex" {//modified from "Unlit/Color"
Properties {
    _Color ("Main Color", Color) = (1,1,1,1)
}
SubShader { //this subShader is same with "Unlit/Color" shader, except the RenderType change to "GroupBloom"
    Tags { "RenderType"="GroupBloom" }
    LOD 100
    Pass {  
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
            };
            struct v2f {
                float4 vertex : SV_POSITION;
                UNITY_FOG_COORDS(0)
            };


            fixed4 _Color;
            
            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : COLOR
            {
                fixed4 col = _Color;
                UNITY_APPLY_FOG(i.fogCoord, col);
                UNITY_OPAQUE_ALPHA(col.a);
                return col;
            }
        ENDCG
    }
}
SubShader { //because this subShader renders pure black, so we need not support fog
    Tags { "RenderType"="Opaque" }
    LOD 100
    
    Pass {  
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
            };

            fixed4 _Color;
            
            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : COLOR
            {
                fixed4 col = float4(0,0,0,1);
                return col;
            }
        ENDCG
    }
}
}

此shader的第一个subShader与unity5内置的"Unlit/Color"shader完全相同,只是将"RenderType"="Opaque"改成了"RenderType"="GroupBloom"。第二个subShader则是将所渲染的像素都弄成纯黑色,其标签仍使用"RenderType"="Opaque"。

当调用GetComponent<Camera>().RenderWithShader(m_renderBloomTexShader, "RenderType"),系统将会对所有含有"RenderType"="Opaque"标签的物体使用第二个subShader进行渲染(渲染成纯黒色),而对所有含有"RenderType"="GroupBloom"标签的物体使用第一个subShader进行渲染(Unlit+Color)。

(注:关于RenderWithShader的详细解释见:http://www.cnblogs.com/wantnon/p/4528677.html)

于是接下来我们就清楚该怎么做了,就是:

1,保证所有想渲染成纯黑色的物体带有"RenderType"="Opaque"标签,这个基本上是自然保证的,因为unity里物体的默认shader都带有"RenderType"="Opaque"标签。

2,保证所有想渲染为Unlit+Color的物体带有"RenderType"="GroupBloom",这个需要我们手动去添加,或者更有条理的办法是创建一个标准shader,命名为“Custom/bloomEmitter”,并为其添加"RenderType"="GroupBloom"标签,然后对于所有的发光物体,都使用此shader。

至此我们就将发光物体与不发光物体以不同的replace shader渲染到bloomRT上了,得到bloomRT如下:

技术分享

 

然后再把standard assets中的blur脚本添加到Camera_renderBloomRT上,得到bloomRT如下:

技术分享

 

至此bloomRT已完成,我们在AE_GroupBloom.shader的setting中事先将bloomRT赋给BloomTex分量。即bloomRT就是BloomTex。

技术分享

 

我们还应该为Camera_renderBloomRT添加下面脚本letRenderTextureAspectEqualsToScreenAspect.cs:

using UnityEngine;
using System.Collections;
public class letRenderTextureAspectEqualsToScreenAspect : MonoBehaviour {
    public RenderTexture m_rt;
    // Use this for initialization
    void Start () {
        float screenAspect = (float)(Screen.width) / Screen.height;
        ////Debug.Log (screenAspect);
        m_rt.width = (int)(m_rt.height*screenAspect);
    }
}

并将bloomRT通过Inspector赋给m_rt.

这样在游戏开发执行时在保持bloomRT的高度不变的前提下重设其宽度,使bloomRT的长宽比与屏幕的长宽比相等,从而保证bloomRT能够正确地与原始游戏画面进行合成。

另外需要注意的是bloomRT的尺寸不必与屏幕相等,出于效率考虑bloomRT的尺寸应在能保证视觉效果可接受的前提下尽可能地小。越小越好。

最后将Camera_renderBloomRT使成prefab,以方便以后使用。

(完)

文章来自:http://www.cnblogs.com/wantnon/p/4542172.html
© 2021 jiaocheng.bubufx.com  联系我们
ICP备案:鲁ICP备09046678号-3