UGUI描边

Unity UGUI的Outl通用化的推荐能力ine(描边)组件的介绍及使用 新增数据源 阅读详情

实现描边

采样坐标用圆的参数方程计算

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]) * _OutlineWidth;
    return (tex2D(_MainTex, pos) + _TextureSampleAdd).w * _OutlineColor.w;
}

像素着色器

fixed4 frag(v2f IN) : SV_Target
{
    fixed4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
 
    half4 val = half4(_OutlineColor.x, _OutlineColor.y, _OutlineColor.z, 0);
	// 注意:这里为了简化代码用了循环
	// 尽量不要在Shader中使用循环,多复制几次代码都行
	for (int i = 0; i < 12; i++)
	{
		val.w += SampleAlpha(i, IN);
	}
	color = (val * (1.0 - color.a)) + (color * color.a);
 
	return color;
}

超出图片范围的部分被裁减掉了,我们需要对图片的区域进行调整,保证描边的部分也被包含在区域内。

在C#层进行区域扩展

要扩展区域,就得修改顶点。Unity提供了BaseMeshEffect类供开发者对UI组件的顶点进行修改。

获取顶点

获取到所有的顶点信息

public class OutlineEx : BaseMeshEffect
{
    public Color OutlineColor = Color.white;
    [Range(0, 6)]
    public int OutlineWidth = 0;
 
    private static List<UIVertex> m_VetexList = new List<UIVertex>();
 
 
    protected override void Awake()
    {
        base.Awake();
 
        var shader = Shader.Find("TSF Shaders/UI/OutlineEx");
        base.graphic.material = new Material(shader);
 
        var v1 = base.graphic.canvas.additionalShaderChannels;
        var v2 = AdditionalCanvasShaderChannels.Tangent;
        if ((v1 & v2) != v2)
        {
            base.graphic.canvas.additionalShaderChannels |= v2;
        }
        this._Refresh();
    }
 
 
#if UNITY_EDITOR
    protected override void OnValidate()
    {
        base.OnValidate();
 
        if (base.graphic.material != null)
        {
            this._Refresh();
        }
    }
#endif
 
 
    private void _Refresh()
    {
        base.graphic.material.SetColor("_OutlineColor", this.OutlineColor);
        base.graphic.material.SetInt("_OutlineWidth", this.OutlineWidth);
        base.graphic.SetVerticesDirty();
    }
 
 
    public override void ModifyMesh(VertexHelper vh)
    {
        vh.GetUIVertexStream(m_VetexList);
 
        this._ProcessVertices();
 
        vh.Clear();
        vh.AddUIVertexTriangleStream(m_VetexList);
    }
 
 
    private void _ProcessVertices()
    {
        // TODO: 处理顶点
    }
}

进行外扩

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;
        }
        // 为每个顶点设置新的Position和UV
        //
        v1 = _SetNewPosAndUV(v1, this.OutlineWidth, posCenter, triX, triY, uvX, uvY);
        v2 = _SetNewPosAndUV(v2, this.OutlineWidth, posCenter, triX, triY, uvX, uvY);
        v3 = _SetNewPosAndUV(v3, this.OutlineWidth, posCenter, triX, triY, uvX, uvY);
        // 应用设置后的UIVertex
        //
        m_VetexList[i] = v1;
        m_VetexList[i + 1] = v2;
        m_VetexList[i + 2] = v3;
    }
}
 
 
private static UIVertex _SetNewPosAndUV(UIVertex pVertex, int pOutLineWidth,
    Vector2 pPosCenter,
    Vector2 pTriangleX, Vector2 pTriangleY,
    Vector2 pUVX, Vector2 pUVY)
{
    // 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;
 
    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);
}

在这里插入图片描述

UV裁剪,排除不需要的像素

UV裁剪框就相当于图集中每个小图的范围。直接扩大必然会包含到小图邻接的图的像素。所以这一步我们需要对最终绘制出的图进行裁剪,保证这些不要的像素不被画出来。
裁剪的逻辑也很简单。如果该像素处于被扩大前的UV范围外,则设置它的alpha为0


