
本教程是關(guān)于一個Shader滿天飛的項(xiàng)目,涉及到的知識點(diǎn)有:
- 攝像頭獲取
- 顏色過濾
- 多攝像頭圖像拼接
- 帶高度圖的調(diào)色板
- 像素捕捉定位
- 動態(tài)水效果
- 燃燒融化效果
如果對圖形學(xué)吃不消的同學(xué),可以先去買馮樂樂的Shader入門書籍:
https://book.douban.com/subject/26821639/?from=tag
最近有朋友聯(lián)系我,需要做一個VR版的消費(fèi)演習(xí)項(xiàng)目。這已經(jīng)是這個月第三個朋友找我做逃生演習(xí)項(xiàng)目,難道最近廣州又有土豪老板開始千萬級的融資???反正不關(guān)我事,我就是程序員,負(fù)責(zé)做好項(xiàng)目就行。把項(xiàng)目需求拿過來,需求很奇葩,不使用Kinect不使用Slam算法做一個消防逃生演習(xí)項(xiàng)目。具體需求是這樣子滴:
- 需要用3個攝像頭對真實(shí)世界的人物進(jìn)行捕捉,并把人物繪制到Unity的主場景
- 使用圖像識別技術(shù),判斷人物在場景里面的位置,并做相對應(yīng)的觸發(fā)時間
- 人物走到左邊沙發(fā)開始燃燒
- 人物走到中間天花板掉落
- 人物走到右邊有水的漣漪產(chǎn)生
- 人物走到終點(diǎn)門打開,有煙霧散發(fā)進(jìn)來
- 不使用Kinect和Slam,將開發(fā)成本壓縮在2W左右。
- 場景布置圖如下
- 時限是兩周的時間
咋看一下,功能很簡單,無非就是Unity截獲Camera的圖像,顯示在面片上,場景搭個面片把截獲的信息顯示出來。2W的項(xiàng)目妥妥的接。
但是后來的事實(shí)證明了,根本不是我想的那么簡單,開始的需求是折紙飛機(jī),后面開始讓我弄波音747,沒辦法,10萬的項(xiàng)目2萬的接,自己攬的鍋跪下都要背。
首先我們來看看具體需求:
1。需要用3個攝像頭對真實(shí)世界的人物進(jìn)行捕捉,并把人物繪制到Unity的主場景。
Unity是最大支持4個攝像頭,但是我測試只需要拿一個就行了。思路就是把攝像頭截取,把畫面顯示在Quad上面
代碼:
IEnumerator Start()
{
yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
if (Application.HasUserAuthorization(UserAuthorization.WebCam))
{
WebCamDevice[] devices = WebCamTexture.devices;
WebCamTexture tex = new WebCamTexture(devices[0].name, 400, 400, 12);
mCameraTex.GetComponent<MeshRenderer>().material.mainTexture = tex;
tex.Play();
}
}
重寫了Start,因?yàn)閁nity底層使用反射調(diào)用Start,所以需要把void Start刪掉。為啥要用攜程,因?yàn)榻孬@攝像頭的圖像有延時,必須在每次
渲染完攝像頭才能截取,所以必須用協(xié)程把時間錯開。
出來的結(jié)果是這樣的:

我們?nèi)宋镌趫鼍俺鰜砹?,背景也出來了。但是我們要的效果是只要人物,那么背景怎么去掉呢?br>
我第一想到的是用摳顏色的方法,把背景摳掉。但是這樣就有疑問,如果把人物也摳了怎么辦?
多方研究發(fā)現(xiàn),綠色是和人體顏色相似度最差的顏色,我們使用綠色背景打底,人物走過就能把綠色摳掉了。但是如果有人穿綠色的衣服,那我也沒辦法,這年頭穿綠色套裝出社會本來就有危險= =!
綠色布搞來了,下面開始動工?。。?/strong>

現(xiàn)在要做的事情就是摳圖!摳圖!摳圖!
網(wǎng)上找了個摳圖插件很難用,很多依賴弄不掉,決定自己寫??!
思路無非就是找到顏色匹配的那塊像素,把他變成透明,同時Shader要支持透明通道還有透明隊(duì)列。
顏色匹配的算法我想了很久,最后還是決定使用方差公式,即Color1和Color2的相似度等于。
sqrt(pow((Color1.r - Color2.r),2) + pow((Color1.g - Color2.g),2) + pow((Color1.b - Color2.b),2))
得出來的值越小,說明和目標(biāo)顏色越接近,很明顯,上面的Color2就是我決定使用的綠色
Shader "MyShader/FilterfColor" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_FilterfColor("Ridof (RGB)",Color)=(1,1,1,1)
_Range("Range",range(0,1))=0
_X("X",range(0,1))=0
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
pass
{
CGPROGRAM
#pragma vertex vertext_convert
#pragma fragment fragment_convert
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _MainTex1;
float4 _FilterfColor;
float4 _MainTex_ST;
float _Range;
float _X;
struct Inputvrite
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct Inputfragment
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
float ColorLerp(float3 tmp_nowcolor,float3 tmp_FilterfColor)
{
float3 dis = float3(abs(tmp_nowcolor.x - tmp_FilterfColor.x),abs(tmp_nowcolor.y - tmp_FilterfColor.y),abs(tmp_nowcolor.z - tmp_FilterfColor.z));
float dis0 =sqrt(pow(dis.x,2)+pow(dis.y,2)+pow(dis.z,2));
float maxdis = sqrt(3);
float dis1 = lerp(0,maxdis,dis0);
if(dis1 < _Range){
dis1 = dis1 *dis1 *dis1;
}
return dis1;
}
Inputfragment vertext_convert(Inputvrite i)
{
Inputfragment o;
o.pos = mul(UNITY_MATRIX_MVP,i.vertex);
o.uv = TRANSFORM_TEX(i.texcoord, _MainTex);
return o;
}
float4 fragment_convert(Inputfragment o) : COLOR
{
// o.uv.x *= _X;
float4 c = tex2D(_MainTex,o.uv);
c.a *=ColorLerp(c.rgb,_FilterfColor.rgb);
// c.a *= c.a;
return c;
}
ENDCG
}
}
FallBack "Diffuse"
}
稍作調(diào)整可以看到下面的效果,綠色被我摳掉了?。?/p>

但是后面還有更多艱巨的任務(wù)等著我,一個是調(diào)色板方便客戶摳圖,一個是要判斷小人走到地圖哪個位置。
兩周時間第一天就只做了個摳圖。其實(shí)好戲還在后頭= =*
下一期要講的內(nèi)容是:像素遍歷判斷小人的位置。
(程序是個永無止境的東西,必須每天保持學(xué)習(xí))
版權(quán)所有,違責(zé)必究
菜鳥在線 林老師 2017.7.29
