IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity中实现TextOutline -> 正文阅读

[游戏开发]Unity中实现TextOutline

一:效果演示


?二:使用


Outline Color:描边颜色
Outline Width:描边宽度


三:为什么尽量避免使用UGUI的Outline和Shadaw组件?

下面是Outline的源码,不论是Outline还是Shadow,它的实现原理都是把原来的顶点拷贝出来一份,根据偏移量来设置拷贝出来的顶点位置,相当于复制出一个一模一样的Text组件进行位置偏移。Shadow拷贝一份,Outline拷贝四份

using System.Collections.Generic;

namespace UnityEngine.UI
{
    [AddComponentMenu("UI/Effects/Outline", 15)]
    /// <summary>
    /// Adds an outline to a graphic using IVertexModifier.
    /// </summary>
    public class Outline : Shadow
    {
        protected Outline()
        {}

        public override void ModifyMesh(VertexHelper vh)
        {
            if (!IsActive())
                return;

            var verts = ListPool<UIVertex>.Get();
            vh.GetUIVertexStream(verts);

            var neededCpacity = verts.Count * 5;
            if (verts.Capacity < neededCpacity)
                verts.Capacity = neededCpacity;

            var start = 0;
            var end = verts.Count;
            ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, effectDistance.y);

            start = end;
            end = verts.Count;
            ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, -effectDistance.y);

            start = end;
            end = verts.Count;
            ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, effectDistance.y);

            start = end;
            end = verts.Count;
            ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, -effectDistance.y);

            vh.Clear();
            vh.AddUIVertexTriangleStream(verts);
            ListPool<UIVertex>.Release(verts);
        }
    }
}

四:代码实现

Shadar:

Shader "UI/TextOutline" 
{
    Properties
    {
        _MainTex ("Main Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1, 1, 1, 1)
        //_OutlineColor ("Outline Color", Color) = (1, 1, 1, 1)
        //_OutlineWidth ("Outline Width", Int) = 1

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }
        
        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp] 
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            Name "OUTLINE"

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            //Add for RectMask2D  
            #include "UnityUI.cginc"
            //End for RectMask2D  

            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _MainTex_TexelSize;

            //float4 _OutlineColor;
            //int _OutlineWidth;

            //Add for RectMask2D  
            float4 _ClipRect;
            //End for RectMask2D

			struct appdata
			{
				float4 vertex : POSITION;
				float4 tangent : TANGENT;
				float4 normal : NORMAL;
				float2 texcoord : TEXCOORD0;
				float2 uv1 : TEXCOORD1;
				float2 uv2 : TEXCOORD2;
				float2 uv3 : TEXCOORD3;
				fixed4 color : COLOR;
			};


            struct v2f
            {
                float4 vertex : SV_POSITION;
				float4 tangent : TANGENT;
				float4 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
				float2 uv2 : TEXCOORD2;
				float2 uv3 : TEXCOORD3;
                //Add for RectMask2D  
                float4 worldPosition : TEXCOORD4;
                //End for RectMask2D
                fixed4 color : COLOR;
            };

            v2f vert(appdata IN)
            {
                v2f o;

                //Add for RectMask2D  
                o.worldPosition = IN.vertex;
                //End for RectMask2D 

                o.vertex = UnityObjectToClipPos(IN.vertex);
                o.tangent = IN.tangent;
                o.texcoord = IN.texcoord;
                o.color = IN.color * _Color;
				o.uv1 = IN.uv1;
				o.uv2 = IN.uv2;
				o.uv3 = IN.uv3;
				o.normal = IN.normal;

                return o;
            }
			/*
            fixed IsInRect(float2 pPos, float4 pClipRect)
            {
                pPos = step(pClipRect.xy, pPos) * step(pPos, pClipRect.zw);
                return pPos.x * pPos.y;
            }
			*/
			fixed IsInRect(float2 pPos, float2 pClipRectMin, float2 pClipRectMax)
			{
				pPos = step(pClipRectMin, pPos) * step(pPos, pClipRectMax);
				return pPos.x * pPos.y;
			}

			fixed SampleAlpha(int pIndex, v2f IN)
			{
				const fixed sinArray[12] = { 0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5 };
				const fixed cosArray[12] = { 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5, 0, 0.5, 0.866 };
				float2 pos = IN.texcoord + _MainTex_TexelSize.xy * float2(cosArray[pIndex], sinArray[pIndex]) * IN.normal.z;	//normal.z 存放 _OutlineWidth
				return IsInRect(pos, IN.uv1, IN.uv2) * (tex2D(_MainTex, pos) + _TextureSampleAdd).w * IN.tangent.w;		//tangent.w 存放 _OutlineColor.w
			}

			fixed4 frag(v2f IN) : SV_Target
			{
				fixed4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
				if (IN.normal.z > 0)	//normal.z 存放 _OutlineWidth
				{
					color.w *= IsInRect(IN.texcoord, IN.uv1, IN.uv2);	//uv1 uv2 存着原始字的uv长方形区域大小
					half4 val = half4(IN.uv3.x, IN.uv3.y, IN.tangent.z, 0);		//uv3.xy tangent.z 分别存放着 _OutlineColor的rgb

					val.w += SampleAlpha(0, IN);
					val.w += SampleAlpha(1, IN);
					val.w += SampleAlpha(2, IN);
					val.w += SampleAlpha(3, IN);
					val.w += SampleAlpha(4, IN);
					val.w += SampleAlpha(5, IN);
					val.w += SampleAlpha(6, IN);
					val.w += SampleAlpha(7, IN);
					val.w += SampleAlpha(8, IN);
					val.w += SampleAlpha(9, IN);
					val.w += SampleAlpha(10, IN);
					val.w += SampleAlpha(11, IN);

					color = (val * (1.0 - color.a)) + (color * color.a);

                    color.a *= IN.color.a*IN.color.a*IN.color.a;	//字逐渐隐藏时,描边也要隐藏

				}

                //Add for RectMask2D 
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#ifdef UNITY_UI_ALPHACLIP
                clip(color.a - 0.001);
#endif
                //End for RectMask2D 

				return color;
			}

            ENDCG
        }
    }
}

