Unity场景物体动态合批

这篇具有很好参考价值的文章主要介绍了Unity场景物体动态合批。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1)Unity场景物体动态合批
​2)Unity内置音频Android平台播放延迟问题
3)对Unity Package中的Shader打包避免冗余的方案
4)UnityEditor PropertyField并排显示错误


这是第307篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Rendering

Q1:问题1:为了能使场景中物体合批(同材质球),场景在制作的时候相同的预制体放在了一起,但是FrameDebug看到的却是乱序。请问在这个Frame里的顺序是如何定义才能达到相同的合批?也就是如何能最少化的Objects have different materials?

Unity场景物体动态合批

Unity场景物体动态合批

问题2:在FrameDebug上下排列且相同物体,依然没有合批。给出的原因是“Objects are lightmapped”。网上给出的解释是使用了不同的光照贴图(这里是一样的),或在相同的光照贴图中由不同的光照贴图UV转换关系。这个转换关系是什么意思?是UV对应的Offset几个参数必须要一样么?但是物体在不同的位置,理论上一定会不一样。请问这个要怎么解决?目前场景中物体占用了120个DrawCall,想尽可能优化。

Unity场景物体动态合批

A:第一个问题,针对不透明物体,Unity默认由近往远绘制(离相机的距离),所以如果想要相同的材质球是连续绘制的,可以通过调整RenderQueue来强行连续绘制,不过可能会导致渲染效果不符合预期(遮挡关系错乱),另外的坏处是会破坏Early-Z的功能,尤其在没有HSR功能的低端机上,Overdraw会造成高复杂度的Shader带来的GPU高压力,所以需要权衡CPU提交Batch的耗时和GPU的压力。

第二个问题,同类型材质球(Shader一样,变体也一样),如果Lightmap index不一样(也就是Lightmap不一样),肯定是不能合批的(SRP Batcher除外,SRP Batch无视材质球,只要Shader和变体一样就可以)。在Lightmap相同的情况下,如果对应的unity_LightmapST不一样,也是不能合批的。这里有两种方法解决,一种是用Static Batching,这样会让unity_LightmapST变成同一种,具体的UV会变成顶点数据(本来子Mesh的UV都是(0,1)的,合并Combined Mesh后会变成类似于(0.3,0.5)这样的区间),就是说顶点里面的UV本来存储的是Local空间的,变成Combined Mesh后,合体的Mesh数据里面存储的是World空间的,这样外在的unity_LightmapST对于不同的子Mesh就会变成共用的(1,1,0,0)这样的共同属性,也就可以合批了。另外一种是开启GPU Instancing,这样unity_LightmapST会变成CBuffer,这样也是会合批的。

如下图所示,Cube1和Cube2在FrameDebugger的唯一区别就是unity_LightmapST不一样,所以不开启GPU Instancing和Static Batching的情况下,是不能合批的,虽然它们是相邻的绘制顺序,Lightmap也是相同的。另外两个Cube(1)和Cube分别用的Lightmap2和Lightmap0。

Unity场景物体动态合批

Unity场景物体动态合批

Unity场景物体动态合批

开启静态合批后,中间的2个Cube合成了一个Batch,它们的unity_LightmapST变成了共同的(1,1,0,0),如下图:

Unity场景物体动态合批

材质球勾选GPU Instancing后,在FrameDebugger面板里面,已经看不到unity_LightmapST这个属性了。这两个Cube也是合成了一个Instanced Batch。如下图:

Unity场景物体动态合批

感谢Xuan@UWA问答社区提供了回答

Q2:对于第二个问题的解决方法。因为我们是动态创建物体。所以无法用静态合批。当我尝试使用GPU Instancing 后,批次几乎没变,一种是我们的不同的Mesh,使用同一个材质球,无法合批;其次是同一个Mesh也无法合批。如下提示:

Unity场景物体动态合批

另外依然还有“Objects are lightmaped”的提示,且已经没有Lightmap_ST,材质球都已勾选GPU:

Unity场景物体动态合批

还有Shader是有两个PASS的。是否另一个PASS也需要加入GPU Instancing 的代码?附Shader代码:

