Unity ShaderVariant 变体收集方案分析

最近遇到一个问题,在editor中场景渲染正确,打包android之后,渲染异常。

经过排查得出原因:工程把所有shader单独打包Assetbundle,editor打包ab包的时候,未收集到正确的shader变体,未将场景中使用的shader变体打包到ab包中,所以发布apk之后,渲染异常。

什么是shader变体:ShaderVariant变体_Sevol_Y的博客-CSDN博客

在网上猛搜了一天,找到很多解决方法,也测试了很多方法:

  1. 方案1,测试可行。将shader加入到editor设置中,ProjectSetting-->Graphics-->AlwaysIncludedShaders。所有加入设置的shader都将编译全部shader变体。如果你知道是哪一个shader出现异常,加入进设置列表中,应该能解决问题。注意注意!:如果你的shader变体有很多,类似unity内置的standard,那么千万不要加入,这会导致你的出包巨慢,而且包内有很多用不到的shader变体。还有就是我出现问题的shader,包含的总变体有6M+个(百万个),这时候editor就会提醒你,亲,你的变体个数实在太多,这边不建议你加入列表,直接阻止你的操作。
  2. 方案2,未测试。将shader放入Resources文件夹下,等同方案一效果。
  3. 方案3,测试可行,将出现异常的mat和shader放在同一文件夹下,效果正常。
  4. 方案4,测试可行。使用unity自动收集shader变体文件功能。先点击Clear,清除已经收集到的变体,然后直接运行游戏,将游戏的犄角旮哪都跑一遍,或者你也可以运行到出现异常的地方(不过这样不全,只会解决你现目前发现问题的地方),然后点击SaveToAsset,保存shader变体文件到Shader  assetbundle包内,效果正常。
  5. 方案5,自动收集变体代码1,测试,未解决我的问题。自动收集变体文件ShaderVariant ,网上流传的脚本,放入工程测试了,不过未解决我的问题,不知道什么原因,这是BDFramework的框架内容,大家可以放到自己工程试试。GitHub - yimengfan/BDFramework.Core: Simple and powerful Unity3d game workflow! 简单、高效、高度工业化的商业级unity3d 工作流。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEditor;
    using UnityEngine;
    using System.IO;
    using System.Reflection;
    using System;
    using UnityEngine.Rendering;
    using System.Linq;
    
    public class ShaderCollection : EditorWindow
    {
        static Dictionary<string, List<ShaderVariantCollection.ShaderVariant>> ShaderVariantDict = new Dictionary<string, List<ShaderVariantCollection.ShaderVariant>>();
        public static List<string> GetAllRuntimeDirects()
        {
            //搜索所有资源
            List<string> directories = new List<string>();
            directories.Add("Assets");
            return directories;
        }
        private ShaderVariantCollection svc;
        readonly public static string ALL_SHADER_VARAINT_PATH = "Assets/AllShaders.shadervariants";
    
        static List<string> allShaderNameList = new List<string>();
    
        [MenuItem("ShaderTool/AutoShaderVariants")]
        public static void GenShaderVariant()
        {
            ShaderVariantDict = new Dictionary<string, List<ShaderVariantCollection.ShaderVariant>>();
            //先搜集所有keyword到工具类SVC
            toolSVC = new ShaderVariantCollection();
            var shaders = AssetDatabase.FindAssets("t:Shader", new string[] { "Assets", "Packages" }).ToList();
            foreach (var shader in shaders)
            {
                ShaderVariantCollection.ShaderVariant sv = new ShaderVariantCollection.ShaderVariant();
                var shaderPath = AssetDatabase.GUIDToAssetPath(shader);
                sv.shader = AssetDatabase.LoadAssetAtPath<Shader>(shaderPath);
                toolSVC.Add(sv);
                //
                allShaderNameList.Add(shaderPath);
            }
    
            var toolsSVCpath = "Assets/Tools.shadervariants";
            //防空
    
            File.WriteAllText(toolsSVCpath, "");
            AssetDatabase.DeleteAsset(toolsSVCpath);
            AssetDatabase.CreateAsset(toolSVC, toolsSVCpath);
    
            //搜索所有Mat
            var paths = GetAllRuntimeDirects().ToArray();
            var assets = AssetDatabase.FindAssets("t:Prefab", paths).ToList();
            var assets2 = AssetDatabase.FindAssets("t:Material", paths);
            assets.AddRange(assets2);
            List<string> allMats = new List<string>();
    
            //GUID to assetPath
            for (int i = 0; i < assets.Count; i++)
            {
                var p = AssetDatabase.GUIDToAssetPath(assets[i]);
                //获取依赖中的mat
                var dependenciesPath = AssetDatabase.GetDependencies(p, true);
                var mats = dependenciesPath.ToList().FindAll((dp) => dp.EndsWith(".mat"));
                allMats.AddRange(mats);
            }
    
            //处理所有的 material
            allMats = allMats.Distinct().ToList();
    
            float count = 1;
            foreach (var mat in allMats)
            {
                var obj = AssetDatabase.LoadMainAssetAtPath(mat);
                if (obj is Material)
                {
                    var _mat = obj as Material;
                    EditorUtility.DisplayProgressBar("处理mat", string.Format("处理:{0} - {1}", Path.GetFileName(mat), _mat.shader.name), count / allMats.Count);
                    AddToDict(_mat);
                }
    
                count++;
            }
    
            EditorUtility.ClearProgressBar();
            //所有的svc
            ShaderVariantCollection svc = new ShaderVariantCollection();
            foreach (var item in ShaderVariantDict)
            {
                foreach (var _sv in item.Value)
                {
                    svc.Add(_sv);
                }
            }
    
            AssetDatabase.DeleteAsset(ALL_SHADER_VARAINT_PATH);
            AssetDatabase.CreateAsset(svc, ALL_SHADER_VARAINT_PATH);
            AssetDatabase.Refresh();
    
        }
        public class ShaderData
        {
            public int[] PassTypes = new int[] { };
            public string[][] KeyWords = new string[][] { };
            public string[] ReMainingKeyWords = new string[] { };
        }
    
        //shader数据的缓存
        static Dictionary<string, ShaderData> ShaderDataDict = new Dictionary<string, ShaderData>();
    
    
    
        //添加Material计算
        static List<string> passShaderList = new List<string>();
    
        /// <summary>
        /// 添加到Dictionary
        /// </summary>
        /// <param name="curMat"></param>
        static void AddToDict(Material curMat)
        {
            if (!curMat || !curMat.shader) return;
    
            var path = AssetDatabase.GetAssetPath(curMat.shader);
            if (!allShaderNameList.Contains(path))
            {
                Debug.LogError("不存在shader:" + curMat.shader.name);
                Debug.Log(path);
                return;
            }
    
            ShaderData sd = null;
            ShaderDataDict.TryGetValue(curMat.shader.name, out sd);
            if (sd == null)
            {
                //一次性取出所有的 passtypes 和  keywords
                sd = GetShaderKeywords(curMat.shader);
                ShaderDataDict[curMat.shader.name] = sd;
            }
    
            var kwCount = sd.PassTypes.Length;
            if (kwCount > 2000)
            {
                if (!passShaderList.Contains(curMat.shader.name))
                {
                    Debug.LogFormat("Shader【{0}】,变体数量:{1},不建议继续分析,后续也会跳过!", curMat.shader.name, kwCount);
                    passShaderList.Add(curMat.shader.name);
                }
                else
                {
                    Debug.LogFormat("mat:{0} , shader:{1} ,keywordCount:{2}", curMat.name, curMat.shader.name, kwCount);
                }
    
                return;
            }
    
    
            List<ShaderVariantCollection.ShaderVariant> svlist = null;
            if (!ShaderVariantDict.TryGetValue(curMat.shader.name, out svlist))
            {
                svlist = new List<ShaderVariantCollection.ShaderVariant>();
                ShaderVariantDict[curMat.shader.name] = svlist;
            }
    
            //求所有mat的kw
            for (int i = 0; i < sd.PassTypes.Length; i++)
            {
                //
                var pt = (PassType)sd.PassTypes[i];
                ShaderVariantCollection.ShaderVariant? sv = null;
                try
                {
                    string[] key_worlds = sd.KeyWords[i];
    
                    //变体交集 大于0 ,添加到 svcList
                    sv = new ShaderVariantCollection.ShaderVariant(curMat.shader, pt, key_worlds);
                    SetShaderVariantKeyWorld(svlist, sv);
                }
                catch (Exception e)
                {
                    Debug.LogErrorFormat("{0}-当前shader不存在变体(可以无视):{1}-{2}", curMat.name, pt, curMat.shaderKeywords.ToString());
                    continue;
                }
    
    
            }
        }
    
        static void SetShaderVariantKeyWorld(List<ShaderVariantCollection.ShaderVariant> svlist, ShaderVariantCollection.ShaderVariant? sv)
        {
            //判断sv 是否存在,不存在则添加
            if (sv != null)
            {
                bool isContain = false;
                var _sv = (ShaderVariantCollection.ShaderVariant)sv;
                foreach (var val in svlist)
                {
                    if (val.passType == _sv.passType && System.Linq.Enumerable.SequenceEqual(val.keywords, _sv.keywords))
                    {
                        isContain = true;
                        break;
                    }
                }
    
                if (!isContain)
                {
                    svlist.Add(_sv);
                }
            }
        }
    
    
        static MethodInfo GetShaderVariantEntries = null;
    
        static ShaderVariantCollection toolSVC = null;
    
        //获取shader的 keywords
        public static ShaderData GetShaderKeywords(Shader shader)
        {
            ShaderData sd = new ShaderData();
            GetShaderVariantEntriesFiltered(shader, new string[] { }, out sd.PassTypes, out sd.KeyWords, out sd.ReMainingKeyWords);
            return sd;
        }
    
        /// <summary>
        /// 获取keyword
        /// </summary>
        /// <param name="shader"></param>
        /// <param name="filterKeywords"></param>
        /// <param name="passTypes"></param>
        /// <param name="keywordLists"></param>
        /// <param name="remainingKeywords"></param>
        static void GetShaderVariantEntriesFiltered(Shader shader, string[] filterKeywords, out int[] passTypes, out string[][] keywordLists, out string[] remainingKeywords)
        {
            //2019.3接口
            //            internal static void GetShaderVariantEntriesFiltered(
            //                Shader                  shader,                     0
            //                int                     maxEntries,                 1
            //                string[]                filterKeywords,             2
            //                ShaderVariantCollection excludeCollection,          3
            //                out int[]               passTypes,                  4
            //                out string[]            keywordLists,               5
            //                out string[]            remainingKeywords)          6
            if (GetShaderVariantEntries == null)
            {
                GetShaderVariantEntries = typeof(ShaderUtil).GetMethod("GetShaderVariantEntriesFiltered", BindingFlags.NonPublic | BindingFlags.Static);
            }
    
            passTypes = new int[] { };
            keywordLists = new string[][] { };
            remainingKeywords = new string[] { };
            if (toolSVC != null)
            {
                var _passtypes = new int[] { };
                var _keywords = new string[] { };
                var _remainingKeywords = new string[] { };
                object[] args = new object[] { shader, 256, filterKeywords, toolSVC, _passtypes, _keywords, _remainingKeywords };
                GetShaderVariantEntries.Invoke(null, args);
    
                var passtypes = args[4] as int[];
                passTypes = passtypes;
                //key word
                keywordLists = new string[passtypes.Length][];
                var kws = args[5] as string[];
                for (int i = 0; i < passtypes.Length; i++)
                {
                    keywordLists[i] = kws[i].Split(' ');
                }
    
                //Remaning key word
                var rnkws = args[6] as string[];
                remainingKeywords = rnkws;
            }
        }
    }
  6. 方案6,自动收集变体代码2,测试。这里留一篇文章,我觉得很对,思路很清晰,易懂,可以看看GitHub - lujian101/ShaderVariantCollector
  7. 方案7,手撸Shader Variant Collection文件,同样不全,还不咋好用。

 最后贴几篇参考:

【Unity3D】Shader变体管理流程2-变体收集 - 简书

GitHub - lujian101/ShaderVariantCollector

爬坑篇(3)之 Unity2019 keyword坑(又名:性感程序化身名侦探) - 知乎

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智能合约是一种在区块链上执行的自动化合约。Python是一种常用的编程语言,也可以用于编写智能合约。在Python中,可以使用一些库来编写智能合约,例如Web3.py和Solidity.py。下面是一个使用Web3.py库编写智能合约的示例: ```python from web3 import Web3 # 连接到以太坊节点 w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/your-infura-project-id')) # 加载智能合约ABI contract_abi = [ { "constant": False, "inputs": [ { "name": "x", "type": "uint256" } ], "name": "set", "outputs": [], "payable": False, "stateMutability": "nonpayable", "type": "function" }, { "constant": True, "inputs": [], "name": "get", "outputs": [ { "name": "", "type": "uint256" } ], "payable": False, "stateMutability": "view", "type": "function" } ] # 部署智能合约 contract_address = '0x1234567890abcdef1234567890abcdef12345678' contract = w3.eth.contract(address=contract_address, abi=contract_abi) # 调用智能合约方法 transaction = contract.functions.set(42).buildTransaction({ 'from': w3.eth.accounts[0], 'gas': 100000, 'gasPrice': w3.toWei('1', 'gwei'), 'nonce': w3.eth.getTransactionCount(w3.eth.accounts[0]) }) signed_transaction = w3.eth.account.signTransaction(transaction, private_key='your-private-key') transaction_hash = w3.eth.sendRawTransaction(signed_transaction.rawTransaction) transaction_receipt = w3.eth.waitForTransactionReceipt(transaction_hash) # 获取智能合约状态 result = contract.functions.get().call() print("智能合约状态:", result) ``` 请注意,上述示例中的合约ABI和地址是虚构的,您需要根据您自己的合约来替换它们。此外,您还需要替换连接到以太坊节点的URL和私钥。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值