UIVertex结构体

public struct UIVertex
{
    public static UIVertex simpleVert;
    public Vector3 position;
    public Vector3 normal;
    public Color32 color;
    public Vector2 uv0;
    public Vector2 uv1;
    public Vector2 uv2;
    public Vector2 uv3;
    public Vector4 tangent;
}

将原始UV框赋值给uv1和uv2成员

var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0);
var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0);
vertex.uv1 = new Vector2(pUVOrigin.x, pUVOrigin.y);
vertex.uv2 = new Vector2(pUVOrigin.z, pUVOrigin.w);
 
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));
}

在这里插入图片描述

最终代码

c#

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
 
 
namespace TooSimpleFramework.UI
{
    /// <summary>
    /// UGUI描边
    /// </summary>
    public class OutlineEx : BaseMeshEffect
    {
        public Color OutlineColor = Color.white;
        [Range(0, 6)]
        public int OutlineWidth = 0;
 
        private static List<UIVertex> m_VetexList = new List<UIVertex>();
 
 
        protected override void Start()
        {
            base.Start();
 
            var shader = Shader.Find("TSF Shaders/UI/OutlineEx");
            base.graphic.material = new Material(shader);
 
            var v1 = base.graphic.canvas.additionalShaderChannels;
            var v2 = AdditionalCanvasShaderChannels.TexCoord1;
            if ((v1 & v2) != v2)
            {
                base.graphic.canvas.additionalShaderChannels |= v2;
            }
            v2 = AdditionalCanvasShaderChannels.TexCoord2;
            if ((v1 & v2) != v2)
            {
                base.graphic.canvas.additionalShaderChannels |= v2;
            }
 
            this._Refresh();
        }
 
 
#if UNITY_EDITOR
        protected override void OnValidate()
        {
            base.OnValidate();
 
            if (base.graphic.material != null)
            {
                this._Refresh();
            }
        }
#endif
 
 
        private void _Refresh()
        {
            base.graphic.material.SetColor("_OutlineColor", this.OutlineColor);
            base.graphic.material.SetInt("_OutlineWidth", this.OutlineWidth);
            base.graphic.SetVerticesDirty();
        }
 
 
        public override void ModifyMesh(VertexHelper vh)
        {
            vh.GetUIVertexStream(m_VetexList);
 
            this._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);
                var uvOrigin = new Vector4(uvMin.x, uvMin.y, uvMax.x, uvMax.y);
                // 为每个顶点设置新的Position和UV,并传入原始UV框
                //
                v1 = _SetNewPosAndUV(v1, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
                v2 = _SetNewPosAndUV(v2, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
                v3 = _SetNewPosAndUV(v3, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
                // 应用设置后的UIVertex
                //
                m_VetexList[i] = v1;
                m_VetexList[i + 1] = v2;
                m_VetexList[i + 2] = v3;
            }
        }
 
 
        private static UIVertex _SetNewPosAndUV(UIVertex pVertex, int pOutLineWidth,
            Vector2 pPosCenter,
            Vector2 pTriangleX, Vector2 pTriangleY,
            Vector2 pUVX, Vector2 pUVY,
            Vector4 pUVOrigin)
        {
            // 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;
            // 原始UV框
            pVertex.uv1 = new Vector2(pUVOrigin.x, pUVOrigin.y);
            pVertex.uv2 = new Vector2(pUVOrigin.z, pUVOrigin.w);
 
            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));
        }
    }
}

Shader