Shader "WF/SceneObj"
{
    Properties
    {
        [NoScaleOffset]_MainTex("主贴图 (RGBA)", 2D) = "white" {}
        [Space(20)]
        _OutlineWidth("描边宽度", Range(0, 0.5)) = 0.018
        _OutlineColor("描边颜色", Color) = (0, 0, 0, 1)
        [Space(20)]
        _EmissionMask("自发光遮罩 (G)", Range(0, 5)) = 1
        [HDR]_EmissionColor("自发光颜色", Color) = (1, 1, 1, 1)
        _Color("Color", Color) = (1, 1, 1, 1)
    }

        SubShader
        {

            Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+1" }
            // lightmap
            Pass
            {
                Tags { "LightMode" = "ForwardBase" }

                CGPROGRAM
                #pragma multi_compile_fwdbase
                #pragma multi_compile_instancing
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"
                #include "Lighting.cginc"
                #include "AutoLight.cginc"
                #pragma multi_compile_fog
                #define USING_FOG (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))

            struct a2v
                {
                    float4 vertex : POSITION;
                    float3 normal : NORMAL;
                    float2 texcoord : TEXCOORD0;
                    float3 uv1 : TEXCOORD1; // lightmap
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct v2f
                {
                    float4 pos : SV_POSITION;
                    float2 uv : TEXCOORD0;
                    float2 uv1 : TEXCOORD2;
                    half3 worldlight : TEXCOORD1;
#if USING_FOG
                        fixed fog : TEXCOORD3;
#endif
                    UNITY_VERTEX_OUTPUT_STEREO
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;

                float _EmissionMask;
                fixed4 _EmissionColor;

                UNITY_INSTANCING_BUFFER_START(Props)
                    UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
                    UNITY_INSTANCING_BUFFER_END(Props)


                v2f vert(a2v v)
                {
                    v2f o;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_TRANSFER_INSTANCE_ID(v, o);

                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    // 
                    o.worldlight = UnityObjectToViewPos(v.normal);
                    o.uv1 = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;

                    // fog
#if USING_FOG
                    float3 eyePos = UnityObjectToViewPos(v.vertex);
                    float fogCoord = length(eyePos.xyz);  // radial fog distance
                    UNITY_CALC_FOG_FACTOR_RAW(fogCoord);
                    o.fog = saturate(unityFogFactor);
#endif

                    return o;
                }

                fixed4 frag(v2f i) : SV_Target
                {
                    UNITY_SETUP_INSTANCE_ID(i);
                    fixed4 originalCol = tex2D(_MainTex, i.uv) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);

                    ------------------------------漫反射------------------------------
                    //-----------------------------------自发光
                    //float emission = originalCol.a * _EmissionMask;
                    //自发光
                    //float emisstionValue = emission < 0.5 ? 0 : 1;
                    fixed3 emisstionCol = originalCol.rgb * _EmissionColor.rgb * _EmissionMask;
                    //
                    //叠加
                    fixed3 finnalCol = originalCol;
                    fixed4 col = (fixed4)1;
#if defined(LIGHTMAP_ON)
                    //
                    half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv1.xy);

                    col.rgb = DecodeLightmap(bakedColorTex);
#endif
                    finnalCol = lerp(finnalCol, emisstionCol, originalCol.a) * col.rgb;

                    // fog
#if USING_FOG
                    finnalCol.rgb = lerp(unity_FogColor.rgb, finnalCol.rgb, i.fog);
#endif

                    return fixed4(finnalCol, 1);
                }
                ENDCG
            }

            Pass
            {
                NAME "OUTLINE"
                Cull Front

                CGPROGRAM
                #pragma multi_compile_fog
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

                struct a2v
                {
                    float4 vertex : POSITION;
                    float3 normal : NORMAL;
                };

                struct v2f
                {
                    float4 pos : SV_POSITION;
                    UNITY_FOG_COORDS(0)
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;
                fixed4 _OutlineColor;
                float _OutlineWidth;
                v2f vert(a2v v)
                {
                    v2f o;
                    o.pos = UnityObjectToClipPos(v.vertex);
                    float3 viewNormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
                    float2 ndcNormal = normalize(TransformViewToProjection(viewNormal.xy)) * pow(o.pos.w, 0.6) * 2;
                    float4 nearUpperRight = mul(unity_CameraInvProjection, float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y));
                    float aspect = abs(nearUpperRight.y / nearUpperRight.x);
                    ndcNormal.x *= aspect;
                    o.pos.xy += ndcNormal * _OutlineWidth * 0.1;
                    UNITY_TRANSFER_FOG(o,o.pos);
                    return o;
                }

                fixed4 frag(v2f i) : SV_Target
                {
                    fixed3 col = _OutlineColor.rgb ;
                    return fixed4(col, 0);
                }
                ENDCG
            }
        }

            Fallback "Diffuse"

A:动态创建物体的静态合批的方法可以参考StaticBatchingUtility.Combine的API,这是一项运行时静态合批的做法;其次如果使用了多个Pass的Shader,想要使用GPU Instancing进行合批,每个Pass都要设置成GPU Instancing才能正常合批。

感谢宗卉轩@UWA问答社区提供了回答,欢迎大家转至社区交流:
收集完Shader Variants后,URP/UberPost 变成双份了,怎么删掉? -- UWA问答 | 游戏开发者互动问答社区 | 侑虎科技


Audio

Q:最近遇到Unity使用内置音频方案在Android平台音频延迟问题,而且是有时候会延迟。不知道各位使用的是什么方案来解决这个问题?

A:安卓延迟比较大是比较正常的,特别是在一些低端机上。

可以先试试Unity内置的延迟解决方案:ProjectSettings的Audio中的DSP Buffer Size设置为Best latency,然后短音效的AudioType改为Decomppress On Load。

如果无法解决 可以尝试一下官方提供的插件:NativeAudio SDK

如果不想用Unity内置的FMOD,可以尝试一下以下方案:Wwise、Criware

感谢萧小俊@UWA问答社区提供了回答,欢迎大家转至社区交流:
收集完Shader Variants后,URP/UberPost 变成双份了,怎么删掉? -- UWA问答 | 游戏开发者互动问答社区 | 侑虎科技


AssetBundle

Q:收集完Shader Variants后,URP/UberPost变成双份了,怎么删掉?

Unity场景物体动态合批

Unity场景物体动态合批

A:这个问题我的判断是UberPost没有被指定AssetBundle,所以在打包SVC文件时,增加了一个额外的引用。而此时SVC文件需要指定AssetBundle,就将其依赖的未指定AssetBundle文件额外添加了进来。

这个问题的难点是:UberPost是在Packages下的,而Packages文件无法指定AssetBundle。

解决方案1:将URP放到工程中,而非Packages下,这样就可以指定AssetBundle。
解决方案2:改用了可以修改AssetBundle关联性的ScriptableBuildPipeline,手动为UberPost指定打包到某个AssetBundle中,这样打包系统检测到UberPost已有AssetBundle,就不会重复打包了。

感谢小枫不会飞@UWA问答社区提供了回答,欢迎大家转至社区交流:
收集完Shader Variants后,URP/UberPost 变成双份了,怎么删掉? -- UWA问答 | 游戏开发者互动问答社区 | 侑虎科技


Editor

Q:我在制作一个工具,用来编辑很多内容,其中我有一个需求,就是在Editor窗口中去修改List的内容,比如添加新的项、删除和修改等。

我是用EditorGUILayout.PropertyField,效果如下:

Unity场景物体动态合批

但是我需要在若干列中展示多个PropertyField,此时效果成了这样子,可以明显看到第二个列表的文本框短了一大截:

Unity场景物体动态合批

如果是三个的话,根本就不显示:

Unity场景物体动态合批

我限制了前两个的长度,然后拉长界面,可以发现其实他可以显示,就是离得太远了:

Unity场景物体动态合批

求问这种情况怎么解决?以下是源码:

public class TestWindow : EditorWindow
    {

        [MenuItem("test2/open")]
        private static void Test()
        {
            var s_CurrentWindow = GetWindow<TestWindow>("My Tool");
            s_CurrentWindow.Show();

        }
        XmlNode node;
        XmlNode node2;
        XmlNode node3;
        SerializedObject serObj;
        SerializedProperty serPty;

        SerializedObject serObj2;
        SerializedProperty serPty2;

        SerializedObject serObj3;
        SerializedProperty serPty3;

        private void OnGUI()
        {
            using (var scope2 = new EditorGUILayout.HorizontalScope())
            {
                serObj.Update();
                serObj2.Update();
                serObj3.Update();
                EditorGUI.BeginChangeCheck();

                using (var scope = new EditorGUILayout.VerticalScope(GUILayout.Width(500)))
                {
                    //temp = EditorGUILayout.TextField("What: ", temp);
                    EditorGUILayout.PropertyField(serPty, true);
                }

                using (var scope = new EditorGUILayout.VerticalScope(GUILayout.Width(500)))
                {
                    //temp = EditorGUILayout.TextField("What: ", temp);
                    EditorGUILayout.PropertyField(serPty2, true);
                }

                using (var scope = new EditorGUILayout.VerticalScope())
                {

                    EditorGUILayout.PropertyField(serPty3, true, GUILayout.ExpandWidth(true));
                }

                if (EditorGUI.EndChangeCheck())
                {
                    serObj.ApplyModifiedProperties();
                    serObj2.ApplyModifiedProperties();
                    serObj3.ApplyModifiedProperties();
                }
            }
        }

        private void OnEnable()
        {
            //var titleRow = NewRow(
            //    new Label() { text = "Old" },
            //    new Label() { text = "new" }
            //    );
            //titleRow.name = "title-compare-box";
            //rootVisualElement.Add(titleRow);
            //rootVisualElement

            node = ScriptableObject.CreateInstance<XmlNode>();
            node2 = ScriptableObject.CreateInstance<XmlNode>();
            node3 = ScriptableObject.CreateInstance<XmlNode>();

            node.InnerTextList.Add("QwQ");
            node.CType = ContentType.List;
            node2.InnerTextList.Add("heiheihie");
            node2.CType = ContentType.List;
            node3.InnerTextList.Add("WWW");
            node3.CType = ContentType.List;

            serObj = new SerializedObject(node);
            serPty = serObj.FindProperty("InnerTextList");
            serObj2 = new SerializedObject(node2);
            serPty2 = serObj2.FindProperty("InnerTextList");
            serObj3 = new SerializedObject(node3);
            serPty3 = serObj3.FindProperty("InnerTextList");
        }
        private class XmlNode : ScriptableObject
        {
            public string Path { get; set; }
            public string Name { get; set; }
            public ContentType CType { get; set; }
            public Dictionary<string, string> AttributeDict { get; set; } = new Dictionary<string, string>();
            public List<string> InnerTextList = new List<string>();
            public string InnerText { get; set; }

            //public XmlNode(string path, string name, ContentType cType)
            //{
            //    Path = path;
            //    Name = name;
            //    CType = cType;
            //    AttributeDict = new Dictionary<string, string>();
            //    InnerTextList = new List<string>();
            //}
        }

        private enum ContentType
        {
            String, // 单行文本 InnerText
            List // 多行的内容,用List存
        }
    }

A1:建议直接使用Odin插件。

感谢张迪@UWA问答社区提供了回答

A2:这种List在Editor下可以使用ReorderableList,供EditorGUI使用,表现看起来跟你的需求差不多。

Unity场景物体动态合批

Unity场景物体动态合批

感谢范世青@UWA问答社区提供了回答,欢迎大家转至社区交流:
收集完Shader Variants后,URP/UberPost 变成双份了,怎么删掉? -- UWA问答 | 游戏开发者互动问答社区 | 侑虎科技

封面图来源于网络


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)
文章来源地址https://www.toymoban.com/news/detail-476929.html

