Unity自定义shader打包SpriteAtlas图集问题

Unity打包图集还是有一些坑的,至于图集SpriteAtlas是什么请参考我之前写的文章:【Sprite Atlas】Unity新图集系统SpriteAtlas超详细使用教程_spriteatlas 使用-CSDN博客

问题:

今天碰到的问题是,shader绘制的时候,因为打包图集后,MainTexture是图集的图片,所以shader渲染就错误了。

非图集是这样显示的,正常的一个地块。

打包完图集后,发现这个MainTexture是整个图集的图片

导致显示就错乱了,如下图。

正常的显示是这样的。

问题所在:

原因就是打包图集后传入给shader的uv变了,本来只有图片的时候,uv就是0-1的本地uv值,现在素材换成一张更大的图了,导致uv采样就出现了问题。

解决方法:

将图片本身的uv信息传给shader,做一个计算转换即可。

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Sprites;
using UnityEngine;

public class StaticGroundObject : GroundObject
{
    public GameObject notCellObjectShow;
    private SpriteRenderer spriteRenderer;
    private const string _UVRangeName = "_UVRange";
    protected override void OnStart()
    {
        base.OnStart();
        SetSortingOrder();
        spriteRenderer = GetComponentInChildren<SpriteRenderer>();
        UpdateSpriteRenderer();
    }

    protected override void OnEnable()
    {
        base.OnEnable();
        
    }
    //修改也要
    void UpdateSpriteRenderer()
    {
        if (spriteRenderer != null)
        {
            Sprite sprite = spriteRenderer.sprite;
            Vector2 texelSize = sprite.texture.texelSize;
            Rect rect = sprite.textureRect;
           
            Vector4 uvRemap = new(
              rect.x * texelSize.x,
              rect.y * texelSize.y,
              rect.width * texelSize.x,
              rect.height * texelSize.y
            );

            // 将UV值传递给材质
            Material material = spriteRenderer.material;
            if (material != null)
            {
                // 确保shader中有对应的属性
                if (material.HasProperty(_UVRangeName))
                {
                    material.SetVector(_UVRangeName, uvRemap);
                    Debug.Log("UV值已成功写入shader");
                }
                else
                {
                    Debug.LogError("shader缺少必要的属性,请确保shader中定义了_UV1和_UV2属性");
                }
            }
            else
            {
                Debug.LogError("renderer的材质为空");
            }
        }
    }

    public override void SetNotCellObjectShow(GameObject notCellObjectShow)
    {
        base.SetNotCellObjectShow(notCellObjectShow);
        this.notCellObjectShow = notCellObjectShow;
    }

    protected override void CheckRandomPrefabs(bool isShow)
    {
        if (notCellObjectShow != null)
        {
            //上面没有东西,而且没有混合到其他格子上
            if (ParentGround == null && isShow)
            {
                notCellObjectShow.SetActive(true);
            }
            else
            {
                notCellObjectShow.SetActive(false);
            }
        }
    }

    private void SetSortingOrder()
    {
        var ground = GetComponentsInChildren<SpriteRenderer>(true)[0];
        var groundName = ground.name;
        var lastIndex = groundName.LastIndexOf('_');
        var secondLastIndex = groundName.LastIndexOf('_', lastIndex - 1);
        int.TryParse(groundName.Substring(secondLastIndex + 1, lastIndex - secondLastIndex - 1), out int result);
        ground.sortingOrder -= result;
    }

    protected override void SetBlendMaskValue(Texture2D newBlendMask, int newRotateMaskValue, Texture2D mainBlendTex,
        Texture2D blendMaskChamfer, Dictionary<int, float> _offsetList)
    {
        // base.SetBlendMaskValue(newBlendMask, newRotateMaskValue, mainBlendTex, blendMaskChamfer, offsetList);
        //获得要渲染的对象
        if (mainBlends.Count == 0)
        {
            mainBlends = GetComponentsInChildren<Renderer>().ToList();
        }

        if (mainBlends.Count == 0)
        {
            Debug.LogError("mainBlends.Count==0");
            return;
        }

        //设置遮罩贴图
        this.blendMask = newBlendMask;
        this.rotateMaskValue = newRotateMaskValue;
        this.mainBlendTex = mainBlendTex;
        this.blendMaskChamfer = blendMaskChamfer;
        //设置偏移值
        this.blendOffsetTargetList = _offsetList;

        UpdateMaterial();
        UpdateSpriteRenderer();
    }

