[Unity] WebGL 加载 Addressable 包得到的示例中 Sprite Renderer 材质纹理丢失,但是在监视器中更换其材质后又能找到主纹理

1,502 阅读6分钟

Unity 版本:

2021.2/china_support/instantgame/staging 8bd2ddd0516e

问题:

使用 Addressable 加载的场景中,UI 的纹理没有丢失,但是其他 GameObject 中的 Sprite Renderer 的材质纹理丢失了,提示如图

image.png

Material does not have a _MainTex texture property. It is required for SpriteRenderer.

但是在监视器中看这个 Sprite Renderer 组件,很显然的是,这个组件的 Sprite 属性是有值的

然后在监视器中更换这个物体的材质,随便换任何一个其他材质,都不会再弹出找不到纹理的警告

image.png

然后再换回原来的材质,发现它也是能够找到纹理的

image.png

这个错误具体的表现就是,物体 A B C 用的材质 M1,物体 D E F 用的材质 M2,M1 和 M2 在场景加载之后都说找不到纹理,物体 A B C D E F 都是洋红;然后从 A B C 中任选一个物体,将它的材质从 M1 换到别的材质再换回来,物体 A B C 就会恢复正常,物体 D E F 不变,还是洋红;然后从 D E F 中任选一个物体,将它的材质从 M2 换到别的材质再换回来,物体 D E F 就会恢复正常

不论材质 M1 M2 是自定义的着色器还是 Unity 的默认着色器,比如 Sprite/Default,都会有这个问题

即使假设通过在编辑器里来回更换材质的方式,让材质从洋红恢复成正常了,也会有个问题是,换回来之后,自定义的着色器会出错,而 Unity 的默认着色器,比如 Sprite/Default,不会再出错

就非常神奇

并且只在 AA 包的 WebGL 构建中会出现这个 bug,在 Use Asset DatabaseSimulate Groups 模式都不会有问题

image.png

失败的尝试 1

感觉是因为材质是使用 Instance 的形式加载的,所以一个材质 Instance 会控制所有使用这个材质的物体

然后我猜可能是因为材质 Instance 在加载的时候可能是比纹理加载得更早,所以材质 Instance 初始化的时候找不到纹理,就变洋红了。之后我在编辑器中操作的话,这个时候纹理也早就加载完了,所以我再更换材质,新材质初始化的时候就能找到纹理了

于是我做了一些测试

测试 1

protected void Start()
{
    StartCoroutine(TestCo());
}

private IEnumerator TestCo()
{
    yield return new WaitForEndOfFrame();
    Debug.Log("!!!!!!!!!!!!");
    Shader oldSha = spriteRenderer.material.shader;
    spriteRenderer.material = new Material(oldSha);
}

结果:

在执行完 spriteRenderer.material = new Material(oldSha); 之后,依然有找到主纹理的警告消失,材质依然是洋红

测试 2

protected void Start()
{
    StartCoroutine(TestCo());
}

private IEnumerator TestCo()
{
    yield return new WaitForSeconds(3f);
    Debug.Log("!!!!!!!!!!!!");
    spriteRenderer.material.SetTexture("_MainTex", spriteRenderer.sprite.texture);
}

结果:

在执行完 SetTexture 之后,提示没有找到主纹理的警告消失,但是材质依然是洋红

测试 3:

protected void Start()
{
    StartCoroutine(TestCo());
}

private IEnumerator TestCo()
{
    yield return new WaitForEndOfFrame();
    Debug.Log("!!!!!!!!!!!!");
    Shader oldSha = spriteRenderer.material.shader;
    spriteRenderer.material = new Material(oldSha);
    spriteRenderer.material.SetTexture("_MainTex", spriteRenderer.sprite.texture);
}

结果:

在执行完 SetTexture 之后,提示没有找到主纹理的警告消失,但是材质依然是洋红

测试 4:

protected void Start()
{
    StartCoroutine(TestCo());
}

private IEnumerator TestCo()
{
    yield return new WaitForEndOfFrame();
    Debug.Log("!!!!!!!!!!!!");
    spriteRenderer.material.SetTexture("_MainTex", spriteRenderer.sprite.texture);
    Shader oldSha = spriteRenderer.material.shader;
    spriteRenderer.material = new Material(oldSha);
}

结果:

在执行完 SetTexture 之后,依然有找到主纹理的警告消失,材质依然是洋红

这都不行的话,只能说明我的假设有问题

我想想,AA 是基于 AB 的,而 AB 里面一个常见的操作就是先加载一个包的依赖包,再加载这个包

于是我觉得确实应该和资源加载顺序无关把……?

搜帖子

www.reddit.com/r/Unity3D/c…

这个人具体指的是 UnityWebRequest.GetTexture() 无法正常工作,跟我的稍微有点不一样,并且他也没有得到回答

forum.unity.com/threads/sha…

这里说把 Auto Graphics APIs 切换成 false,或者为 true 但是 lightmap quality = low

image.png

我都试过了,没有效果

失败的尝试 2

看到一个人说是在 build 的时候有问题

forum.unity.com/threads/clo…

然后说是因为 #include "UnityCG.cginc" 导致的问题

我不用 build,我就把我的着色器改了一下

以前:

