糍粑大叔的独游之旅-u3d中2D轮廓的生成(下)

本篇根据上、中篇的内容,进行总结,给出关键类的结构。

EffectRTGenerator

EffectRTGenerator是用来做渲染到纹理的辅助类。

  • 通过Inst方法来访问EffectRTGenerator,Inst内部会在首次调用时创建一个实例,不需要在Scene预先放一个RTCamera碍眼,让开发人员完全不用去理会rt生成的细节
  • CreateOutterline方法会创建一个轮廓的rendertexture。CreateOutterline的逻辑框架是:
    1、_Begin(),生成rt,移动target
    2、RenderWithShader
    3、_End(),还原target
    4、根据需求处理纹理
  • 类的定位:EffectRTGenerator放在场景中“非常远”的位置,专门用来做渲染到纹理,其他对象有rt的相关需求时,只需要调用相关的方法即可生成rt。这个类的定位不仅仅是做轮廓rt的生成,而是扩展并支持各种类似rt的生成。例如,可以加入生成“发光”、“烟雾“等贴图,只要按CreateOutterline的模式进行改造就行了。

**注意:**CreateOutterline不能在target的Awake和Start里调用。
代码:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Camera))]
public class EffectRTGenerator : MonoBehaviour
{
    //public int RTGenLayer = 31;

    static EffectRTGenerator m_Instance;

    public static EffectRTGenerator Inst()
    {
        if (m_Instance == null)
        {
            GameObject rtCamera = new GameObject("RTCamera");
            rtCamera.transform.position = new Vector3(9999999, 9999999, -10);
            Camera c = rtCamera.AddComponent<Camera>();
            c.clearFlags = CameraClearFlags.Color;
            c.backgroundColor = Color.black;
            c.orthographic = true;
            c.orthographicSize = 5;
            c.nearClipPlane = 0.3f;
            c.farClipPlane = 1000f;
            c.enabled = false;
            m_Instance = rtCamera.AddComponent<EffectRTGenerator>();    
        }
        return m_Instance;
    }

    Material m_SolidMat = null;
    Material m_BlurMaterial = null;
    Material m_CutoffMaterial = null;
    Material m_CompositeMaterial = null;

    Vector3 m_TargetOldPosition;
    Vector3 m_TargetOldEulerAngles;
    Vector3 m_TargetOldScale;
    int m_TargetOldLayer;

    public void Awake()
    {
        m_Instance = this;

        m_SolidMat = new Material(Shader.Find("Outterline/Solid"));
        m_SolidMat.hideFlags = HideFlags.HideAndDontSave;
        //m_SolidMat.shader.hideFlags = HideFlags.None;

        m_BlurMaterial = new Material(Shader.Find("Outterline/ShapeBlur"));
        m_BlurMaterial.hideFlags = HideFlags.HideAndDontSave;
        //m_BlurMaterial.shader.hideFlags = HideFlags.None;

        m_CutoffMaterial = new Material(Shader.Find("Outterline/CutoffBody"));
        m_CutoffMaterial.hideFlags = HideFlags.HideAndDontSave;
        //m_CutoffMaterial.shader.hideFlags = HideFlags.None;

        m_CompositeMaterial = new Material(Shader.Find("Outterline/Composer"));
        m_CompositeMaterial.hideFlags = HideFlags.HideAndDontSave;
        //m_CompositeMaterial.shader.hideFlags = HideFlags.None;

        // 设置到一个极其远的地方,以便于只渲染目标
        transform.position = new Vector3(99999, 99999, -10);
        //GetComponent<Camera>().cullingMask = 1 >> RTGenLayer;

    }

    void OnDestroy()
    {
        DestroyImmediate(m_SolidMat);
        DestroyImmediate(m_BlurMaterial);
        DestroyImmediate(m_CutoffMaterial);
        DestroyImmediate(m_CompositeMaterial);

    }

