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?
问题 2:在 FrameDebug 高低排列且雷同物体,仍然没有合批。给出的起因是“Objects are lightmapped”。网上给出的解释是应用了不同的光照贴图(这里是一样的),或在雷同的光照贴图中由不同的光照贴图 UV 转换关系。这个转换关系是什么意思?是 UV 对应的 Offset 几个参数必须要一样么?然而物体在不同的地位,实践上肯定会不一样。请问这个要怎么解决?目前场景中物体占用了 120 个 DrawCall,想尽可能优化。
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。
开启动态合批后,两头的 2 个 Cube 合成了一个 Batch,它们的 unity_LightmapST 变成了独特的(1,1,0,0),如下图:
材质球勾选 GPU Instancing 后,在 FrameDebugger 面板外面,曾经看不到 unity_LightmapST 这个属性了。这两个 Cube 也是合成了一个 Instanced Batch。如下图:
感激 Xuan@UWA 问答社区提供了答复
Q2:对于第二个问题的解决办法。因为咱们是动态创建物体。所以无奈用动态合批。当我尝试应用 GPU Instancing 后,批次简直没变,一种是咱们的不同的 Mesh,应用同一个材质球,无奈合批;其次是同一个 Mesh 也无奈合批。如下提醒:
另外仍然还有“Objects are lightmaped”的提醒,且曾经没有 Lightmap_ST,材质球都已勾选 GPU:
还有 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 问答社区提供了答复
Audio
Q:最近遇到 Unity 应用内置音频计划在 Android 平台音频提早问题,而且是有时候会提早。不晓得各位应用的是什么计划来解决这个问题?
A:安卓提早比拟大是比拟失常的,特地是在一些低端机上。<br/>
能够先试试 Unity 内置的提早解决方案:ProjectSettings 的 Audio 中的 DSP Buffer Size 设置为 Best latency,而后短音效的 AudioType 改为 Decomppress On Load。<br/>
如果无奈解决 能够尝试一下官网提供的插件:NativeAudio SDK<br/>
如果不想用 Unity 内置的 FMOD,能够尝试一下以下计划:Wwise、Criware
感激萧小俊 @UWA 问答社区提供了答复
AssetBundle
Q:收集完 Shader Variants 后,URP/UberPost 变成双份了,怎么删掉?
A:这个问题我的判断是 UberPost 没有被指定 AssetBundle,所以在打包 SVC 文件时,减少了一个额定的援用。而此时 SVC 文件须要指定 AssetBundle,就将其依赖的未指定 AssetBundle 文件额定增加了进来。
这个问题的难点是:UberPost 是在 Packages 下的,而 Packages 文件无奈指定 AssetBundle。
解决方案 1:将 URP 放到工程中,而非 Packages 下,这样就能够指定 AssetBundle。
解决方案 2:改用了能够批改 AssetBundle 关联性的 ScriptableBuildPipeline,手动为 UberPost 指定打包到某个 AssetBundle 中,这样打包零碎检测到 UberPost 已有 AssetBundle,就不会反复打包了。
感激小枫不会飞 @UWA 问答社区提供了答复
Editor
Q:我在制作一个工具,用来编辑很多内容,其中我有一个需要,就是在 Editor 窗口中去批改 List 的内容,比方增加新的项、删除和批改等。
我是用 EditorGUILayout.PropertyField,成果如下:
然而我须要在若干列中展现多个 PropertyField,此时成果成了这样子,能够显著看到第二个列表的文本框短了一大截:
如果是三个的话,基本就不显示:
我限度了前两个的长度,而后拉长界面,能够发现其实他能够显示,就是离得太远了:
求问这种状况怎么解决?以下是源码:
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 应用,体现看起来跟你的需要差不多。
感激范世青 @UWA 问答社区提供了答复
封面图来源于网络
明天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题兴许都只是冰山一角,咱们早已在 UWA 问答网站上筹备了更多的技术话题等你一起来摸索和分享。欢送酷爱提高的你退出,兴许你的办法恰能解他人的当务之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官网技术博客:blog.uwa4d.com
官网问答社区:answer.uwa4d.com
UWA 学堂:edu.uwa4d.com
官网技术 QQ 群:793972859(原群已满员)