如何在2D ui界面模拟3D的透视效果

如何在2D ui界面模拟3D的透视效果,或许通过缩放UI元素,远小近大也可以实现。

一种更好的方法是shader中使用透视矩阵来渲染,透视矩阵如下:

透视矩阵

 参考shader如下:

Shader "UI/Test3D"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,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
        
        //透视
        [Space(20)]
        [Toggle] _IsPerspective("是否透视", float) = 0
        _VSRotateAngle("旋转角度",float) = 35
        _VSPosOffset("位置偏移", Vector) = (0, 0, 0, 0)
        _CotHalfFov("Cot Half Fov",float) = 1.732051
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderPipeline"="UniversalPipeline"
            "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 One OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            Name "Default"
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment pixel
            #pragma target 2.0

            #pragma multi_compile_local _ UNITY_UI_CLIP_RECT
            #pragma multi_compile_local _ UNITY_UI_ALPHACLIP

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float4 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                float4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                half4  mask : TEXCOORD2;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);
            float4 _TextureSampleAdd;
            float4 _ClipRect;
            float _UIMaskSoftnessX;
            float _UIMaskSoftnessY;
            CBUFFER_START(UnityPerMaterial)
            float4 _Color;
            float4 _MainTex_ST;
            CBUFFER_END
            
            float _IsPerspective;
            float _VSRotateAngle;
            float4 _VSPosOffset;
            float _CotHalfFov;

            float4 TransformObjectToHClipPerspective(float4 vertex)
            {
                float4 posVS = mul(UNITY_MATRIX_MV, float4(vertex.xyz, 1.0));
                float4x4 pMatrix;
                if (_IsPerspective > 0)
                {
                    posVS.xyz = RotateAroundByDegree(-_VSRotateAngle, posVS.xyz);
                    posVS += _VSPosOffset;
                    /*
                     * 透视投影矩阵
                     * ugui canvas overlay mode, projection camera,fov:60,near:1,far:8000
                     * _VSRotateAngle:35, _VSPosOffset:Vector4(0,1635,-3327,0)
                     */
                    float aspect = _ScreenParams.y / _ScreenParams.x;
                    pMatrix = float4x4(_CotHalfFov * aspect,    0,    0,    0,
                                        0,    _CotHalfFov,    0,    0,
                                        0,    0,    -1.00025,    -2.00025,
                                        0,    0,    -1,    0);
                }
                else
                {
                    pMatrix = UNITY_MATRIX_P;
                }
                return mul(pMatrix, posVS);
            }
            
            v2f vert(appdata_t v)
            {
                v2f OUT;
                
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                
                OUT.worldPosition = v.vertex;
                //OUT.vertex = TransformObjectToHClip(v.vertex.xyz);
                OUT.vertex = TransformObjectToHClipPerspective(v.vertex);
                
                float2 pixelSize = OUT.vertex.w;
                pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));

                float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
                float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
                OUT.texcoord = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
                OUT.mask = half4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));

                OUT.color = v.color * _Color;
                return OUT;
            }

            // This is a overridable method for calculate UI image color
            // if you want make some effects for UI image, please override this method.
            #ifndef INITIALIZE_UI_IMAGE
            #define INITIALIZE_UI_IMAGE InitializeUIImage
            void InitializeUIImage(v2f IN,inout float4 color)
            {
                color = IN.color * (SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,IN.texcoord) + _TextureSampleAdd);
            }
            #endif

            float4 pixel(v2f IN) : SV_Target
            {
                //Round up the alpha color coming from the interpolator (to 1.0/256.0 steps)
                //The incoming alpha could have numerical instability, which makes it very sensible to
                //HDR color transparency blend, when it blends with the world's texture.
                const half alphaPrecision = half(0xff);
                const half invAlphaPrecision = half(1.0/alphaPrecision);
                IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision;

                half4 color;
                INITIALIZE_UI_IMAGE(IN,color);

                #ifdef UNITY_UI_CLIP_RECT
                half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
                color.a *= m.x * m.y;
                #endif

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

                // Guaranteeing that your UI shader is in mode of "One OneMinusSrcAlpha".
                color.rgb *= color.a;

                return color;
            }
            
            ENDHLSL
        }
    }
}

转载请注明出处:https://www.cnblogs.com/jietian331/p/19099066

posted @ 2025-09-18 16:30  太乙_真人  阅读(19)  评论(0)    收藏  举报