    public RenderTexture _Begin(GameObject target, float x, float y)
    {
        // 根据输入的xy,创建一个rt
        RenderTexture rt = new RenderTexture((int)x, (int)y,0);

        rt.useMipMap = true;
        rt.filterMode = FilterMode.Bilinear;
        rt.depth = 0;
        rt.generateMips = true;
        rt.Create();
        GetComponent<Camera>().targetTexture = rt;

        // 调整cemara的范围以适应rt
        float cameraSize = y / 2;
        GetComponent<Camera>().orthographicSize = cameraSize;
        //float r = (float)Screen.width / Screen.height;
        //float r_x =  x / ( y * 1);
        //GetComponent<Camera>().orthographicSize = cameraSize;
        //GetComponent<Camera>().rect = new Rect(0, 0, r_x, 1);

        // 保持旧的位置
        m_TargetOldPosition = target.transform.position;
        m_TargetOldEulerAngles = target.transform.eulerAngles;
        m_TargetOldScale = target.transform.localScale;
        //m_TargetOldLayer = target.layer;

        // 移动目标到摄像机处,保证只有目标在摄像机的范围内
        // 由于此用一个极远的位置,此处只有目标,所有可以不用设置layer
        Vector3 pos = transform.position;
        pos.z = 0;
        target.transform.localScale = Vector3.one;
        target.transform.position = pos;
        target.transform.eulerAngles = Vector3.zero;
        //target.layer = RTGenLayer;

        return rt;
    }

    void _End(GameObject target)
    {
        // 还原
        target.transform.position = m_TargetOldPosition;
        target.transform.eulerAngles = m_TargetOldEulerAngles;
        target.transform.localScale = m_TargetOldScale;
        //target.layer = m_TargetOldLayer;
    }

    public RenderTexture CreateOutterline(GameObject target, float x, float y)
    {
        int iterations = 5;
        float spread = 0.5f;

        RenderTexture rt = _Begin(target, x, y);

        GetComponent<Camera>().RenderWithShader(m_SolidMat.shader, "");

        _End(target);

        iterations = Mathf.Clamp(iterations, 0, 15);
        spread = Mathf.Clamp(spread, 0.5f, 6.0f);

        RenderTexture buffer = RenderTexture.GetTemporary(rt.width, rt.height, 0);
        RenderTexture buffer2 = RenderTexture.GetTemporary(rt.width, rt.height, 0);
        //buffer2.filterMode = FilterMode.Bilinear;
        //buffer.filterMode = FilterMode.Bilinear;
        //rt.filterMode = FilterMode.Bilinear;
        Graphics.Blit(rt, buffer);

        bool oddEven = true;
        for (int i = 0; i < iterations; i++)
        {
            if (oddEven)
                _FourTapCone(buffer, buffer2, i, spread);
            else
                _FourTapCone(buffer2, buffer, i, spread);
            oddEven = !oddEven;
        }
        if (oddEven)
        {
            Graphics.Blit(rt, buffer, m_CutoffMaterial);
            Graphics.Blit(buffer, rt, m_CompositeMaterial);
        }
        else
        {
            Graphics.Blit(rt, buffer2, m_CutoffMaterial);
            Graphics.Blit(buffer2, rt, m_CompositeMaterial);
        }       

        RenderTexture.ReleaseTemporary(buffer);
        RenderTexture.ReleaseTemporary(buffer2);
        return rt;
    }

    void _FourTapCone(RenderTexture source, RenderTexture dest, int iteration, float spread)
    {
        float off = 0.5f + iteration * spread;
        Graphics.BlitMultiTap(source, dest, m_BlurMaterial,
            new Vector2(off, off),
            new Vector2(-off, off),
            new Vector2(off, -off),
            new Vector2(-off, -off)
        );
    }

}

OutterlineRenderer

OutterlineRenderer是目标GameObject的子GameObject,用来绘制轮廓。

  • 提供CreateOutlineRenderer静态方法,target调用该静态方法来创建一个OutterlineRenderer子节点。
  • FindOrCreateOutlineRenderer静态方法是对CreateOutlineRenderer的封装,对于已存在OutterlineRenderer则不再创建。
  • 通过color属性,设置轮廓颜色

代码:

using UnityEngine;
using System.Collections;

public class OutterlineRenderer : MonoBehaviour
{
    Material m_Material;
    public RenderTexture m_RT;

