Unity中实现UI的质感和圆角

质感思路有两种:

一种是玻璃质感的做法,抓取UI后面的图像做模糊(build是GrabPass,urp抓图像我有写过在往期文章),这个方式网络上有很多就不写了;

另外一种是使用CubeMap的方式去模拟质感,这种用贴图的方式会更省性能,我这里主要讲的是第二种,其中需要注意的点是给CubeMap采样的时候需要将顶点转换为世界坐标,不然会出现极坐标的情况(上图为极坐标,下图是正常的);

Unity UI质感和圆角

如果你用shaderGraph可能需要用自定义节点去写转换,黑盒似乎也会出现极坐标,具体的你可以自行测试;

关键代码:

复制代码
vert:

output.worldPos = mul(unity_ObjectToWorld,input.vertex);

----------------------------------------------------------

Frag:
float3 viewDir  = normalize(UnityWorldSpaceViewDir(input.worldPos.xyz));
float3 vrDirWS = reflect(-viewDir, input.worldNormal);
float3 var_Cubemap = texCUBElod(_Cubemap,float4(vrDirWS,6));
color.rgb += var_Cubemap;

全部代码如下:

复制代码
Shader "Unlit/RoundedBoxUI"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)

        _Cubemap ("HDRTex", cube) = "white" {} // 输入HDR单图//YJJ
        //_CubemapMip("_CubemapMip",Range(0,7)) = 6
        _RotationY ("RotationY", Range(0, 360)) = 0

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

	    _ColorMask ("Color Mask", Float) = 15
        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
        _BorderWidth ("Border Width", Float) = 0
        [Enum(NoBorder,0,OnlyBorder,1,Both,2)] _BorderColorType ("Border Type", Int) = 0
        [Toggle(IMAGE_SDF)] _UseImageAsSDF ("Use Image as SDF", Float) = 0
        [Enum(Off,0,On,1)]_ZWrite ("ZWrite", Float) = 1.0
    }
    SubShader
    {
        Tags
	    {
		    "Queue"="Transparent"
		    "IgnoreProjector"="True"
		    "RenderType"="Transparent"
	    }

	    Stencil
	    {
		    Ref [_Stencil]
		    Comp [_StencilComp]
		    Pass [_StencilOp]
		    ReadMask [_StencilReadMask]
		    WriteMask [_StencilWriteMask]
	    }

	    Cull Off
        Lighting Off
        ZWrite [_ZWrite]
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha, OneMinusDstAlpha One
        ColorMask [_ColorMask]

        Pass
        {
            Name "Default"
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
		    #pragma multi_compile __ UNITY_UI_CLIP_RECT
		    #pragma multi_compile __ UNITY_UI_ALPHACLIP
            #pragma multi_compile __ IMAGE_SDF

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"
            #include "Box2DSignedDistance.cginc"

            struct vertexInput
            {
                float4 vertex : POSITION;
                float4 color : COLOR;
                float4 texcoord : TEXCOORD0;
                //--- Custom
                float4 borderRadius : TEXCOORD1;
                float3 normal : NORMAL;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct fragmentInput
            {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                float4 texcoord : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                //--- Custom
                float4 borderRadius : TEXCOORD2;
                float3 worldNormal : TEXCOORD3;
                float3 worldPos : TEXCOORD4;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float _BorderWidth;
            int _BorderColorType;   samplerCUBE _Cubemap; float _RotationY; //float _CubemapMip; 

            float4 RotateAroundYInDegrees (float4 vertex, float degrees)
            {
                float alpha = degrees * 3.14 / 180.0;
                float sina, cosa;
                sincos(alpha, sina, cosa);
                float2x2 m = float2x2(cosa, -sina, sina, cosa);
                return float4(mul(m, vertex.xz), vertex.yw).xzyw;
            }

            float4 RotateAroundXInDegrees (float4 vertex, float degrees)
            {
                float alpha = degrees * 3.14 / 180.0;
                float sina, cosa;
                sincos(alpha, sina, cosa);
            
                // 创建绕X轴的旋转矩阵
                float2x2 m = float2x2(cosa, -sina, sina, cosa);
            
                // 创建完整的旋转矩阵
                float4x4 rotationMatrix = float4x4(
                    1.0, 0.0, 0.0, 0.0,
                    0.0, cosa, -sina, 0.0,
                    0.0, sina, cosa, 0.0,
                    0.0, 0.0, 0.0, 1.0
                );
            
                // 旋转顶点并返回
                return mul(rotationMatrix, vertex);
            }
            
            float4 RotateAroundZInDegrees (float4 vertex, float degrees)
            {
                float alpha = degrees * 3.14 / 180.0;
                float sina, cosa;
                sincos(alpha, sina, cosa);
            
                // 创建绕Z轴的旋转矩阵
                float4x4 rotationMatrix = float4x4(
                    cosa, -sina, 0.0, 0.0,
                    sina, cosa, 0.0, 0.0,
                    0.0, 0.0, 1.0, 0.0,
                    0.0, 0.0, 0.0, 1.0
                );
            
                // 旋转顶点并返回
                return mul(rotationMatrix, vertex);
            }
            

            fragmentInput vert(vertexInput input)
            {
                fragmentInput output;

                UNITY_INITIALIZE_OUTPUT(fragmentInput, output);
                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

                output.worldPosition = input.vertex;

                output.worldPos = mul(unity_ObjectToWorld,input.vertex);//YJJ//给CubeMap采样使用

                output.vertex = UnityObjectToClipPos(output.worldPosition);
                output.texcoord = input.texcoord;
                output.color = input.color * _Color;
                output.borderRadius = input.borderRadius;

                output.worldNormal = UnityObjectToWorldNormal(input.normal);

                return output;
            }

            fixed4 frag (fragmentInput input) : SV_Target
            {
                float2 rectSize = input.texcoord.zw;
                float2 uv = input.texcoord.xy * rectSize;
                uv = uv - rectSize * 0.5;

                float dist = sdRoundBox(uv, rectSize * 0.5 - (_BorderWidth).xx, input.borderRadius);
                float2 ddDist = float2(ddx(dist), ddy(dist));
                float ddDistLen = length(ddDist);

                float alpha = saturate(((dist - _BorderWidth) / ddDistLen) + 1.0);
                float borderParam = saturate((dist) / ddDistLen);

                half4 color = half4(0.0, 0.0, 0.0, 0.0);
                
                #ifdef IMAGE_SDF
                    float4 texSample = tex2D(_MainTex, input.texcoord) + _TextureSampleAdd;
                    float c_dist = texSample.x - 0.1;
                    float c_mask = smoothstep(0.00, 0.2, c_dist);

                    color = input.color;
                    color.a *= 1.0 - alpha;
                    color.a *= saturate(c_mask);
                #else
                    color = (tex2D(_MainTex, input.texcoord) + _TextureSampleAdd) * input.color;
                    color.a *= 1.0 - alpha;
                #endif

                //描边YJJ
                //color.a = 1;//挤出描边

                //描边End
                if (_BorderColorType == 1) {
                    color.a *= borderParam;
                }
                //color.rgb *= 1.0 - borderParam;

                //color.a *= c_dist;
                #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(input.worldPosition.xy, _ClipRect);
                #endif

                #ifdef UNITY_UI_ALPHACLIP
                    clip (color.a - 0.001);
                #endif

                //魔改反射效果
                float3 viewDir  = normalize(UnityWorldSpaceViewDir(input.worldPos.xyz));
                //float3 vDirWS=normalize(_WorldSpaceCameraPos.xyz - input.worldPosition.xyz);
                float3 vrDirWS = reflect(-viewDir, input.worldNormal);

                vrDirWS = RotateAroundZInDegrees(float4(vrDirWS,1),0).xyz;

                float3 var_Cubemap = texCUBElod(_Cubemap,float4(vrDirWS,6));
                color.rgb += var_Cubemap;
                //return float4(var_Cubemap,1);
                return color;
            }
            ENDCG
        }
    }
}

脚本:

复制代码
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class RoundedBoxUIProperties : UIBehaviour, IMeshModifier
{
    private Image _image;
    public Vector4 borderRadius;

    [Range(0,7)]public float CubemapMip = 6;

#if UNITY_EDITOR
    protected override void OnValidate()
    {
        if (_image == null)
        {
            _image = gameObject.GetComponent<Image>();
            if (_image == null) return;
        }
        _image.SetAllDirty();
    }
#endif

    protected override void OnEnable()
    {
        _image = gameObject.GetComponent<Image>();
    }
    protected override void OnDisable()
    {
        _image = null;
    }

    protected override void Start()
    {
        StartCoroutine(DelayVertexGeneration());
    }

    IEnumerator DelayVertexGeneration()
    {
        yield return new WaitForSeconds(0.1f);
        if (_image == null)
        {
            _image = gameObject.GetComponent<Image>();
            if (_image == null) yield break;
        }
        _image.SetAllDirty();
    }

    public void ModifyMesh(Mesh mesh)
    {
    }

    public void ModifyMesh(VertexHelper verts)
    {
        if (_image == null)
        {
            _image = gameObject.GetComponent<Image>();
            if (_image == null) return;
        }

        var rectTransform = (RectTransform)transform;
        var rect = rectTransform.rect;
        var offset = new Vector4(rect.x, rect.y, Mathf.Abs(rect.width), Mathf.Abs(rect.height));
        UIVertex vert = new UIVertex();

        for (int i = 0; i < verts.currentVertCount; i++)
        {
            verts.PopulateUIVertex(ref vert, i);
            var uv0 = vert.uv0;
            uv0.z = offset.z;
            uv0.w = offset.w;
            vert.uv0 = uv0;
            vert.uv1 = borderRadius * 0.5f;
            verts.SetUIVertex(vert, i);
        }
    }

    // [ContextMenu("UI模糊调整")]
    // public void UIMipBlur(){
    //     Shader.SetGlobalFloat("_CubemapMip",CubemapMip);
    //     float Test =  Shader.GetGlobalFloat("_CubemapMip");
    //     Debug.Log("" + Test);
    // }
}

Box2DSignedDistance.cginc

复制代码
float sdRoundBox( in float2 p, in float2 b, in float4 r )
{
	// We choose the radius based on the quadrant we're in
    // We cap the radius based on the minimum of the box half width/height
    r.xy = (p.x>0.0)?r.xy : r.zw;
    r.x = (p.y>0.0)?r.x : r.y;
    r.x = min(2.0f*r.x, min(b.x, b.y));

    float2 q = abs(p)-b+r.x;
    return min(max(q.x,q.y),0.0) + length(max(q,0.0)) - r.x;
}

其中shader部分写了一些矩阵用于调整角度的方法,可以自行删减冗余;

相关推荐
wkm9563 小时前
Unity程序嵌入Qt后点击UI按钮Button没有反应
qt·ui·unity
虾球xz3 小时前
游戏引擎学习第189天
学习·信息可视化·游戏引擎
启诚科技9 小时前
虚拟现实--->unity学习
学习·unity
WarPigs10 小时前
Unity声音管理系统笔记
笔记·unity·音频
虾球xz10 小时前
游戏引擎学习第194天
c++·学习·游戏引擎
Kermit202310 小时前
unity一个图片的物体,会有透明的效果
unity
咩咩觉主11 小时前
Unity 一个丝滑的3D下--XY轴2D平台跳跃--控制器模板(FSM)
3d·unity·游戏引擎
avi911120 小时前
Unity打包崩溃SRP-URP-管线的问题:Shader::SRPBatcherInfoSetup()
unity·android studio·调试·crash·崩溃
徐子竣1 天前
Unity编辑器功能及拓展(2) —Gizmos编辑器绘制功能
unity·编辑器·游戏引擎
Tatalaluola1 天前
【Unity】 鼠标拖动物体移动速度跟不上鼠标,会掉落
学习·unity·c#·游戏引擎