Shader "TSF Shaders/UI/OutlineEx" 
{
    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
 
			sampler2D _MainTex;
			fixed4 _Color;
			fixed4 _TextureSampleAdd;
			float4 _MainTex_TexelSize;
 
			float4 _OutlineColor;
			int _OutlineWidth;
 
			struct appdata
			{
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
                float2 texcoord1 : TEXCOORD1;
                float2 texcoord2 : TEXCOORD2;
				fixed4 color : COLOR;
			};
 
			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 texcoord : TEXCOORD0;
                float2 uvOriginXY : TEXCOORD1;
                float2 uvOriginZW : TEXCOORD2;
				fixed4 color : COLOR;
			};
 
			v2f vert(appdata IN)
			{
				v2f o;
 
				o.vertex = UnityObjectToClipPos(IN.vertex);
				o.texcoord = IN.texcoord;
                o.uvOriginXY = IN.texcoord1;
                o.uvOriginZW = IN.texcoord2;
				o.color = IN.color * _Color;
 
				return o;
			}
 
			fixed IsInRect(float2 pPos, float2 pClipRectXY, float2 pClipRectZW)
			{
				pPos = step(pClipRectXY, pPos) * step(pPos, pClipRectZW);
				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]) * _OutlineWidth;
				return IsInRect(pos, IN.uvOriginXY, IN.uvOriginZW) * (tex2D(_MainTex, pos) + _TextureSampleAdd).w * _OutlineColor.w;
            }
 
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
                if (_OutlineWidth > 0) 
                {
                    color.w *= IsInRect(IN.texcoord, IN.uvOriginXY, IN.uvOriginZW);
                    half4 val = half4(_OutlineColor.x, _OutlineColor.y, _OutlineColor.z, 0);
 
                    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);
 
                    val.w = clamp(val.w, 0, 1);
                    color = (val * (1.0 - color.a)) + (color * color.a);
                }
                return color;
            }
            ENDCG
        }
    }
}

在这里插入图片描述

优化

仔细观察上面最终效果的Ass,可以发现它们的字符本身被后一个字符的描边覆盖了一部分。使用两个Pass可以解决,一个只绘制描边,另一个只绘制本身
Pass1

fixed4 frag(v2f IN) : SV_Target
{
    // 省略
    val.w = clamp(val.w, 0, 1);
    return val;
}

Pass2

fixed4 frag(v2f IN) : SV_Target
{
    fixed4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
    color.w *= IsInRect(IN.texcoord, IN.uvOriginXY, IN.uvOriginZW);
    return color;
}
Unity UGUI的Outline(描边)组件的介绍及使用所以说是玩具, 相当于利用泛型函数保存了类型信息 阅读详情

相关推荐

unity 给图片边缘_unity探索者之UGUI图片描边

自从UGUI出现之后,我就已经放弃使用NGUI了,原因不多说,毕竟是亲爹产的不过即使如此,UGUI也存在不少问题,很多组件也不完善今天要说的就是Outline这个组件,相信不少朋友都使用过,也相信不少朋友都和我一样,对于这个组件十分恶心比如,我们给下面这张图添加一个描边诶,效果还不错哦,我们再换一张恩,我觉得应该没有美工能接受这样的描边,就算你叫他美术都不好使这个问题要解决,怎么办好呢?我们先...

weixin_39945531的博客 1120

unity 继承ui Shadow的外描边测试

unity 继承ui Shadow的外描边测试

weixin_38926960的博客 369

Unity中实现UI描边