到了这里,关于Unity场景物体动态合批的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Unity获取场景中所有根物体

            每个物体都属于一个Scene,而通过Scene.GetRootGameObjects()接口就可以获得该Scene的所有根物体,我们只需要遍历所有Scene即可         而被DontDestroyOnLoad处理过的物体就不再属于原来的场景了,它们被分配到一个隐藏的叫做DontDestroyOnLoad的新场景里。这个场景无法通

    2024年02月11日
    浏览(43)
  • Unity中查找场景中物体的方法

    unity中查找物体的方法一共分为四种 在代码中定义后直接拖拽上去即可识别 第一步先设置Tag:(可以通过Add Tag添加自己需要的标签) ps:本人在校大学生一枚,单纯写自己学习unity的经验分享,文章内容如有错误,希望各位指正!🙏🏼🙏🏼

    2024年02月11日
    浏览(35)
  • 笔记:Unity多相机场景下,物体显示常用设置

    1、主相机:MainCamera; 2、子相机:ClippingCamera、FieldViewCamera、UICamera: 注:为了减少干扰 每个子相机仅显示特定物体,不和主相机同时渲染; 每个物体也仅被唯一的相机渲染; 3、把子相机显示内容按不同先后顺序渲染到主相机里方法: (1)将子相机添加进MainCamera的Stack模

    2023年04月08日
    浏览(38)
  • Unity3D-场景中3D物体添加点击事件

    Unity3D - 场景中3D物体添加鼠标点击事件 鼠标点击3D物体触发,Unity从本质上来说有两种:一种是通过事件(event)触发,一种是通过射线(ray)判断穿过的物体触发。这两种触发的原理是不同的,不论哪种触发都必须满足触发的要求才可以,既然原理不同,触发的要求也不一样

    2024年02月08日
    浏览(87)
  • 使用DoTween插件在Unity中实现物体或场景的振动效果

    在Unity中,我们可以使用DoTween插件来实现物体或场景的振动效果。DoTween是一个功能强大的插件,可以简化动画的创建和管理过程,并提供了丰富的插值和缓动函数,使得振动效果的实现变得更加容易。 首先,我们需要确保已经安装了DoTween插件,并将其导入到Unity项目中。接

    2024年04月17日
    浏览(73)
  • unity中:从主场景切换到另一个子场景后,子场景里的3D物体都变黑,单独播放该场景,是正常的

    从主场景切换到另一个子场景后,子场景里的3D物体都变黑,UI是正常的,可以看见,单独播放该场景,是正常的。 这种情况通常是由于Unity的光照系统和场景之间的不兼容性导致的。当你从一个场景切换到另一个场景时,Unity会自动卸载旧场景中的所有对象和光照信息,并加

    2023年04月20日
    浏览(49)
  • unity给子物体动态添加boxcollider(碰撞盒)

    在工作中遇到过给物体拍照截图的功能,由于物体是动态加载并且大小不一,但是要求拍的照片正好被物体填充,因此需要动态计算物体大小,从而进行调整,这种方法也可以进行相机检测物体是否在视野内。使用方法非常简单,只需要将要计算的物体放到空物体的子物体下

    2024年02月11日
    浏览(37)
  • unity NGO 代码教程:网络上动态生成物体

    生成一个网络对象有多种办法,但始终 只能由Server/Host生成/销毁 必须有network  object组件 要在NetworkManager中的NetworkPrefebList中注册 建议先看完第一章:unity netcode for gameobject(NGO)逻辑代码教程-CSDN博客  简单的Debug程序: 生成 销毁 注意Despawn会默认销毁对象 ,可以理解为在其后调

    2024年02月02日
    浏览(45)
  • 【Unity3D】使用 FBX 格式的外部模型 ( 向 Unity 中添加 FBX 模型 | 向 Scene 场景中添加 FBX 模型 | 3D 物体渲染 | 3D 物体材质设置 )

    Unity 中使用的 3D 模型格式为 FBX , 使用如下建模软件 可制作该类型模型 : 3Dmax Maya ZBrush Cinema4D Blender 建模完成后 , 将 3D 模型导出为 FBX ( .fbx ) 格式 即可在 Unity 中使用 ; 在 Project 文件窗口 中的 Asstes 目录 下 , 创建一个模型目录 Models , 将 模型文件直接从文件系统中拖到该目录中

    2023年04月15日
    浏览(82)
  • unity 性能优化之合批和剔除

    批次对渲染的性能影响是比较大的,批次过多会导致cpu提交的次数过多,导致每帧渲染时间过长,所以我们需要对其优化,减少Bathches数量和SetPassCall次数。 批次合并的方法有多种,下面一一列出: 将相同材质的Mesh,合并为一个新的Mesh,这样一次渲染,最方便调节,虽然现

    2024年02月15日
    浏览(40)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包