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

这个方法来自于圣典中一个大神的分享,链接: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);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值