UnityVR消防項(xiàng)目(一)顏色過濾


本教程是關(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)目。具體需求是這樣子滴:

  1. 需要用3個攝像頭對真實(shí)世界的人物進(jìn)行捕捉,并把人物繪制到Unity的主場景
  2. 使用圖像識別技術(shù),判斷人物在場景里面的位置,并做相對應(yīng)的觸發(fā)時間
    • 人物走到左邊沙發(fā)開始燃燒
    • 人物走到中間天花板掉落
    • 人物走到右邊有水的漣漪產(chǎn)生
    • 人物走到終點(diǎn)門打開,有煙霧散發(fā)進(jìn)來
  3. 不使用Kinect和Slam,將開發(fā)成本壓縮在2W左右。
  4. 場景布置圖如下
  5. 時限是兩周的時間

咋看一下,功能很簡單,無非就是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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容