    public static OutterlineRenderer FindOrCreateOutlineRenderer(Unit target)
    {
        Transform t =  target.transform.Find("OutterlineRenderer");
        if (t == null)
            return CreateOutlineRenderer(target);
        else
            return t.GetComponent<OutterlineRenderer>();
    }

    public static OutterlineRenderer CreateOutlineRenderer(Unit target)
    {
        float x = target.bound.size.x * 2;
        float y = target.bound.size.y * 2;
        RenderTexture rt = EffectRTGenerator.Inst().CreateOutterline(target.gameObject, x, y);

        string path = "Misc/OutterlineRenderer";
        GameObject type = Resources.Load<GameObject>(path);
        if (type == null)
        {
            rt.Release();
            return null;
        }
        GameObject go = GameObject.Instantiate(type);
        go.name = type.name;

        go.GetComponent<OutterlineRenderer>().Init(rt, x, y, Color.white);

        go.transform.parent = target.transform;
        go.transform.localPosition = new Vector3(0, 0, 1f);
        go.transform.localScale = Vector3.one;
        go.transform.localEulerAngles = Vector3.zero;

        return go.GetComponent<OutterlineRenderer>();
    }

    public Color color
    {
        get{
            return gameObject.GetComponent<MeshRenderer>().material.color;
        }
        set{
            gameObject.GetComponent<MeshRenderer>().material.color = value;  
        }

    }

    void Init(RenderTexture rt, float x, float y ,Color color)
    {
        Mesh mesh; 

        MeshRenderer renderer = gameObject.GetComponent<MeshRenderer>(); 
        mesh = GetComponent<MeshFilter>().mesh;

        /* 6 7
         * 4 5
         * 2 3
         * 0 1
         */

        int sectionCount = 1;
        int[] triangles = new int[ sectionCount * 6];  
        Vector3[] vertices = new Vector3[ sectionCount * 4];
        Color[] colors = new Color[ sectionCount * 4];
        Vector2[] uv = new Vector2[ sectionCount * 4];
        for (int i = 0; i < sectionCount; i++)
        {  
            vertices[4 * i] = new Vector2(-x / 2, -y / 2);
            vertices[4 * i + 1] = new Vector2(x / 2, -y / 2);
            vertices[4 * i + 2] = new Vector2(-x / 2, y / 2);
            vertices[4 * i + 3] = new Vector2(x / 2, y / 2);
            colors[4 * i] = Color.white;
            colors[4 * i + 1] = Color.white;
            colors[4 * i + 2] = Color.white;
            colors[4 * i + 3] = Color.white;
            uv[4 * i] = new Vector2(0, 0);
            uv[4 * i + 1] = new Vector2(1, 0);
            uv[4 * i + 2] = new Vector2(0, 1);
            uv[4 * i + 3] = new Vector2(1, 1);
        }  

        for (int i = 0; i < sectionCount; i++)
        {  
            triangles[6 * i] = (i * 4) + 0;  
            triangles[6 * i + 1] = (i * 4) + 3;  
            triangles[6 * i + 2] = (i * 4) + 1; 
            triangles[6 * i + 3] = (i * 4) + 0;  
            triangles[6 * i + 4] = (i * 4) + 2;  
            triangles[6 * i + 5] = (i * 4) + 3; 
        }

        mesh.vertices = vertices;  
        mesh.triangles = triangles;
        mesh.colors = colors;
        mesh.uv = uv;

        m_RT = rt;
        m_Material = new Material(Shader.Find("Outterline/Render"));
        m_Material.hideFlags = HideFlags.HideAndDontSave;
        renderer.material = m_Material;
        renderer.material.color = color;
        renderer.material.mainTexture = rt;

    }

    void OnDestroy()
    {
        DestroyImmediate(m_Material);
        DestroyImmediate(m_Material);
    }

    public void Hide()
    {
        gameObject.SetActive(false);
    }

    public void Show()
    {
        gameObject.SetActive(true);
    }
}
posted @ 2016-07-30 12:52  糍粑大叔  阅读(210)  评论(0编辑  收藏  举报