CS:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[AddComponentMenu("LFramework/UI/Effect/TextOutline", 0)]
public class TextOutline : BaseMeshEffect
{
    protected TextOutline()
    {

    }

    //描边颜色
    public Color m_OutlineColor = Color.white;
    //描边宽度
    public float m_OutlineWidth = 1;

    private static List<UIVertex> m_VetexList = new List<UIVertex>();

    protected override void Awake()
    {
        base.Awake();

        if (graphic)
        {
            if (graphic.material == null
                || graphic.material.shader.name != "UI/TextOutline")
            {
                Debug.LogError("没有找到材质TextOutline.mat");
            }

            if (graphic.canvas)
            {
                var v1 = graphic.canvas.additionalShaderChannels;
                var v2 = AdditionalCanvasShaderChannels.TexCoord1;
                if ((v1 & v2) != v2)
                {
                    graphic.canvas.additionalShaderChannels |= v2;
                }
                v2 = AdditionalCanvasShaderChannels.TexCoord2;
                if ((v1 & v2) != v2)
                {
                    graphic.canvas.additionalShaderChannels |= v2;
                }
                v2 = AdditionalCanvasShaderChannels.TexCoord3;
                if ((v1 & v2) != v2)
                {
                    graphic.canvas.additionalShaderChannels |= v2;
                }
                v2 = AdditionalCanvasShaderChannels.Tangent;
                if ((v1 & v2) != v2)
                {
                    graphic.canvas.additionalShaderChannels |= v2;
                }
                v2 = AdditionalCanvasShaderChannels.Normal;
                if ((v1 & v2) != v2)
                {
                    graphic.canvas.additionalShaderChannels |= v2;
                }
            }
            Refresh();
        }
    }

#if UNITY_EDITOR
    protected override void OnValidate()
    {
        base.OnValidate();

        if (graphic.material != null)
        {
            if (graphic.material.shader.name != "UI/TextOutline")
            {
                Debug.LogError("没有找到材质TextOutline.mat");
            }
            Refresh();
        }
    }
#endif

    private void Refresh()
    {
        graphic.SetVerticesDirty();
    }

    public override void ModifyMesh(VertexHelper vh)
    {
        vh.GetUIVertexStream(m_VetexList);

        ProcessVertices();

        vh.Clear();
        vh.AddUIVertexTriangleStream(m_VetexList);
    }