    protected override void SetBlendOffsetList(Dictionary<int, float> _offsetList)
    {
        // base.SetBlendOffsetList(_offsetList);
        blendOffsetTargetList = _offsetList;
        UpdateMaterial();
        UpdateSpriteRenderer();
    }
}

public class MaterialCacheTool
{
    public static Dictionary<string, Material> materialCache = new Dictionary<string, Material>();

    public static Material CheckMaterial(string materialKey, Renderer defaultRenderer, Action<Material> onInit)
    {
        Material checkMaterial = null;
        if (materialCache.ContainsKey(materialKey))
        {
            checkMaterial = materialCache[materialKey];
        }
        else
        {
            var material = new Material(defaultRenderer.sharedMaterial);
            material.name = materialKey;
            onInit?.Invoke(material);
            materialCache.Add(materialKey, material);
            checkMaterial = material;
        }

        return checkMaterial;
    }
}
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Shader/GroundObjectV3_Code"
{
    Properties
    {
        [NoScaleOffset]_MaskMap("MaskMap", 2D) = "white" {}
        [NoScaleOffset]_MainTex("MainTex", 2D) = "white" {}
        [NoScaleOffset]_Chamfer("Chamfer", 2D) = "black" {}
        
        _RotateMask("RotateMask",Range(0, 360)) = 0
        _OffsetUp("OffsetUp", Range(0, 1)) = 0
        _OffsetDown("OffsetDown", Range(0, 1)) = 0
        _OffsetLeft("OffsetLeft", Range(0, 1)) = 0
        _OffsetRight("OffsetRight", Range(0, 1)) = 0
        _OffsetUpLeft("OffsetUpLeft", Range(0, 1)) = 0
        _OffsetUpRight("OffsetUpRight", Range(0, 1)) = 0
        _OffsetDownLeft("OffsetDownLeft", Range(0, 1)) = 0
        _OffsetDownRight("OffsetDownRight", Range(0, 1)) = 0
        //相反
        _IsInversion("IsInversion", Float) = 0
        
        [NoScaleOffset]_BlendMaskMap("BlendMaskMap", 2D) = "black" {}
        [NoScaleOffset]_BlendMainTex("BlendMainTex", 2D) = "white" {}
        [NoScaleOffset]_BlendChamfer("_BlendChamfer", 2D) = "black" {}
        _BlendOffsetUp("BlendOffsetUp", Range(0, 1)) = 0
        _BlendOffsetDown("BlendOffsetDown", Range(0, 1)) = 0
        _BlendOffsetLeft("BlendOffsetLeft", Range(0, 1)) = 0
        _BlendOffsetRight("BlendOffsetRight", Range(0, 1)) = 0
        _BlendOffsetUpLeft("BlendOffsetUpLeft", Range(0, 1)) = 0
        _BlendOffsetUpRight("BlendOffsetUpRight", Range(0, 1)) = 0
        _BlendOffsetDownLeft("BlendOffsetDownLeft", Range(0, 1)) = 0
        _BlendOffsetDownRight("BlendOffsetDownRight", Range(0, 1)) = 0
        
        
        [HideInInspector]_QueueOffset("_QueueOffset", Float) = 0
        [HideInInspector]_QueueControl("_QueueControl", Float) = -1
        [HideInInspector][NoScaleOffset]unity_Lightmaps("unity_Lightmaps", 2DArray) = "" {}
        [HideInInspector][NoScaleOffset]unity_LightmapsInd("unity_LightmapsInd", 2DArray) = "" {}
        [HideInInspector][NoScaleOffset]unity_ShadowMasks("unity_ShadowMasks", 2DArray) = "" {}
        _OffsetFactor("OffsetFactor", Float) = 0
        _OffsetUnits("OffsetUnits", Float) = 0
        
        [ToggleUI] _ReceiveShadows("Receive Shadows", Float) = 1.0
        [ToggleUI] _CastShadows("Cast Shadows", Float) = 1.0
        //阴影模糊
        _ShadowBlur("ShadowBlur", Float) = 0.0
        _Cull("__cull", Float) = 2.0

        // 新增距离缩放因子属性
        _DistanceScaleFactor("Distance Scale Factor", Range(0.1, 100.0)) = 60.0
        //补充UV
         _UVRange("UV Range", Vector) = (0, 0, 1, 1)

    }
    SubShader
    {
        Tags
        {
            "RenderPipeline"="UniversalPipeline"
            "RenderType"="Opaque"
            "UniversalMaterialType" = "Lit"
            "Queue"="AlphaTest"
            "DisableBatching"="LODFading"
            "ShaderGraphShader"="true"
            "ShaderGraphTargetId"="UniversalLitSubTarget"
            "EnableGPUInstancing" = "true"

        }
        LOD 100

        Pass
        {
            // Render State
            Cull Back
            ZTest LEqual
            ZWrite On
            Blend One Zero
            AlphaToMask On
            offset [_OffsetFactor] , [_OffsetUnits]
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            // #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            
            // #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            #include "EgoLight.hlsl"
                #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                
                //光照计算
                float4 positionOS : POSITION;
                float4 normalOS : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                // UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float3 worldPos : TEXCOORD1;

                //光照计算
                float3 viewDirWS : TEXCOORD2;
                float3 normalWS : TEXCOORD3;

                // 新增距离变量
                float distanceToCamera : TEXCOORD4;
                float prevLod : TEXCOORD5; // 新增变量存储上一次的LOD
                float2 uv2:TEXCOORD6;
            };

            sampler2D _MaskMap;
            float4 _MaskMap_ST;

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _Chamfer;
            float4 _Chamfer_ST;

            float _RotateMask;
            float _OffsetUp;
            float _OffsetDown;
            float _OffsetLeft;
            float _OffsetRight;
            float _OffsetUpLeft;
            float _OffsetUpRight;
            float _OffsetDownLeft;
            float _OffsetDownRight;

            float _IsInversion;

            //混合剔除的参数
            sampler2D _BlendMaskMap;
            float4  _BlendMaskMap_ST;

            sampler2D _BlendMainTex;
            float4 _BlendMainTex_ST;
            
            sampler2D _BlendChamfer;
            float4 _BlendChamfer_ST;


            float _BlendOffsetUp;
            float _BlendOffsetDown;
            float _BlendOffsetLeft;
            float _BlendOffsetRight;
            float _BlendOffsetUpLeft;
            float _BlendOffsetUpRight;
            float _BlendOffsetDownLeft;
            float _BlendOffsetDownRight;

            float4 _GlobalShadowColor;

             // 新增距离缩放因子属性变量,放在HLSL代码的合适位置,这里在函数外部声明
            float _DistanceScaleFactor;
           
           float4 _UVRange;

            v2f vert (appdata v)
            {
                v2f o;
       
                o.uv = TRANSFORM_TEX(v.uv, _MaskMap);
                   
                float2 spriteRectPos = _UVRange.xy;
                float2 spriteRectSize = _UVRange.zw;
                float2 localUV = (v.uv - spriteRectPos) / spriteRectSize;
                o.uv2 = localUV;

                o.vertex = TransformObjectToHClip(v.vertex);//UnityObjectToClipPos   //o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);                        
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                
                // 计算物体到摄像机的距离
                o.distanceToCamera = distance(mul(unity_ObjectToWorld, v.vertex).xyz, _WorldSpaceCameraPos.xyz);
                o.prevLod = 0; // 初始化

                //光照计算
                VertexPositionInputs positionInputs = GetVertexPositionInputs(v.positionOS.xyz);
                VertexNormalInputs normalInputs = GetVertexNormalInputs(v.normalOS.xyz);
                o.viewDirWS = GetCameraPositionWS() - positionInputs.positionWS;
                o.normalWS = normalInputs.normalWS;
                
                return o;
            }


            float2 GetMaskUV(float2 uv)
            {
                float2 maskUV=uv;
                maskUV -= 0.5;

                // 旋转 45 度(π/4 弧度)
                float angle = radians(_RotateMask);
                float cosAngle = cos(angle);
                float sinAngle = sin(angle);
                float2x2 rotationMatrix = float2x2(cosAngle, -sinAngle, sinAngle, cosAngle);
                maskUV = mul(rotationMatrix, maskUV);

                // 平移回原来的坐标
                maskUV += 0.5;
                return maskUV;
            }
            
            //全部的Offset
            float GetColorMask(sampler2D colorMask, sampler2D colorChamfer,float2 uv, float _OffsetUp,  float _OffsetDown,  float _OffsetLeft,  float _OffsetRight,  float _OffsetUpLeft,  float _OffsetUpRight,  float _OffsetDownLeft,  float _OffsetDownRight,float _lod)
            {
                float offsetMax = 0.5;
                float2 offsets[8] = {
                    float2(0, -_OffsetUp * offsetMax),
                    float2(0, _OffsetDown * offsetMax),
                    float2(_OffsetLeft * offsetMax, 0),
                    float2(-_OffsetRight * offsetMax, 0),
                    float2(_OffsetUpLeft * offsetMax, -_OffsetUpLeft * offsetMax),
                    float2(-_OffsetUpRight * offsetMax, -_OffsetUpRight * offsetMax),
                    float2(_OffsetDownLeft * offsetMax, _OffsetDownLeft * offsetMax),
                    float2(-_OffsetDownRight * offsetMax, _OffsetDownRight * offsetMax)
                };

                float alpha = 0;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv),0,_lod)).r;

                // 循环采样偏移纹理
                //for (int i = 0; i < 8; i++) {
                //    alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[i]),0,_lod)).r;
                //}
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[0]),0,_lod)).r;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[1]),0,_lod)).r;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[2]),0,_lod)).r;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[3]),0,_lod)).r;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[4]),0,_lod)).r;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[5]),0,_lod)).r;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[6]),0,_lod)).r;
                alpha += tex2Dlod(colorMask, float4(GetMaskUV(uv + offsets[7]),0,_lod)).r;

                // 采样倒角纹理
                alpha += tex2Dlod(colorChamfer, float4(uv + float2( _OffsetLeft * offsetMax, -_OffsetUp * offsetMax),0,_lod)).r;
                alpha += tex2Dlod(colorChamfer, float4(uv + float2(-_OffsetRight * offsetMax, -_OffsetUp * offsetMax),0,_lod)).r;
                alpha += tex2Dlod(colorChamfer, float4(uv + float2( _OffsetLeft * offsetMax,  _OffsetDown * offsetMax),0,_lod)).r;
                alpha += tex2Dlod(colorChamfer, float4(uv + float2(-_OffsetRight * offsetMax, _OffsetDown * offsetMax),0,_lod)).r;
                
                // 透明度限制在0-1之间
                alpha = clamp(alpha, 0, 1);                         
                
                return alpha;
            }

            float4 frag (v2f i) : SV_Target
            {
                float2 maskUV = i.uv2;    
                float lod = i.distanceToCamera / _DistanceScaleFactor;
                lod = clamp(lod, 0, 5); 
                float mainAlpha = GetColorMask( _MaskMap, _Chamfer, maskUV, _OffsetUp,  _OffsetDown,  _OffsetLeft,  _OffsetRight,  _OffsetUpLeft,  _OffsetUpRight,  _OffsetDownLeft,  _OffsetDownRight,lod);


                 // 计算主纹理的UV偏移
               float2 mainTexUV = TRANSFORM_TEX(i.uv, _MainTex).xy;


                float4 mainColor = tex2Dlod(_MainTex, float4(mainTexUV, 0, lod));//tex2D(_MainTex, TRANSFORM_TEX(i.uv, _MainTex));
                mainAlpha *= mainColor.a;

                float f = lerp(1.01,0.99,_IsInversion);//1.01;
                //maskUV缩放变大一点点
                maskUV -= 0.5;
                maskUV *=lerp(1.01,0.99,_IsInversion);         // 1.005;
                maskUV += 0.5;
                //各个边移动多一丢丢
                // _BlendOffsetUp += float2(0,f);
                // _BlendOffsetDown  += float2(0,-f);
                // _BlendOffsetLeft  +=  float2(-f,0);
                // _BlendOffsetRight  += float2(f,0);
                // _BlendOffsetUpLeft  += float2(-f,f);
                // _BlendOffsetUpRight += float2(f,f);
                // _BlendOffsetDownLeft += float2(-f,-f);
                // _BlendOffsetDownRight  += float2(f,-f);
                
                float blendAlpha = GetColorMask( _BlendMaskMap, _BlendChamfer, maskUV, _BlendOffsetUp,  _BlendOffsetDown,  _BlendOffsetLeft,  _BlendOffsetRight,  _BlendOffsetUpLeft,  _BlendOffsetUpRight,  _BlendOffsetDownLeft,  _BlendOffsetDownRight,lod);
           
                float4 blendColor = tex2Dlod(_BlendMainTex, float4(TRANSFORM_TEX(i.uv, _BlendMainTex),0,lod));
                blendAlpha*=blendColor.a;

                float alpha = mainAlpha - blendAlpha;
                alpha = lerp(alpha,1-alpha,_IsInversion);          
                
                // 根据距离调整纹理采样的LOD
                
               // mainColor = tex2Dlod(_MainTex, float4(TRANSFORM_TEX(i.uv, _MainTex).xy, 0, lod));
               // blendColor = tex2Dlod(_BlendMainTex, float4(TRANSFORM_TEX(i.uv, _BlendMainTex).xy, 0, lod));

                
                
                float _Cutoff = 0.8;
                
                float3 color = CheckColor(i.worldPos,i.normalWS,_GlobalShadowColor,mainColor.rgb);
                
                clip(alpha - _Cutoff);
                return float4(color.rgb, alpha);
            }
            ENDHLSL
        }
        

      
        
        
    }
}