Shader "Unlit/UnlitGrid"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _MainTexScale ("Main Texture Scale", float) = 1
        _GridSize ("Grid Size", float) = 1
        [FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
        _LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
        _ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
        _UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)   
        _TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
        _TintRadius("Tint Radius", float) = 1
        _TintPower("Tint Power", float) = 0
        _TintSpreadTime("Tint Spread Time", float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float4 worldSpacePos : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            float _MainTexScale;
            
            float _GridSize;
            float _GridLineSize;
            float4 _LineColor;
            
            float4 _ReachableTint;
            float4 _UnreachableTint;
            
            vector _TintCenter;
            float _TintRadius;
            float _TintPower;

            float _TintSpreadTime;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            fixed4 add_tint(fixed4 col, float2 ipos, float2 fpos)
            {
                float itime = floor(_TintSpreadTime);  // integer time
                float ftime = frac(_TintSpreadTime);  // fraction time
                
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = abs(tint_center_to_ipos.x) + abs(tint_center_to_ipos.y);
                
                float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
                
                float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
                
                // move
                
                float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);

                float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);

                // scale
                
                float2 fpos_to_grid_center = fpos - tint_grid_center;

                float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
                
                spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);

                // add tint

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
                
                float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;
                
                fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
                
                return lerp(col, tint, _TintPower);
                
            }
            
            fixed4 add_grid(fixed4 col, float2 fpos)
            {
                float scaled_line_size = _GridLineSize/_GridSize;
                
                float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos);       // bottom-left
                float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos);   // top-right
                float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;

                col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;

                return col;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                float2 st_world = i.worldSpacePos.xy/_MainTexScale;
                
                float2 ipos = floor(st);  // integer
                float2 fpos = frac(st);  // fraction

                fixed4 col = tex2D(_MainTex, st_world);
                
                col = add_tint(col, ipos, fpos);
                col = add_grid(col, fpos);
          
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

现在,把 #include "UnityCG.cginc" 给去掉了:

Shader "Unlit/UnlitGrid"
{
    Properties
    {
        [PerRendererData] _MainTex ("Texture", 2D) = "white" {}
        _MainTexScale ("Main Texture Scale", float) = 1
        _GridSize ("Grid Size", float) = 1
        [FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
        _LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
        _ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
        _UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)   
        _TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
        _TintRadius("Tint Radius", float) = 1
        _TintPower("Tint Power", float) = 0
        _TintSpreadTime("Tint Spread Time", float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 worldSpacePos : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            float _MainTexScale;
            
            float _GridSize;
            float _GridLineSize;
            float4 _LineColor;
            
            float4 _ReachableTint;
            float4 _UnreachableTint;
            
            vector _TintCenter;
            float _TintRadius;
            float _TintPower;

            float _TintSpreadTime;

            #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            fixed4 add_tint(fixed4 col, float2 ipos, float2 fpos)
            {
                float itime = floor(_TintSpreadTime);  // integer time
                float ftime = frac(_TintSpreadTime);  // fraction time
                
                float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
                
                float dist_tint_center_to_ipos = abs(tint_center_to_ipos.x) + abs(tint_center_to_ipos.y);
                
                float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
                
                float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
                
                // move
                
                float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);

                float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);

                // scale
                
                float2 fpos_to_grid_center = fpos - tint_grid_center;

                float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
                
                spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);

                // add tint

                float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
                
                float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;
                
                fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
                
                return lerp(col, tint, _TintPower);
                
            }
            
            fixed4 add_grid(fixed4 col, float2 fpos)
            {
                float scaled_line_size = _GridLineSize/_GridSize;
                
                float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos);       // bottom-left
                float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos);   // top-right
                float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;

                col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;

                return col;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.worldSpacePos.xy / _GridSize;
                float2 st_world = i.worldSpacePos.xy/_MainTexScale;
                
                float2 ipos = floor(st);  // integer
                float2 fpos = frac(st);  // fraction
                
                fixed4 col = tex2D(_MainTex, st_world);
                
                col = add_tint(col, ipos, fpos);
                col = add_grid(col, fpos);
                
                return col;
            }
            ENDCG
        }
    }
}

对结果还是没有影响,还是洋红

尝试把出错的物体不用 Addressable 加载场景,而是加载预制体

然后我尝试不把出错的物体放在场景中,而是放在预制体中,然后等到场景加载的时候再加载这个物体,还是不行

尝试将 Shader 加入 Include Shader

看到别人都在说要将 Shader 加入 Include Shader 否则打包的时候识别不到 Shader,就可能包里面没有 Shader

stackoverflow.com/questions/4…

blog.csdn.net/u010377179/…

我试了一下,也没有解决我的问题

解决方法:使用 Shader.Find()

forum.unity.com/threads/loa…

使用 #14 的脚本解决了问题……就是 Unity 的问题,解决方法真的很 hacky,从材质那里获得 Shader 的名字,根据这个名字再找一遍 Shader 赋回给材质 Shader.Find 是在构建的工程中寻找的,所以这至少说明资源是成功加载的……?或者像这个帖子所说的一样,在打包的时候会向目标平台构建着色器,但是实际上在设置着色器的时候就会有问题……?不懂,真是天坑

对于我来说,就是:

protected void Start()
{
    Material mat = spriteRenderer.material;
    mat.shader = Shader.Find(mat.shader.name);
}