前兩天看到一篇關(guān)于雨滴效果制作的文章,學(xué)習(xí)實(shí)踐了一下递胧。這里貼一下涉及到的知識點(diǎn)碑韵。
1.CommandBuffer
用來存儲一系列渲染指令,可以通過Camera(Camera.AddCommandBuffer)
,Light( Light.AddCommandBuffer
)及Graphics(Graphics.ExecuteCommandBuffer
)來調(diào)用執(zhí)行缎脾。
2.ComputeShader
ComputeShader運(yùn)行在GPU上祝闻,可以執(zhí)行大量并行算法,或者用來加速游戲渲染遗菠。為了更高效的使用Compute Shader治筒,需要額外了解GPU的架構(gòu)及常見并行算法。
使用ComputerShader之前需要先使用SystemInfo.supportsComputeShaders測試可用性舷蒲。下面是基本的ComputeShader格式
// test.compute
// 一個(gè)Compute Shader必須至少有一個(gè) kernel耸袜, #pragma kernel后可添加額外的宏,如OTHER_DEFINE
#pragma kernel FillWithRed OTHER_DEFINE
RWTexture2D<float4> res;
[numthreads(100,1,1)]
void FillWithRed (uint3 dtid : SV_DispatchThreadID)
{
res[dtid.xy] = float4(1,0,0,1);
}
之后可以通過 ComputeShader.Dispatch來執(zhí)行shader牲平。方式如下:
ComputeShader shader;
int kernal = shader.FindKernel("CSMain");
shader.Dispatch(kernal, 100, 1, 1);
3. ComputeBuffer
GPU數(shù)據(jù)緩存堤框,通常是在ComputeShader中使用,可以填充數(shù)據(jù)纵柿,也可以獲取數(shù)據(jù)蜈抓。
在HLSL格式里,使用StructuredBuffer<T>或者 RWStructuredBuffer<T>來聲明ComputeBuffer昂儒。
4. GPU Instance
GPU Instance是一種將相同Mesh的多個(gè)拷貝在少量的draw call中完成繪制的技術(shù)沟使。經(jīng)常在繪制建筑,樹木渊跋,草木或者其他一些在場景中重復(fù)出現(xiàn)的物件中使用腊嗡。可以明顯的提升渲染性能拾酝。
GPU Instance在每個(gè)draw call中繪制的必須是完全相同的mesh,但是每個(gè)mesh可以擁有不同的參數(shù)(形如顏色燕少,縮放值)。
為了使用GPU Instance蒿囤,需要開啟材質(zhì)的 Enable Instancing選項(xiàng)客们。
除了以上方式,也可以在腳本中調(diào)用Graphics.DrawMeshInstanced 或者 Graphics.DrawMeshInstancedIndirect執(zhí)行GPU Instance材诽。
通常底挫,Unity只會對僅有Transform變化的GameObject做批處理,為了為每個(gè)實(shí)例添加更多的變體脸侥,需要在Shader中添加per-instance 屬性建邓,示例如下:
Shader "SimplestInstancedShader"
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in __fragment Shader__.
};
//聲明實(shí)例屬性_Color
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
}
ENDCG
}
}
}
上面聲明了_Color屬性,Unity會從MaterialPropertyBlock中采集_Color屬性湿痢,設(shè)置到GameObject的Shader中涝缝,并將GameObject合并在一個(gè)DrawCall中
MaterialPropertyBlock props = new MaterialPropertyBlock();
MeshRenderer renderer;
foreach (GameObject obj in objects)
{
float r = Random.Range(0.0f, 1.0f);
float g = Random.Range(0.0f, 1.0f);
float b = Random.Range(0.0f, 1.0f);
props.SetColor("_Color", new Color(r, g, b));
renderer = obj.GetComponent<MeshRenderer>();
renderer.SetPropertyBlock(props);
}
一些高級Tips
1.當(dāng)Unity進(jìn)行batch的時(shí)候,Static batching
優(yōu)先級要比instancing高譬重,也就是說拒逮,如果將某個(gè)GameObject設(shè)置為static batching,Unity 會禁掉instancing,即使使用了instancing的shader,但是會完成static batching。這時(shí)臀规,Inspector窗口會顯示警告提示關(guān)閉static batch滩援。
GPU Instancing的優(yōu)先級會比dynamic batching高,當(dāng)執(zhí)行instancing的時(shí)候塔嬉,dynamic batching會被禁用玩徊。
2.某些情況如材質(zhì)改變或者深度排序會導(dǎo)致阻止Unity進(jìn)行自動的instancing,這是可以調(diào)用Graphics.DrawMeshInstanced
強(qiáng)制執(zhí)行GPU instancing谨究。
3.從Unity2018.1開始恩袱,Unity支持GI 渲染時(shí),在GPU Instancing使用光照探針及occlusion探針胶哲∨纤可以在MaterialPropertyBlock中提供 light probe 及 occlusion probe數(shù)據(jù)(LightProbeUsage
參數(shù)中設(shè)置).
4.使用UnityObjectToClipPos(v.vertex) 而不是 mul(UNITY_MATRIX_MVP,v.vertex),前者更高效鸯屿。
其他需要注意的地方:
1.SurfaceShader默認(rèn)會生成示例變量澈吨,但是可以用#pragma noinstancing禁止這一行為。
2.Graphics.DrawMeshInstanced需要在材質(zhì)設(shè)置中開啟GPU Instancing設(shè)定寄摆,但是Graphics.DrawMeshInstancedIndirect不需要谅辣。
3.instanced draw call在Frame Debugger中以 Draw Mesh (instanced)形式出現(xiàn)。
4.在前向渲染的時(shí)候婶恼,只有base pass會執(zhí)行instancing桑阶,add pass不會
5.如果有兩個(gè)以上的pass,只有第一個(gè)pass會被instanced勾邦,因?yàn)楹竺娴膒ass需要強(qiáng)制在一起渲染联逻,這個(gè)操作會是材質(zhì)發(fā)生變化.
5.Draw call batching
Dynamic batching:通常用于特別小的mesh,在cpu層面做頂點(diǎn)變換检痰,之后將相似的頂點(diǎn)合批在一個(gè)draw call中繪制包归。會耗費(fèi)大量cpu時(shí)間。
Static batching: 將靜態(tài)的GameObject合并成一個(gè)大的mesh铅歼,從而提高渲染的速度公壤。缺點(diǎn)是會耗費(fèi)大量內(nèi)存和硬盤空間。
只有使用相同的材質(zhì)的GameObjects才會被合批處理椎椰。
1.如果除了紋理厦幅,兩個(gè)材質(zhì)完全相同,可以將兩個(gè)紋理合在一張大的紋理中慨飘。
2.Renderer.material會創(chuàng)建一份材質(zhì)的拷貝确憨,而不是共享译荞。需要調(diào)用Renderer.sharedMaterial共享材質(zhì)
3.Shadow casters在材質(zhì)不同的情況下也會被合批處理,只要供Shadow casters使用的材質(zhì)參數(shù)一樣即可休弃。
4.一次動態(tài)合批不能超過900個(gè)頂點(diǎn)屬性吞歼,不能超過300個(gè)頂點(diǎn)。
5.鏡面GameObject(scale = -1)不能被合批
6.除了Shadow casters外塔猾,使用不同material的實(shí)例的對象不能被合批
7.動態(tài)合批需要將頂點(diǎn)全部轉(zhuǎn)化為世界坐標(biāo)篙骡,所以只有當(dāng)這項(xiàng)消耗低于合批的時(shí)候才值得做動態(tài)合批。