Unity ShaderVariant 变体收集方案分析

这篇具有很好参考价值的文章主要介绍了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包内,效果正常。unity 变体收集,Shader,unity,游戏引擎
  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文件,同样不全,还不咋好用。unity 变体收集,Shader,unity,游戏引擎

 最后贴几篇参考:

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

GitHub - lujian101/ShaderVariantCollector

爬坑篇(3)之 Unity2019 keyword坑(又名:性感程序化身名侦探) - 知乎文章来源地址https://www.toymoban.com/news/detail-592659.html

到了这里,关于Unity ShaderVariant 变体收集方案分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity 性能优化之Shader分析处理函数ShaderUtil.HasProceduralInstancing: 深入解析与实用案例

    点击封面跳转到Unity国际版下载页面 在Unity中,性能优化是游戏开发过程中非常重要的一环。其中,Shader的优化对于游戏的性能提升起着至关重要的作用。本文将深入解析Unity中的Shader处理函数 ShaderUtil.HasProceduralInstancing ,并提供一些实用案例来展示其用法。 ShaderUtil.HasProce

    2024年02月09日
    浏览(42)
  • Unity -- 预制体与变体

     1.一个游戏物体的图标为蓝色的时候,表明这个游戏物体是通过预制体创建的 当我们在修改预制体的时候,通过预制体创建的游戏物体也会同步修改(给预制体添加组件的时候,通过预制体创建的游戏对象也会添加对应的组件) 而当我们修改通过预制体创建的游戏物体时,

    2023年04月27日
    浏览(37)
  • Unity中AssetBundle的变体(Varient)使用教程

    在使用Unity的AssetBundle过程中,选中资源物体之后,发现右下角的AssetBundle面板,除了有一个命名标签外,还有一个Varient标签,这边文章主要介绍下这个标签的用法,如下图: 多语言 :在做游戏开发的过程中,会遇到多语言问题,特别是现在推行的游戏全球化,所以支持多语

    2024年02月16日
    浏览(44)
  • 详解Unity预制体和预制体变体(Prefab Variant)

    一、预制体变体的创建 二、预制体与变体的绑定 三、预制体到变体的继承关系 四、变体常见API 五、变体用途 通过一个预制体创建预制体变体可以通过以下两种方法: 1.在需要创建变体的预制体上右键选择图中选项即可在预制体当前目录下创建预制体变体。  2.通过脚本进行

    2024年01月17日
    浏览(42)
  • 【Unity编辑器扩展】包体优化神器,图片压缩,批量生成图集/图集变体,动画压缩

    功能介绍: 1. 压缩工具支持对图片原文件压缩(支持png/jpg),也支持使用Unity内置图片压缩批量对图片设置压缩参数。 2. 支持以文件夹或及其子文件夹为单位批量生成图集(SpriteAtlas), 支持同时生成图集变体(SpriteAtlas Variant),支持忽略像素宽高大于限定值的图片打进图集。 3. 批

    2023年04月10日
    浏览(46)
  • 【Unity Shader】Unity阴影

    记录下在unity中如果想实现阴影,有哪些路子可以选择,目前看有两种 1.经典的shadowmap 2.planar projection 如果开启renderer组件的cast shadows为on,开启平行光的light组件的shadow type,那么就会在物体shader中寻找LightMode=ShadowCaster的Pass进行渲染 场景有两个物体,平面和球体,使用unity内

    2024年02月09日
    浏览(59)
  • Unity | Shader基础知识(第一集:unity中最简单的shader)

    目录 一、unity的shader 二、创建一个shader(在创建时,选前三种都可以) 三、内容解读 1.shader一直都在 2.我们写shader在写什么 四、没有被干预的shader(最简单的shader) 相关阅读 编写着色器概述 - Unity 手册 一、unity的shader unity写的shader并不是真正意义上的shader。 官方解释:

    2024年02月04日
    浏览(53)
  • Unity - 记一次非正规变体优化带来的兼容性导致部分手机卡死的问题

    在 2023.4.6 我们的 角色展示界面 就遇到了 华为手机,red mi note 11 的测试手机上的 后 2023.5.24 再次遇到类似的问题,但是这次重现的地方很多,不单止 角色展示界面 遇到 排除过: 模型 特效 场景 人物 材质 后来多次排查,发现是 PBR 所有的 变体拆分优化 的文件导致阴影部分

    2024年02月08日
    浏览(47)
  • 【Unity Shader】Unity前向渲染

    ForwardBase Pass(优先渲染),渲染一个逐像素平行光和所有的顶点/球面调和光,阴影只和平行光有关系,那阴影应该是这个Pass中实现的 ForwardAdd Pass(需要和Base配合使用,否则不生效),渲染剩余全部逐像素灯光 Unity会根据场景中各个光源的设置以及这些光源对物体的影响程

    2024年02月08日
    浏览(59)
  • 【Unity Shader】Shader Graph

    shader graph 入门: (对基础内容进行详细介绍) Shader Graph入门-CSDN博客 unity-shader(入门)_unity3d shader-CSDN博客 各种效果的节点配置: 【unity造轮子】Unity ShaderGraph使用教程与各种特效案例(2023/12/1更新)_unity特效-CSDN博客 大佬文章里所用的PBR Master更新后已经没有了,所以参考

    2024年02月01日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包