UnityVR消防项目(一)颜色过滤

2017-08-07  本文已影响0人  时过敬迁

本教程是关于一个Shader满天飞的项目,涉及到的知识点有:

如果对图形学吃不消的同学,可以先去买冯乐乐的Shader入门书籍:
https://book.douban.com/subject/26821639/?from=tag

最近有朋友联系我,需要做一个VR版的消费演习项目。这已经是这个月第三个朋友找我做逃生演习项目,难道最近广州又有土豪老板开始千万级的融资???反正不关我事,我就是程序员,负责做好项目就行。把项目需求拿过来,需求很奇葩,不使用Kinect不使用Slam算法做一个消防逃生演习项目。具体需求是这样子滴:

  1. 需要用3个摄像头对真实世界的人物进行捕捉,并把人物绘制到Unity的主场景
  2. 使用图像识别技术,判断人物在场景里面的位置,并做相对应的触发时间
    • 人物走到左边沙发开始燃烧
    • 人物走到中间天花板掉落
    • 人物走到右边有水的涟漪产生
    • 人物走到终点门打开,有烟雾散发进来
  3. 不使用Kinect和Slam,将开发成本压缩在2W左右。
  4. 场景布置图如下
  5. 时限是两周的时间

咋看一下,功能很简单,无非就是Unity截获Camera的图像,显示在面片上,场景搭个面片把截获的信息显示出来。2W的项目妥妥的接。
但是后来的事实证明了,根本不是我想的那么简单,开始的需求是折纸飞机,后面开始让我弄波音747,没办法,10万的项目2万的接,自己揽的锅跪下都要背。

首先我们来看看具体需求:
1。需要用3个摄像头对真实世界的人物进行捕捉,并把人物绘制到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,因为Unity底层使用反射调用Start,所以需要把void Start删掉。为啥要用携程,因为截获摄像头的图像有延时,必须在每次
渲染完摄像头才能截取,所以必须用协程把时间错开。

出来的结果是这样的:

我们人物在场景出来了,背景也出来了。但是我们要的效果是只要人物,那么背景怎么去掉呢?
我第一想到的是用抠颜色的方法,把背景抠掉。但是这样就有疑问,如果把人物也抠了怎么办?
多方研究发现,绿色是和人体颜色相似度最差的颜色,我们使用绿色背景打底,人物走过就能把绿色抠掉了。但是如果有人穿绿色的衣服,那我也没办法,这年头穿绿色套装出社会本来就有危险= =!

绿色布搞来了,下面开始动工!!!

现在要做的事情就是抠图!抠图!抠图!

网上找了个抠图插件很难用,很多依赖弄不掉,决定自己写!!

思路无非就是找到颜色匹配的那块像素,把他变成透明,同时Shader要支持透明通道还有透明队列。

颜色匹配的算法我想了很久,最后还是决定使用方差公式,即Color1和Color2的相似度等于。

sqrt(pow((Color1.r - Color2.r),2) + pow((Color1.g - Color2.g),2) + pow((Color1.b - Color2.b),2))

得出来的值越小,说明和目标颜色越接近,很明显,上面的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" 
}

稍作调整可以看到下面的效果,绿色被我抠掉了!!

但是后面还有更多艰巨的任务等着我,一个是调色板方便客户抠图,一个是要判断小人走到地图哪个位置。

两周时间第一天就只做了个抠图。其实好戏还在后头= =*

下一期要讲的内容是:像素遍历判断小人的位置。


(程序是个永无止境的东西,必须每天保持学习)

版权所有,违责必究

菜鸟在线 林老师 2017.7.29

上一篇 下一篇

猜你喜欢

热点阅读