一:为什么尽量避免使用大量UGUI的Outline和Shadaw组件 下面是Outline的源码,不论是Outline还是Shadow,它的实现原理是把原来的顶点拷贝出来一份,根据偏移量来设置拷贝出来的顶点位置。Shadow拷贝一份,Outline拷贝四份 using System.Collections.Generic; namespace UnityEngine.UI { [AddComponentMenu("UI/Effects/Outline", 15)] /// <s

L的博客 6524

Unity UGUI的Outline(描边)组件的介绍及使用

Outline(描边)组件是Unity UGUI中的一种特效组件,用于给UI元素添加描边效果。通过设置描边的颜色、宽度和模糊程度,可以使UI元素在视觉上更加突出。

alianblank的博客 5026

【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效,描边及阴影特效

在Unity中 UGUI 的使用几乎是必不可少的,虽然也有NGUI、FGUI等可以使用,但使用UGUI的还是占多数。 而在使用UGUI的过程中,除了一些基础的UI交互页面及逻辑,学习一些UGUI特效的使用也是很有必要的。 接下来会分享一些UGUI中比较简单且实用的特效,供有需要的小伙伴学习使用。

努力前行,总会成为自己心中的那道光 4万+

别再混淆了!5分钟搞懂机器人导纳控制与阻抗控制的本质区别(附Simulink对比仿真)

本文深入解析机器人柔顺控制中的导纳控制与阻抗控制的本质区别,通过信号流方向、架构对比和应用场景三个关键视角进行详细阐述。文章还提供Simulink对比仿真案例,帮助读者快速理解两种控制策略的差异及其适用场景,特别适合机器人控制领域的研究者和工程师参考。

weixin_42543046的博客 606

UGUI优化之路- Text的文字描边优化

UGUI自带的Text组件依然是最常用的文本组件,但自带的描边效果却不尽人意,性能差且描边毛刺多,因此实际项目中经常需要对描边效果进行优化才能满足对高品质UI对要求

走走走~ 3413

解决uc浏览器劫持video标签问题

解决uc视频悬浮播放 ,video标签被浏览器劫持 ,vide0适配

smile_y_y的博客 3452

unity探索者之UGUI图片描边

版权声明:本文为原创文章,转载请声明https://www.cnblogs.com/unityExplorer/p/13524270.html 自从UGUI出现之后,我就已经放弃使用NGUI了,原因不多说,毕竟是亲爹产的 不过即使如此,UGUI也存在不少问题,很多组件也不完善 今天要说的就是Outline这个组件,相信不少朋友都使用过,也相信不少朋友都和我一样,对于这个组件十分恶心 比如...

UnityExplorer的博客 2541

从零到一:用AXI DataMover在Zynq MPSoC上实现高效DMA传输(附Vitis HLS驱动示例)

本文详细介绍了如何在Xilinx Zynq UltraScale+ MPSoC平台上使用AXI DataMover实现高效的DMA数据传输。通过解析AXI DataMover架构、Vivado工程配置、Vitis HLS驱动开发及性能优化技巧,帮助开发者快速构建高性能数据传输通道,并提供了可直接复用的代码示例。

weixin_42520796的博客 427

FFTW库安装与使用(3.3.5版本)

FFTW库安装与使用一、FFTW库介绍与下载二、FFTW库安装三、FFTW库测试 一、FFTW库介绍与下载   FFTW ( the Faster Fourier Transform in the West) 是一个快速计算离散傅里叶变换的标准C语言程序集,其由MIT的M.Frigo 和S. Johnson 开发。可计算一维或多维实和复数据以及任意规模的DFT,且运行速度比Eigen和opencv自带的FFT库函数快10倍以上。   下载链接:http://www.fftw.org/install/wind

Jhon_ranble的博客 2万+

UGUI文本描边

实现UGUI的文本描边

a_codecat的博客 639

R语言实战应用精讲50篇(二)-多重线性回归系列之稳健回归

前言 线性回归的参数估计主要采用的是最小二乘法(又称最小平方法),该法是将使观测值与模型预测值之差的平方达到最小的值作为参数估计值。如果数据存在异常点或者异方差,最小二乘法估计会存在偏差,常用的处理策略就是改用稳健回归(Robust Regression)。 稳健回归就是采用更为稳健的参数估计方法来拟合模型,所谓稳健就是数据的波动对参数估计影响不大,比如不受异常值影响,或删除某一观测后对结果影响不大。稳健估计的方法有很多,主要有这么几类:最小绝对值回归(也称最小一乘法回归,lAV),最小中位数二乘法(L

wenyusuran的专栏 3161

分享一个UGUI文本描边的方案

众所周知,Unity自带的文本描边outline组件比较鸡肋,当描边粗细比较小的时候,效果还好,一旦超过2,那就会产生断断续续的效果原因是它的实现原理其实就是上下左右偏移出一定距离(描边粗细)各自在渲染一遍文本,在把效果叠加起来,在粗细比较小的时候,就是描边效果,但是粗细比较大的时候就穿帮了。然而有时候细细的描边并不能满足美术大佬的需求,这时候要怎么办?主流的办法是使用TextMeshPro,用它的话确实可以实现好看的描边以及其他好看的效果。

qq_27275225的博客 2804

Vue3 + uniapp 踩坑:iOS 真机调试一直报错Maximum call stack size exceeded,和(in promise)MiniProgramError err的解决方法

不是你写错代码,是微信开发工具和 iOS 真机调试的兼容问题。开个空项目都报错,勾个设置就没问题,不是你,是工具的锅。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 1267

Qt6.10 | Qt Bluetooth 蓝牙

本文介绍了QtBluetooth模块的功能和使用方法,包括蓝牙API概述、构建配置(CMake/qmake)、权限管理、平台支持(Linux/macOS特殊处理)、日志设置及许可信息。重点说明Linux平台的后端选择机制,以及macOS上的依赖限制。最后提及蓝牙聊天示例,展示RFCOMM协议通信的实现。全文提供了蓝牙开发的技术要点和注意事项。

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 400

UGUI的Text描边Outline拓展

UGUI自带的Text,用outline做描边效果很不好 Github上有人做了拓展,通过复制顶点的方式,不过这种方式很容易爆顶点数。 Github地址:https://github.com/n-yoda/unity-vertex-effects

linxinfa的专栏 4270

微软商店报错“此产品当前不可用”?终极修复指南

微软商店报错“此产品当前不可用”?终极修复指南

安德洛夫 3814

unity ugui自定义Text字体描边优化性能

**在unity的ugui的用于Text描边的组件outline中,其中的实现是将字体copy出几份往外放置形成背景黑边,随之带来的是性能的高消耗。如果是一两个Text描边还好,如果同屏有大量Text在使用其在描边时,其性能消耗提高十分明显,甚至直接影响到了游戏FPS。 因此,同屏Text描边多时有必要优化一下,因此写一个可以实现Text描边的shader。 一样的道理,shader中有2个Pas...

u012740992的专栏 1万+

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

基于Shader实现的UGUI描边解决方案

基于Shader实现的UGUI描边解决方案 - GuyaWeiren - 博客园

山南hsy 356

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

【Unity】用shader做UGUI字体描边的算法改进

Unity自带的Outline 效率实在太差。 找了基于Shader实现的UGUI描边解决方案http://www.cnblogs.com/GuyaWeiren/p/9665106.html 效果还不错,overdraw和顶点数都不高。 不过方案有2个问题,自己尝试调整了一下, 代码放在这以供参考吧。 (注:把ui存储prefab,运行时动态挂到一个canvas上, 这个canvas 如果...

zhenmu的专栏 1万+

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Unity2017之后Shader实现的UGUI外描边方案:让UI更具立体感

Unity2017之后Shader实现的UGUI外描边方案:让UI更具立体感 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在Unity开发中,UI设计是用户体验的关键组成部分。为了让UI元素更加醒目和立体,描边(Outline)效果常被用于突出显示。然而,Unity自带的传统描边方案在性能上存在一定瓶颈,尤其是在顶点数过多和overdraw过大的情况下。Unity2...

gitblog_06726的博客 1151

Unity UGUI Outline组件:从原理到实战的描边效果全解析

本文全面解析Unity UGUI Outline组件的原理与实战应用,详细介绍了描边效果的实现方式、参数调校技巧及性能优化方法。通过实际案例展示如何实现按钮特效和文字描边效果,并提供避坑指南,帮助开发者高效使用Outline组件提升UI视觉效果。

weixin_30924087的博客 340
上一篇: shader实现复杂的星空效果
下一篇: UGUI文本描边
a_codecat
博客等级 码龄4年 8粉丝 112原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值