zoukankan      html  css  js  c++  java
  • 糍粑大叔的独游之旅-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);
        }
    }
  • 相关阅读:
    从零开始入门 K8s| 详解 Pod 及容器设计模式
    从零开始入门 K8s| 阿里技术专家详解 K8s 核心概念
    时间和空间的完美统一!阿里云时空数据库正式商业化
    SaaS加速器,到底加速了谁? 剖析阿里云的SaaS战略:企业和ISV不可错过的好文
    来杭州云栖大会,全面了解企业如何实现云上IT治理
    DataV教你如何给可视化应用一键美颜
    Serverless Kubernetes全面升级2.0架构:支持多命名空间、RBAC、CRD、PV/PVC等功能
    基于 APIGateway 打造生产级别的 Knative 服务
    P1434 滑雪
    P1613 跑路
  • 原文地址:https://www.cnblogs.com/uncleciba/p/5721352.html
Copyright © 2011-2022 走看看