参考这个链接会解释的更加详细点:

Local UVs for Sprites in Sprite Sheet/Atlas | Cyanilux

Unity中,当你创建一个Sprite Atlas并使用它来打包多个Sprite时,你可能需要获取这个图集在构建后占用的具体大小。这可以通过编写脚本来实现,以便在构建过程之后获取图集的大小信息。以下是如何通过脚本实现这一功能的基本步骤: 1. 在Unity编辑器中,创建一个新的C#脚本,命名为例如`SpriteAtlasSizeReporter.cs`。 2. 将这个脚本附加到一个空的GameObject上。 3. 在脚本中,你需要使用`Resources.Load`方法来加载你的Sprite Atlas资源。 4. 之后,使用Unity的`SpriteAtlas.GetPackedSheet`方法来获取图集的详细信息。 5. 通过这些信息,你可以计算出图集的总宽度和高度,这通常基于图集中的每个元素的大小以及它们如何被排列。 以下是一个简单的代码示例,展示了如何获取Sprite Atlas的打包后的图集大小: ```csharp using UnityEngine; using UnityEngine.U2D; using UnityEngine.SpriteRuntimeAtlas; public class SpriteAtlasSizeReporter : MonoBehaviour { void Start() { // 加载指定的Sprite Atlas资源 SpriteAtlas atlas = Resources.Load<SpriteAtlas>("YourSpriteAtlasName"); if (atlas == null) { Debug.LogError("Sprite Atlas not found!"); return; } // 获取图集打包信息 PackedSheet packedSheet = atlas.GetPackedSheet(); if (packedSheet == null) { Debug.LogError("Could not get packed sheet for the atlas."); return; } // 计算图集的大小 int totalWidth = packedSheet.rect.width; int totalHeight = packedSheet.rect.height; Debug.Log($"Sprite Atlas Size: {totalWidth} x {totalHeight}"); } } ``` 请将`"YourSpriteAtlasName"`替换为你的实际Sprite Atlas的名称。 在使用上述脚本之前,确保你的Sprite Atlas已经被正确添加到Resources文件夹中,以便可以通过`Resources.Load`方法加载。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼蛋-Felix

如果对你有用,可以请我喝杯可乐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值