unity中的sun shafts(也称gay ray)的一种实现方法

来源:互联网 发布:数码宝贝物语 网络侦探 编辑:程序博客网 时间:2024/04/29 12:43

这个方法来自于圣典中一个大神的分享,链接:http://www.ceeger.com/forum/read.php?tid=12137#read_222385

上次ShadowGun中的做法是用一个可以伸缩的mesh来模拟,这次的则是用屏幕特效来模拟。

外加其实unity自己也提供了一个现成的,可以直接用的效果,圣典的说明链接:
http://www.ceeger.com/Components/script-SunShafts.html
pro版通过导入ImageEffect包,在里面找到 sun shafts 的.cs文件,然后跟其它屏幕特效一样,把它挂在摄像机上就可以使用了。


这种方法跟unity提供的那个应该是类似的。
首先这个实现最重要的是3个shader,分别是 Blend.shader GodRay.shader GodRayOptimize.shader

Blend:这是做RenderTexture混合用的shader
GodRay:第1种实现方法,主要逻辑在 fragment 里
GodRayOptimize:思路和第1个一样,在此原理基础上,把 uv 的计算挪到 vertex 里去做,提高了效率


思路:

这里写图片描述

这里的方法是,根据光源的方向和要发出 gay ray 的物体产生一个方向向量。
如:

half2 texCoord = i.uv;half2 deltaTexCoord = texCoord - ScreenLightPos.xy;deltaTexCoord *= 1.0f / NUM_SAMPLES * Density;

当前像素根据当前其在 RenderTexture 的 uv,沿着向量往光源方向有间隔的取样8次,每次取到的颜色都比上一次要衰减一次,最后取8次取样结果的平均值为当前像素的颜色。
如:

half4 color = tex2D(_MainTex, i.uv);half illuminationDecay = 1.0f;for (int i = 0; i < NUM_SAMPLES; i++){    texCoord -= deltaTexCoord;    half4 sample = tex2D(_MainTex, texCoord);    sample *= illuminationDecay;    color += sample;    illuminationDecay *= Decay;}color /= NUM_SAMPLES;return half4( color.xyz * Exposure, 1);

实际效果像下面一样:

这里写图片描述

cube是要产生gay ray的物体,Sphere是光线的起点

这里写图片描述

产生了重影的地方,就是沿着光源的方向采样8次后可以取到颜色的像素所产生的。因为每次采样的结果是会进行逐步衰减的,所以它们会有深浅之分。
没有重影的像素,实际上是因为它们沿向量采样8次,结果采样不到任何颜色所造成的。

但是我们应该注意到了,现在这不是我们想要的结果,因为这是重影,并不是gay ray,那要怎么办呢?

这个时候,我们只需要反复多来几次这种采样,衰减的过程就可以了。
因为我们是在第一次采样的结果基础上继续进行采样,衰减的,所以之前没有颜色的像素,这个时候也有颜色了。
代码如:

Graphics.Blit(sourceTexture, tempRtA, material);Graphics.Blit(tempRtA, tempRtB, material);Graphics.Blit(tempRtB, tempRtA, material);Graphics.Blit(tempRtA, tempRtB, material);Graphics.Blit(tempRtB, tempRtA, material);materialBlend.SetTexture("_GodRayTex", tempRtA);Graphics.Blit(sourceTexture, destTexture, materialBlend, 0);

可以看到,这里 Graphics.Blit 对 RenderTexture 进行了好几次的过滤。

外加缩小使用的 RenderTexture 的尺寸,也可以使重影的间隔变得不那么明显。
代码如:

