My blog directory:
Introduction:
Unity和UE兩大商業引擎已經有一定歷史了,然而它們居然沒有一套完整的Billboard解決方案,這讓我感到非常神奇!!!所以打算寫下完整的Billboard方案。
圖1
本來是個很簡單的事情!!!
就是讓面片時時刻刻面朝攝像機,但是因為引擎裡各種各樣的限制和因素,就把這件很簡單的事情弄得很複雜。根據各種不同的情況吧Billboard的實現分為以下幾類。
(1)在CPU裡計算,在圖元彙編階段或者GamePlay層設定面片朝向。
(2)在Shader中計算其朝向。
(3)在使用DrawInstance的情況下在Shader中計算朝向。可以繪製大量Billboard。
下面就直接來講目前最完善的的一種BillBoard Shader for Instancing。
main content:
【Billboard material for Instanced static mesh in Unreal Engine 4】
首先需要製作一個面片,這個面片的LocalSpace首先就要正確。X軸與其垂直。
圖2
匯入引擎和max保持一致。
圖3
Billboard的面向攝像機這個計算其實在哪裡做都可以,但是因為引擎的限制有些時候就不能隨心所欲了。比如Unreal,我們在材質編輯器能觸碰到VertexShader的地方就只有PositionOffset
開啟原始碼,會發現PositionOffset和WorldPosition是相加關係
所以現在只能在WorldSpace計算BillBoard了。
圖4
InstanceLocalToWorld程式碼,這裡可以讓引擎底層把Instance的Transform傳上來。
#if USE_INSTANCING
return mul(InputVector,(MaterialFloat3x3)Parameters。InstanceLocalToWorld);
#else
return mul(InputVector,GetLocalToWorld3x3());
#endif
首先找到Instance的x,y,z在世界空間的朝向。如圖4的黑色xyz座標系所示。然後把頂點的世界空間座標和軸點相減得到相對於軸點的向量
然後把這些向量對映到xyz軸上,得到點ABCD
然後根據攝像機方向,轉動XYZ使其於攝像機方向垂直得到x‘y’z‘
然後把ABCD對映到X’Y‘Z’得到A‘B’C‘d’
然後把相對於Pivot的A‘B’C‘d’變換到相對於世界原點座標
最後減去世界空間的絕對位置,和Shader底層的+=相抵消
所以這裡最後進入投影變換的頂點的座標是原模型頂點的世界空間座標向攝像機方向偏移後的頂點世界空間座標。
於是就可以得到Instance的Billboard了。
Instance的billboard允許我們在場景裡畫
幾萬個
。
這個方案對非Instance也同樣適用。
【Billboard material for instance in Unity】
在Unity中的思路和Unreal有點不一樣,因為Unity對底層的約束較小,所以可以直接在螢幕空間做BillBoard。大體思路是先把面片模型的所有頂點的LocalSpace歸零,然後在CameraSpace把面片模型的頂點根據UV再重新展開。(不需要什麼頂點色什麼亂七八糟的騷操作)。
還是先做一個片,UV注意是平展開的。因為後面需要用UV來把面片在CameraSpace重新展開。
因為要使用Instacing所以需要給shader做一些設定
把頂點座標歸零
然後在攝像機空間用UV值把頂點重新展開
完整BillBoard程式碼如下:
Shader “Unlit/S_BillBoard”
{
Properties
{
_BaseColor(“BaseColor”, Color) = (1, 1, 1, 1)
_SizeH(“SizeH”, Float) = 1
_SizeW(“SizeW”, Float) = 1
}
SubShader
{
Tags { “RenderType”=“Opaque” }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3。0
#pragma multi_compile_instancing
#include “UnityCG。cginc”
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
//If you want to access Instancing data in fragment shader
//UNITY_VERTEX_INPUT_INSTANCE_ID
};
half4 _BaseColor;
float _SizeW;
float _SizeH;
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
//If you want to access Instancing data in fragment shader
//UNITY_TRANSFER_INSTANCE_ID(v, o);
o。uv = v。uv;
float2 bias = v。uv * 2 - 1;
float4 vert = v。vertex;
//Reset vertex position
vert。xyz = 0;
//Expand vertex position in screen space
float4 wpos = mul(unity_ObjectToWorld, vert);
wpos。xyz += normalize(UNITY_MATRIX_V[0]。xyz) * bias。r * _SizeW + normalize(UNITY_MATRIX_V[1]。xyz) * bias。g * _SizeH;
o。vertex = mul(UNITY_MATRIX_VP, wpos);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
half4 Output = half4(1, 1, 1, 1);
Output = _BaseColor;
return Output;
}
ENDCG
}
}
}
在FrameDebuger裡可以看到
用這種辦法可以繪製上萬個Billboard,當然你不器用Instancing這種billboard方案依然適用。
Summary and outlook:
UnrealEngine4 在4。22的時候做了更新,如果VB和Material相同的情況下預設就使用Instance繪製了,不需要手動指認InstanceConsponent
Enjoy it!
Next:
YivanLee:虛幻4渲染程式設計(材質編輯器篇)【第九卷:Image Based Lighting】