1)Unity場景物體動(dòng)態(tài)合批
?2)Unity內(nèi)置音頻Android平臺(tái)播放延遲問題
3)對(duì)Unity Package中的Shader打包避免冗余的方案
4)UnityEditor PropertyField并排顯示錯(cuò)誤
這是第307篇UWA技術(shù)知識(shí)分享的推送。今天我們繼續(xù)為大家精選了若干和開發(fā)、優(yōu)化相關(guān)的問題,建議閱讀時(shí)間10分鐘,認(rèn)真讀完必有收獲。
UWA 問答社區(qū):answer.uwa4d.com
UWA QQ群2:793972859(原群已滿員)
Rendering
Q1:問題1:為了能使場景中物體合批(同材質(zhì)球),場景在制作的時(shí)候相同的預(yù)制體放在了一起,但是FrameDebug看到的卻是亂序。請(qǐng)問在這個(gè)Frame里的順序是如何定義才能達(dá)到相同的合批?也就是如何能最少化的Objects have different materials?
問題2:在FrameDebug上下排列且相同物體,依然沒有合批。給出的原因是“Objects are lightmapped”。網(wǎng)上給出的解釋是使用了不同的光照貼圖(這里是一樣的),或在相同的光照貼圖中由不同的光照貼圖UV轉(zhuǎn)換關(guān)系。這個(gè)轉(zhuǎn)換關(guān)系是什么意思?是UV對(duì)應(yīng)的Offset幾個(gè)參數(shù)必須要一樣么?但是物體在不同的位置,理論上一定會(huì)不一樣。請(qǐng)問這個(gè)要怎么解決?目前場景中物體占用了120個(gè)DrawCall,想盡可能優(yōu)化。
A:第一個(gè)問題,針對(duì)不透明物體,Unity默認(rèn)由近往遠(yuǎn)繪制(離相機(jī)的距離),所以如果想要相同的材質(zhì)球是連續(xù)繪制的,可以通過調(diào)整RenderQueue來強(qiáng)行連續(xù)繪制,不過可能會(huì)導(dǎo)致渲染效果不符合預(yù)期(遮擋關(guān)系錯(cuò)亂),另外的壞處是會(huì)破壞Early-Z的功能,尤其在沒有HSR功能的低端機(jī)上,Overdraw會(huì)造成高復(fù)雜度的Shader帶來的GPU高壓力,所以需要權(quán)衡CPU提交Batch的耗時(shí)和GPU的壓力。
第二個(gè)問題,同類型材質(zhì)球(Shader一樣,變體也一樣),如果Lightmap index不一樣(也就是Lightmap不一樣),肯定是不能合批的(SRP Batcher除外,SRP Batch無視材質(zhì)球,只要Shader和變體一樣就可以)。在Lightmap相同的情況下,如果對(duì)應(yīng)的unity_LightmapST不一樣,也是不能合批的。這里有兩種方法解決,一種是用Static Batching,這樣會(huì)讓unity_LightmapST變成同一種,具體的UV會(huì)變成頂點(diǎn)數(shù)據(jù)(本來子Mesh的UV都是(0,1)的,合并Combined Mesh后會(huì)變成類似于(0.3,0.5)這樣的區(qū)間),就是說頂點(diǎn)里面的UV本來存儲(chǔ)的是Local空間的,變成Combined Mesh后,合體的Mesh數(shù)據(jù)里面存儲(chǔ)的是World空間的,這樣外在的unity_LightmapST對(duì)于不同的子Mesh就會(huì)變成共用的(1,1,0,0)這樣的共同屬性,也就可以合批了。另外一種是開啟GPU Instancing,這樣unity_LightmapST會(huì)變成CBuffer,這樣也是會(huì)合批的。
如下圖所示,Cube1和Cube2在FrameDebugger的唯一區(qū)別就是unity_LightmapST不一樣,所以不開啟GPU Instancing和Static Batching的情況下,是不能合批的,雖然它們是相鄰的繪制順序,Lightmap也是相同的。另外兩個(gè)Cube(1)和Cube分別用的Lightmap2和Lightmap0。
開啟靜態(tài)合批后,中間的2個(gè)Cube合成了一個(gè)Batch,它們的unity_LightmapST變成了共同的(1,1,0,0),如下圖:
材質(zhì)球勾選GPU Instancing后,在FrameDebugger面板里面,已經(jīng)看不到unity_LightmapST這個(gè)屬性了。這兩個(gè)Cube也是合成了一個(gè)Instanced Batch。如下圖:
感謝Xuan@UWA問答社區(qū)提供了回答
Q2:對(duì)于第二個(gè)問題的解決方法。因?yàn)槲覀兪莿?dòng)態(tài)創(chuàng)建物體。所以無法用靜態(tài)合批。當(dāng)我嘗試使用GPU Instancing 后,批次幾乎沒變,一種是我們的不同的Mesh,使用同一個(gè)材質(zhì)球,無法合批;其次是同一個(gè)Mesh也無法合批。如下提示:
另外依然還有“Objects are lightmaped”的提示,且已經(jīng)沒有Lightmap_ST,材質(zhì)球都已勾選GPU:
還有Shader是有兩個(gè)PASS的。是否另一個(gè)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("自發(fā)光遮罩 (G)", Range(0, 5)) = 1
[HDR]_EmissionColor("自發(fā)光顏色", 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);
------------------------------漫反射------------------------------
//-----------------------------------自發(fā)光
//float emission = originalCol.a * _EmissionMask;
//自發(fā)光
//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:動(dòng)態(tài)創(chuàng)建物體的靜態(tài)合批的方法可以參考StaticBatchingUtility.Combine的API,這是一項(xiàng)運(yùn)行時(shí)靜態(tài)合批的做法;其次如果使用了多個(gè)Pass的Shader,想要使用GPU Instancing進(jìn)行合批,每個(gè)Pass都要設(shè)置成GPU Instancing才能正常合批。
感謝宗卉軒@UWA問答社區(qū)提供了回答,歡迎大家轉(zhuǎn)至社區(qū)交流:
收集完Shader Variants后,URP/UberPost 變成雙份了,怎么刪掉? -- UWA問答 | 游戲開發(fā)者互動(dòng)問答社區(qū) | 侑虎科技
Audio
Q:最近遇到Unity使用內(nèi)置音頻方案在Android平臺(tái)音頻延遲問題,而且是有時(shí)候會(huì)延遲。不知道各位使用的是什么方案來解決這個(gè)問題?
A:安卓延遲比較大是比較正常的,特別是在一些低端機(jī)上。
可以先試試Unity內(nèi)置的延遲解決方案:ProjectSettings的Audio中的DSP Buffer Size設(shè)置為Best latency,然后短音效的AudioType改為Decomppress On Load。
如果無法解決 可以嘗試一下官方提供的插件:NativeAudio SDK
如果不想用Unity內(nèi)置的FMOD,可以嘗試一下以下方案:Wwise、Criware
感謝蕭小俊@UWA問答社區(qū)提供了回答,歡迎大家轉(zhuǎn)至社區(qū)交流:
收集完Shader Variants后,URP/UberPost 變成雙份了,怎么刪掉? -- UWA問答 | 游戲開發(fā)者互動(dòng)問答社區(qū) | 侑虎科技
AssetBundle
Q:收集完Shader Variants后,URP/UberPost變成雙份了,怎么刪掉?
A:這個(gè)問題我的判斷是UberPost沒有被指定AssetBundle,所以在打包SVC文件時(shí),增加了一個(gè)額外的引用。而此時(shí)SVC文件需要指定AssetBundle,就將其依賴的未指定AssetBundle文件額外添加了進(jìn)來。
這個(gè)問題的難點(diǎn)是:UberPost是在Packages下的,而Packages文件無法指定AssetBundle。
解決方案1:將URP放到工程中,而非Packages下,這樣就可以指定AssetBundle。
解決方案2:改用了可以修改AssetBundle關(guān)聯(lián)性的ScriptableBuildPipeline,手動(dòng)為UberPost指定打包到某個(gè)AssetBundle中,這樣打包系統(tǒng)檢測(cè)到UberPost已有AssetBundle,就不會(huì)重復(fù)打包了。
感謝小楓不會(huì)飛@UWA問答社區(qū)提供了回答,歡迎大家轉(zhuǎn)至社區(qū)交流:
收集完Shader Variants后,URP/UberPost 變成雙份了,怎么刪掉? -- UWA問答 | 游戲開發(fā)者互動(dòng)問答社區(qū) | 侑虎科技
Editor
Q:我在制作一個(gè)工具,用來編輯很多內(nèi)容,其中我有一個(gè)需求,就是在Editor窗口中去修改List的內(nèi)容,比如添加新的項(xiàng)、刪除和修改等。
我是用EditorGUILayout.PropertyField,效果如下:
但是我需要在若干列中展示多個(gè)PropertyField,此時(shí)效果成了這樣子,可以明顯看到第二個(gè)列表的文本框短了一大截:
如果是三個(gè)的話,根本就不顯示:
我限制了前兩個(gè)的長度,然后拉長界面,可以發(fā)現(xiàn)其實(shí)他可以顯示,就是離得太遠(yuǎn)了:
求問這種情況怎么解決?以下是源碼:
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 // 多行的內(nèi)容,用List存
}
}
A1:建議直接使用Odin插件。
感謝張迪@UWA問答社區(qū)提供了回答
A2:這種List在Editor下可以使用ReorderableList,供EditorGUI使用,表現(xiàn)看起來跟你的需求差不多。
感謝范世青@UWA問答社區(qū)提供了回答,歡迎大家轉(zhuǎn)至社區(qū)交流:
收集完Shader Variants后,URP/UberPost 變成雙份了,怎么刪掉? -- UWA問答 | 游戲開發(fā)者互動(dòng)問答社區(qū) | 侑虎科技
封面圖來源于網(wǎng)絡(luò)
今天的分享就到這里。當(dāng)然,生有涯而知無涯。在漫漫的開發(fā)周期中,您看到的這些問題也許都只是冰山一角,我們?cè)缫言赨WA問答網(wǎng)站上準(zhǔn)備了更多的技術(shù)話題等你一起來探索和分享。歡迎熱愛進(jìn)步的你加入,也許你的方法恰能解別人的燃眉之急;而他山之“石”,也能攻你之“玉”。文章來源:http://www.zghlxwxcb.cn/news/detail-476929.html
官網(wǎng):www.uwa4d.com
官方技術(shù)博客:blog.uwa4d.com
官方問答社區(qū):answer.uwa4d.com
UWA學(xué)堂:edu.uwa4d.com
官方技術(shù)QQ群:793972859(原群已滿員)文章來源地址http://www.zghlxwxcb.cn/news/detail-476929.html
到了這里,關(guān)于Unity場景物體動(dòng)態(tài)合批的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!