一个关于场景过度的叠化效果实现,基于ComandBuffer实现。

htnl7D.gif

概述:

因项目特点,在场景中每个环境下都存在多个mesh,切换环境其实也是针对环境的GameObject做切换。但是做叠化效果的时候,我必须知道下一个要渲染的场景是什么才能去做一个平滑的叠化效果。

基本思路有两个。

  • 1.在要做叠化效果之前的渲染帧取相机rt,保留当前rt场景并显示,与此同时激活下一个环境,立刻渲染到相机rt上并取图,两张图做叠化。
  • 2.利用command Buffer将要激活的下个环境的所有mesh,绘制到一个rt上。 与当前相机rt做叠化。

两者大致实现方式思路差不多,但是第二种需要注意mesh的shader有无grabpass的情况,在存在grabpass的mesh利用drawmesh显示效果可能错误。第一种相对来说费一些,但是因为都是在相机上取rt,效果上更稳定一些。

以下为利用commandbuffer的实现方式,源工程在文末。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

//叠化类型,可以从上下左右过度,none就是普通效果
public enum AlphaCutType
{
    None = 0,
    Left = 1,
    Right = 2,
    Bottom = 3,
    Up = 4,
}

//需要挂接在相机上
[RequireComponent(typeof(Camera))]
public class AlphaCut : MonoBehaviour
{
    public AlphaCutType CutType =  AlphaCutType.None;
    //当前激活对象
    public GameObject srcObject;
    //要绘制的对象
    public GameObject destObject;
    //过渡差值
    [Range(0, 1)]
    public float LerpValue = 0.2f;
    //执行参数
    [Range(0, 1)]
    public float AlphaCutVal = 0f;
    
    //当前相机rt
    RenderTexture srcRt;
    //目前rt,也是comandbuffer绘制的终点
    RenderTexture destRt;
    //目标相机
    Camera targetCamera;
    //叠化mesh
    MeshRenderer m_alphaCutRenderer;
    
    void Start()
    {
        m_alphaCutRenderer = Resources.Load<MeshRenderer>("AlphaCut");
        m_alphaCutRenderer = Instantiate<MeshRenderer>(m_alphaCutRenderer);
        m_alphaCutRenderer.transform.parent = transform;
        targetCamera = GetComponent<Camera>();

        m_alphaCutRenderer.AdaptToCamera(targetCamera);

        srcRt = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32);
        srcRt.Create();

        destRt = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32);
        destRt.Create();

        CommandBuffer cb = new CommandBuffer();
        cb.name = "Alpha Cut Dest";
        cb.SetRenderTarget(destRt);
        var renderers = destObject.GetComponentsInChildren<Renderer>();
        for (int i = 0; i < renderers.Length; i++)
        {
            cb.DrawRenderer(renderers[i], renderers[i].material);
        }

        targetCamera.AddCommandBuffer(CameraEvent.BeforeImageEffects, cb);

        targetCamera.targetTexture = srcRt;
        targetCamera.Render();
        targetCamera.targetTexture = null;
        m_alphaCutRenderer.material.SetTexture("_SrcTex", srcRt);

        m_alphaCutRenderer.material.SetTexture("_DestTex", destRt);

        srcObject.gameObject.SetActive(false);

       
    }

    private void Update()
    {
        if (m_alphaCutRenderer != null && m_alphaCutRenderer.material != null)
        {
            m_alphaCutRenderer.material.SetInt("_Type", (int)CutType);
            m_alphaCutRenderer.material.SetFloat("_LerpValue", LerpValue);
            m_alphaCutRenderer.material.SetFloat("_AlphaCut", AlphaCutVal);
        }
    }


    [ContextMenu("Play")]
    private void ToPlay()
    {
    }
}

shader:

Shader "SceneSwitcher/AlphaCut"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _SrcTex ("SrcTex", 2D) = "white" {}
        _DestTex ("DestTex", 2D) = "white" {}
        _AlphaCut("AlphaCut", Range(0.0, 1.0)) = 1.0
        _Type("Type", Int) = 0
        _LerpValue("LerpValue", Range(0.0, 1.0)) = 1.0
    }
    SubShader
    {
        Tags 
        { 
            "Queue" = "Transparent"
            "RenderType"="Transparent" 
        }
        LOD 200
        
        ZTest Always
        ZWrite Off
        Cull Off
        Blend SrcAlpha OneMinusSrcAlpha, One One

        Pass
        {
            Name "Default"
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment surf
            #pragma target 3.0


            #include "UnityCG.cginc"

            sampler2D _DestTex;
            sampler2D _SrcTex;
            float4 _SrcTex_ST;
            fixed4 _Color;
            float _AlphaCut;
            int _Type;
            float _LerpValue;

            struct VertexData
            {
                float4 vertex  : POSITION;
                float4 color : COLOR;
                float2 tex : TEXCOORD0;
            };

            struct V2f
            {
                float4 vertex : SV_POSITION;
                float4 color : COLOR;
                float2 tex : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
            };

            V2f vert(VertexData vert)
            {
                V2f v2f;
                v2f.worldPosition = vert.vertex;
                v2f.vertex = UnityObjectToClipPos(v2f.worldPosition);
                v2f.tex = vert.tex;
                v2f.color = vert.color * _Color;
                return v2f;
            }

            fixed4 surf (V2f IN) : SV_TARGET
            {
                half4 color = tex2D(_SrcTex, IN.tex) * IN.color;
                half4 destColor = tex2D(_DestTex, IN.tex) * IN.color;

                //根据类型对应index整合公式
                float t = 1.0 - _AlphaCut;

                //获取该方向应该用x还是y
                fixed stepVal = abs(step(_Type, 2) - step(_Type, 4));
                float axis = stepVal * IN.tex.y + (1 - stepVal) *IN.tex.x;

                fixed isZero = step(_Type , 0);
                //计算混合因数
                fixed ret = abs(_Type % 2 - step(axis, abs(_Type % 2 - t))) * (1 - isZero) + isZero * t;
                
                if(isZero == 1)
                {
                    half4 targetColor = color * ret + destColor * (1 - ret);
                    return targetColor;
                }
                //设置混合颜色
                float lerpVal = (0, _LerpValue, _AlphaCut);
                float s = smoothstep(abs(_Type % 2 - t), abs(_Type % 2 - t) + lerpVal * ((_Type % 2) * 2 - 1), axis);
                ret = s * ret;

                half4 targetColor = color * ret + destColor * (1 - ret);
                return targetColor;
            }
        ENDCG
        }
    }
}

链接

Github