    private void ProcessVertices()
    {
        for (int i = 0, count = m_VetexList.Count - 3; i <= count; i += 3)
        {
            var v1 = m_VetexList[i];
            var v2 = m_VetexList[i + 1];
            var v3 = m_VetexList[i + 2];

            //计算原顶点坐标中心点
            var minX = Min(v1.position.x, v2.position.x, v3.position.x);
            var minY = Min(v1.position.y, v2.position.y, v3.position.y);
            var maxX = Max(v1.position.x, v2.position.x, v3.position.x);
            var maxY = Max(v1.position.y, v2.position.y, v3.position.y);
            var posCenter = new Vector2(minX + maxX, minY + maxY) * 0.5f;

            //计算原始顶点坐标和UV的方向
            Vector2 triX, triY, uvX, uvY;
            Vector2 pos1 = v1.position;
            Vector2 pos2 = v2.position;
            Vector2 pos3 = v3.position;
            if (Mathf.Abs(Vector2.Dot((pos2 - pos1).normalized, Vector2.right))
                > Mathf.Abs(Vector2.Dot((pos3 - pos2).normalized, Vector2.right)))
            {
                triX = pos2 - pos1;
                triY = pos3 - pos2;
                uvX = v2.uv0 - v1.uv0;
                uvY = v3.uv0 - v2.uv0;
            }
            else
            {
                triX = pos3 - pos2;
                triY = pos2 - pos1;
                uvX = v3.uv0 - v2.uv0;
                uvY = v2.uv0 - v1.uv0;
            }

            //计算原始uv框
            var uvMin = Min(v1.uv0, v2.uv0, v3.uv0);
            var uvMax = Max(v1.uv0, v2.uv0, v3.uv0);

            //OutlineColor和OutlineWidth也传入,避免出现不一样的材质球
            var col_rg = new Vector2(m_OutlineColor.r, m_OutlineColor.g);//描边颜色用uv3和tangent的zw传递
            var col_ba = new Vector4(0, 0, m_OutlineColor.b, m_OutlineColor.a);
            var normal = new Vector3(0, 0, m_OutlineWidth);//描边的宽度用normal的z传递

            //为每一个顶点设置新的Position和UV,并传入原始UV框
            v1 = SetNewPosAndUV(v1, m_OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
            v1.uv3 = col_rg;
            v1.tangent = col_ba;
            v1.normal = normal;
            v2 = SetNewPosAndUV(v2, m_OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
            v2.uv3 = col_rg;
            v2.tangent = col_ba;
            v2.normal = normal;
            v3 = SetNewPosAndUV(v3, m_OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
            v3.uv3 = col_rg;
            v3.tangent = col_ba;
            v3.normal = normal;

            //应用设置后的UIVertex
            m_VetexList[i] = v1;
            m_VetexList[i + 1] = v2;
            m_VetexList[i + 2] = v3;
        }
    }

    private static UIVertex SetNewPosAndUV(UIVertex pVertex, float pOutLineWidth,
        Vector2 pPosCenter,
        Vector2 pTriangleX, Vector2 pTriangleY,
        Vector2 pUVX, Vector2 pUVY,
        Vector2 pUVOriginMin, Vector2 pUVOriginMax)
    {
        //Position
        var pos = pVertex.position;
        var posXOffset = pos.x > pPosCenter.x ? pOutLineWidth : -pOutLineWidth;
        var posYOffset = pos.y > pPosCenter.y ? pOutLineWidth : -pOutLineWidth;
        pos.x += posXOffset;
        pos.y += posYOffset;
        pVertex.position = pos;
        //UV
        var uv = pVertex.uv0;
        uv += pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1);
        uv += pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1);
        pVertex.uv0 = uv;

        pVertex.uv1 = pUVOriginMin;
        pVertex.uv2 = pUVOriginMax;

        return pVertex;
    }

    private static float Min(float pA, float pB, float pC)
    {
        return Mathf.Min(Mathf.Min(pA, pB), pC);
    }

    private static float Max(float pA, float pB, float pC)
    {
        return Mathf.Max(Mathf.Max(pA, pB), pC);
    }

    private static Vector2 Min(Vector2 pA, Vector2 pB, Vector2 pC)
    {
        return new Vector2(Min(pA.x, pB.x, pC.x), Min(pA.y, pB.y, pC.y));
    }

    private static Vector2 Max(Vector2 pA, Vector2 pB, Vector2 pC)
    {
        return new Vector2(Max(pA.x, pB.x, pC.x), Max(pA.y, pB.y, pC.y));
    }
}

五:自己编写UGUI的Outline组件

与UGUI实现的思路相同

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[AddComponentMenu("LFramework/UI/Effect/TextOutline", 0)]
public class TextOutline: BaseMeshEffect
{
    protected TextOutline()
    {

    }
    
    //描边颜色
    public Color m_OutlineColor = Color.white;
    //描边宽度
    public float m_OutlineWidth = 1;

    List<UIVertex> m_VetexList = new List<UIVertex>();

    public override void ModifyMesh(VertexHelper vh)
    {
        vh.GetUIVertexStream(m_VetexList);

        int startIndex = 0;
        int endIndex = m_VetexList.Count;
        ProcessVertex(m_VetexList, startIndex, endIndex, m_OutlineWidth, 0);
        startIndex = endIndex;
        endIndex = m_VetexList.Count;
        ProcessVertex(m_VetexList, startIndex, endIndex, -m_OutlineWidth, 0);
        startIndex = endIndex;
        endIndex = m_VetexList.Count;
        ProcessVertex(m_VetexList, startIndex, endIndex, 0, m_OutlineWidth);
        startIndex = endIndex;
        endIndex = m_VetexList.Count;
        ProcessVertex(m_VetexList, startIndex, endIndex, 0, -m_OutlineWidth);

        vh.Clear();
        vh.AddUIVertexTriangleStream(m_VetexList);
        m_VetexList.Clear();
    }

    void ProcessVertex(List<UIVertex> vertexList, int startIndex, int endIndex, float x, float y)
    {
        UIVertex uiVertex;
        for (int i = startIndex; i < endIndex; i++)
        {
            uiVertex = vertexList[i];

            vertexList.Add(uiVertex);
            Vector3 offset = new Vector3(x, y);
            uiVertex.position += offset;
            uiVertex.color = m_OutlineColor;
            vertexList[i] = uiVertex;
        }
    }
}

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-01-12 00:22:24  更:2022-01-12 00:22:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 19:54:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码