void CreateBuffers()    {        int rt_width = Screen.width; // Screen.width / 4;        int rt_height = Screen.height; // Screen.height / 4;        if (!tempRtA)        {            tempRtA = new RenderTexture(rt_width, rt_height, 0);            tempRtA.hideFlags = HideFlags.DontSave;        }        if (!tempRtB)        {            tempRtB = new RenderTexture(rt_width, rt_height, 0);            tempRtB.hideFlags = HideFlags.DontSave;        }    }

效果如图:

这里写图片描述

我们可以看到径向光散射效果更明显了。
外加作为光源的物体一定要比别的物体亮,差距越明显,效果越好,这在现实生活中也是一样的道理。
更好的表现需要对发光体和参数进行更进一步的调整,这就需要使用者自己去体验了。

GodRayOptimize 跟 GodRay 的原理是相同的,只是实现方式上略有区别而已。

下面是.shader和.cs文件的具体代码

Blend:

Shader "Z_TestShader/god ray 2 blend"{    Properties    {        _MainTex("Base (RGB)", 2D) = "" {}        _GodRayTex ("God (RGB)", 2D) = ""{}        _Alpha("_Alpha", Float) = 0.5    }    CGINCLUDE    #include "UnityCG.cginc"    struct v2in    {        float4 vertex : POSITION;        float2 texcoord : TEXCOORD0;    };    struct v2f    {        float4 pos : POSITION;        float2 uv : TEXCOORD0;    };    sampler2D _MainTex;    sampler2D _GodRayTex;    uniform float _Alpha;    v2f vert(v2in v)    {        v2f o;        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);        o.uv = v.texcoord;        return o;    }    half4 frag(v2f i) : COLOR    {        half4 color = tex2D(_MainTex, i.uv) + tex2D(_GodRayTex, i.uv)*_Alpha;        return color;    }    ENDCG    Subshader    {        Tags{ "Queue" = "Transparent" }        Pass        {            ZWrite Off            // 绑定通道            BindChannels            {                Bind "Vertex", vertex                Bind "texcoord", texcoord0                Bind "texcoord1", texcoord1            }            Fog{ Mode off }            CGPROGRAM            #pragma fragmentoption ARB_precision_hint_fastest             #pragma vertex vert            #pragma fragment frag            ENDCG        }    }    Fallback off}

GodRay:

Shader "Z_TestShader/GodRay"{    Properties    {        _MainTex ("Base (RGB)", 2D) = "" {}        // 光线的位置        ScreenLightPos ("ScreenLightPos", Vector) = (0,0,0,0)        // 密度(从效果上讲,不要超过8这个采样数)        Density ("Density", Float) = 0.01        // 衰减        Decay ("Decay", Float) = 0.5        // 曝光        Exposure ("Exposure", Float) = 0.5    }    CGINCLUDE    #include "UnityCG.cginc"    // 采样数    #define NUM_SAMPLES 8    struct v2in    {        float4 vertex : POSITION;        float2 texcoord  : TEXCOORD0;    };    struct v2f    {        float4 pos : POSITION;        float2 uv  : TEXCOORD0;    };    sampler2D _MainTex;    uniform float4 ScreenLightPos;    uniform float Density;    uniform float Decay;    uniform float Exposure;    v2f vert(v2in v)     {        v2f o;        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);        o.uv = v.texcoord;        return o;    }    half4 frag(v2f i) : COLOR    {        half2 texCoord = i.uv;        half2 deltaTexCoord = texCoord - ScreenLightPos.xy;        deltaTexCoord *= 1.0f / NUM_SAMPLES * Density;        half4 color = tex2D(_MainTex, i.uv);        half illuminationDecay = 1.0f;        for (int i = 0; i < NUM_SAMPLES; i++)        {            texCoord -= deltaTexCoord;            half4 sample = tex2D(_MainTex, texCoord);            sample *= illuminationDecay;            color += sample;            illuminationDecay *= Decay;        }        color /= NUM_SAMPLES;        return half4( color.xyz * Exposure, 1);    }    ENDCG     Subshader    {        Tags { "Queue" = "Transparent" }        Pass        {          ZWrite Off          BindChannels           {              Bind "Vertex", vertex              Bind "texcoord", texcoord0              Bind "texcoord1", texcoord1          }          Fog { Mode off }          CGPROGRAM          #pragma fragmentoption ARB_precision_hint_fastest           #pragma vertex vert          #pragma fragment frag          ENDCG      }    }    Fallback off}

GodRayOptimize:

Shader "Z_TestShader/GodRayOptimize"{    Properties    {        _MainTex ("Base (RGB)", 2D) = "" {}        ScreenLightPos ("ScreenLightPos", Vector) = (0,0,0,0)        Density ("Density", Float) = 0.01        Decay ("Decay", Float) = 0.5        Exposure ("Exposure", Float) = 0.5    }    CGINCLUDE    #include "UnityCG.cginc"    struct v2in    {        float4 vertex : POSITION;        float2 texcoord  : TEXCOORD0;    };    struct v2f    {        float4 pos : POSITION;        float2 uv0  : TEXCOORD0;        float2 uv1  : TEXCOORD1;        float2 uv2  : TEXCOORD2;        float2 uv3  : TEXCOORD3;        float2 uv4  : TEXCOORD4;        float2 uv5  : TEXCOORD5;        float2 uv6  : TEXCOORD6;        float2 uv7  : TEXCOORD7;    };    sampler2D _MainTex;    uniform float4 ScreenLightPos;    uniform float Density;    uniform float Decay;    uniform float Exposure;    v2f vert(v2in v)     {        v2f o;        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);        half2 texCoord = v.texcoord;        half2 deltaTexCoord = texCoord - ScreenLightPos.xy;        deltaTexCoord *= 1.0f / 8 * Density;        o.uv0 = texCoord;        texCoord -= deltaTexCoord;        o.uv1 = texCoord;        texCoord -= deltaTexCoord;        o.uv2 = texCoord;        texCoord -= deltaTexCoord;        o.uv3 = texCoord;        texCoord -= deltaTexCoord;        o.uv4 = texCoord;        texCoord -= deltaTexCoord;        o.uv5 = texCoord;        texCoord -= deltaTexCoord;        o.uv6 = texCoord;        texCoord -= deltaTexCoord;        o.uv7 = texCoord;        return o;    }     half4 frag(v2f i) : COLOR    {      half illuminationDecay = 1.0f;      half4 color = tex2D(_MainTex, i.uv0)*illuminationDecay;      illuminationDecay *= Decay;      color += tex2D(_MainTex, i.uv1)*illuminationDecay;      illuminationDecay *= Decay;      color += tex2D(_MainTex, i.uv2)*illuminationDecay;      illuminationDecay *= Decay;      color += tex2D(_MainTex, i.uv3)*illuminationDecay;      illuminationDecay *= Decay;      color += tex2D(_MainTex, i.uv4)*illuminationDecay;      illuminationDecay *= Decay;      color += tex2D(_MainTex, i.uv5)*illuminationDecay;      illuminationDecay *= Decay;      color += tex2D(_MainTex, i.uv6)*illuminationDecay;      illuminationDecay *= Decay;      color += tex2D(_MainTex, i.uv7)*illuminationDecay;      color /= 8;      return half4( color.xyz * Exposure, 1);    }    ENDCG     Subshader    {        Tags { "Queue" = "Transparent" }        Pass        {          ZWrite Off          BindChannels           {              Bind "Vertex", vertex              Bind "texcoord", texcoord0              Bind "texcoord1", texcoord1          }          Fog { Mode off }          CGPROGRAM          #pragma fragmentoption ARB_precision_hint_fastest           #pragma vertex vert          #pragma fragment frag          ENDCG        }    }    Fallback off}

cs:

using UnityEngine;using System.Collections;[ExecuteInEditMode]public class GodRayEffect : MonoBehaviour{    public Transform lightpos;    public Shader curShader;    public Shader curShaderblend;    public float Density = 0.01f;    public float Decay = 0.5f;    public float Exposure = 0.5f;    public float Alpha = 1;    public RenderTexture tempRtA = null;    public RenderTexture tempRtB = null;    private Material m_material;    private Material m_materiaBlend;    Material material    {        get        {            if (m_material == null)            {                m_material = new Material(curShader);                m_material.hideFlags = HideFlags.HideAndDontSave;            }            return m_material;        }    }    Material materialBlend    {        get        {            if (m_materiaBlend == null)            {                m_materiaBlend = new Material(curShaderblend);                m_materiaBlend.hideFlags = HideFlags.HideAndDontSave;            }            return m_materiaBlend;        }    }    void Start()    {        if (!SystemInfo.supportsImageEffects)        {            enabled = false;            return;        }        if (!curShader && !curShader.isSupported)        {            enabled = false;        }    }    void OnDisable()    {        if (m_material != null)        {            DestroyImmediate(m_material);        }        if (m_materiaBlend != null)        {            DestroyImmediate(m_materiaBlend);        }    }    void CreateBuffers()    {        int rt_width = Screen.width; // Screen.width / 4;        int rt_height = Screen.height; // Screen.height / 4;        if (!tempRtA)        {            tempRtA = new RenderTexture(rt_width, rt_height, 0);            tempRtA.hideFlags = HideFlags.DontSave;        }        if (!tempRtB)        {            tempRtB = new RenderTexture(rt_width, rt_height, 0);            tempRtB.hideFlags = HideFlags.DontSave;        }    }    void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)    {        if (curShader != null)        {            Vector3 lightScreenPos = Camera.main.WorldToScreenPoint(lightpos.position);            if (lightScreenPos.z > 0 && lightScreenPos.x > 0 && lightScreenPos.x < GetComponent<Camera>().pixelWidth && lightScreenPos.y > 0 && lightScreenPos.y < GetComponent<Camera>().pixelHeight)            {                Vector4 screenLightPos = new Vector4(lightScreenPos.x / GetComponent<Camera>().pixelWidth, lightScreenPos.y / GetComponent<Camera>().pixelHeight, 0, 0);                material.SetVector("ScreenLightPos", screenLightPos);                Debug.Log(screenLightPos);                material.SetFloat("Density", Density);                material.SetFloat("Decay", Decay);                material.SetFloat("Exposure", Exposure);                materialBlend.SetFloat("Alpha", Alpha);                CreateBuffers();                Graphics.Blit(sourceTexture, tempRtA, material);                Graphics.Blit(tempRtA, tempRtB, material);                Graphics.Blit(tempRtB, tempRtA, material);                Graphics.Blit(tempRtA, tempRtB, material);                Graphics.Blit(tempRtB, tempRtA, material);                materialBlend.SetTexture("_GodRayTex", tempRtA);                Graphics.Blit(sourceTexture, destTexture, materialBlend, 0);            }            else            {                Graphics.Blit(sourceTexture, destTexture);            }        }        else        {            Graphics.Blit(sourceTexture, destTexture);        }    }}
1 0
原创粉丝点击