Unity3D视频绿幕抠图的实现及优化

       本文是通过Shader处理绿幕的方式来实现Unity中视频(VideoPlayer)的绿幕抠图。因为项目原因,不追究细节(能用就好能用就好)orz可是我连shader都不了解......项目完成之后有机会的话一定要去深入学习一下!

       效果大概这样

         Unity3D视频绿幕抠图的实现及优化_第1张图片           Unity3D视频绿幕抠图的实现及优化_第2张图片

好了就让我们开始叭ヽ(ー_ー)ノ

Unity版本 5.6.7

       下面是我找的一些关于shader的博客,可以去了解下

       https://onevcat.com/2013/07/shader-tutorial-1/

       https://onevcat.com/2013/08/shader-tutorial-2/

       https://blog.csdn.net/oskytonight/article/details/42241301

       另外,在寻求去除绿幕的方法时,也想过用OpenCVForUnity来做,对本文效果不满意的话说不定可以寻求OpenCVForUnity的方式。以下是我通过shader完成视频绿幕抠图及优化参考的博文:

       https://blog.csdn.net/an050602/article/details/56479741?utm_source=blogxgwz9 

       https://blog.csdn.net/macjeson/article/details/50035073#commentBox

       感谢博主大大们,收获颇多!在此作自我整理与回顾。

1.创建VideoPlayer

       具体过程可以参考我的博文  https://blog.csdn.net/cs874300/article/details/89294433

2.创建Shader

       在Project下的自己想要的文件夹下右键Create -> shader ->(具体哪种无所谓,反正都要替换掉emmm看下面代码的类型应该是Image effect Shader?)。以下为第二个链接的博主代码

Shader "MyShader/FilterfColor" { 
    Properties { 
        _MainTex ("Base (RGB)", 2D) = "white" {} 
        _FilterfColor("Ridof (RGB)",Color)=(1,1,1,1) 
    } 
    SubShader { 
        Tags { "RenderType"="Opaque" } 
        Blend SrcAlpha OneMinusSrcAlpha 
        pass 
        { 
            CGPROGRAM 

            #pragma vertex vertext_convert
            #pragma fragment fragment_convert
            #include "UnityCG.cginc" 

            sampler2D  _MainTex; 
            sampler2D  _MainTex1; 
            float4  _FilterfColor; 
            struct Inputvrite 
            { 
                float4 vertex : POSITION; 
                float4 texcoord : TEXCOORD0; 
            }; 
            struct Inputfragment 
            { 
                float4 pos : SV_POSITION; 
                float4 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); 
                return dis1; 
            } 

            Inputfragment vertext_convert(Inputvrite i) 
            { 
                Inputfragment o; 
                o.pos = mul(UNITY_MATRIX_MVP,i.vertex); 
                o.uv = float4(i.texcoord.xy,1,1); 
                return o; 
            } 

            float4 fragment_convert(Inputfragment o) : COLOR 
            { 
                float4 c = tex2D(_MainTex,o.uv); 
                c.a *=ColorLerp(c.rgb,_FilterfColor.rgb); 
                return c; 
            } 


        ENDCG 
        } 
    } 
    FallBack "Diffuse" 
}

       然后按第一个链接的博主所说,将其中fragment_convert的方法做如下改动。其基本思路就是将绿色的部分Alpha通道设为0(即透明)。其中gbr代表green blue red,0.5为比值(  green/255  )

float4 fragment_convert(Inputfragment o) : COLOR{
 
float4 c = tex2D(_MainTex,o.uv);
 
//简单的判断材质的RGB值
 
if (c.g >0.5&&c.b<0.5&&c.r<0.5){
 
c.a = 0;//材质的绿色大到一定程度,并且蓝色和红色小到一定程度,就把该部分的材质的透明度设置为0
 
}
 
return c;
 
}

          就博主的视频抠图而言,已经完成了。但是对于我的视频来说,还没有完成,因为我最后的效果是这样的

           Unity3D视频绿幕抠图的实现及优化_第3张图片          Unity3D视频绿幕抠图的实现及优化_第4张图片

          我知道绿幕的部分容易解决,可是多出来的白门和白墙和白鞋子是同一个颜色的。可不可以有再Shader中确定位置的方法,来把除了人物的周围部分的其他部分都设置为透明。然后找了半天基本都是和上面代码差不多的,或者就直接是复制的代码,完全没有自己的想法......遂放弃。在大量找shader相关资料的时候发现了一篇博文,是讲shader的uv坐标的,链接如下:

        https://blog.csdn.net/xiongwen_li/article/details/66474826

其中有    i.uv.x=i.uv.x * i.uv.y; 这样的注释。我就想能不能通过这样的方式来限定位置条件(其实只是因为这句注释给了我启发,这篇文章倒是和本文没啥大关系)。然后我就在代码中加了以下几句:

if (o.uv.x < 0.25 || o.uv.x> 0.65 ||o.uv.y < 0.2 || o.uv.y >0.8) {
    c.a = 0;
}

        如图,显然,达到了我想要的位置约束的效果。(完全就是瞎猫碰到死耗子...蒙出来的效果,所以说!如果shader了解的通透的话,这种简单的处理完全就是信手拈来啊!果然还是要提升知识水平啊orz)  

        Unity3D视频绿幕抠图的实现及优化_第5张图片

       除此之外,还可以通过QQ截图来获取某一像素点的RGB值(就当取色器用,真的很好用!)来作背景优化的约束条件,比如说像我就添加了以下的约束条件。因为颜色越深的地方,green可能不到0.5,但是会比red高很多,用此来作为条件优化。

if (o.uv.y > 0.65) {
	if (c.g > 0.34 && c.g > 1.1 * c.r) {
	    c.a = 0;
	}
}
else {
    //绿色>红色值1.1倍就设为透明,可以优化边缘绿色,参数可调
	if (c.g > 1.1 * c.r) {
		c.a = 0;
	}
			
}

if (o.uv.x < 0.25 || o.uv.x> 0.65 ||o.uv.y < 0.2 || o.uv.y >0.8) {
	c.a = 0;
}

        因为深绿色和头部的黑色有冲突,所以我把上部分和下部分分开优化,(还不是为了帮大师保留更多的头发!摔桌(/"— _ —)/~┴┴)但是用了好多if语句啊,感觉代码还是可以有改进的地方.

       以下为最终效果图(我觉得海星(✿◡‿◡)):

       Unity3D视频绿幕抠图的实现及优化_第6张图片

3. 将shader运用到视频或图片上

       我这里是视频,图片同理。

       1.在Project下的自定义文件夹内Create-> Material 

       2.将shader拖入刚创建的材质球(Material),然后我看材质球里面有个Texture,就把视频VideoPlayer的Target Texture中添加的那个Render texture也添加进去了,因为我的视频是通过Render Mode为Render Texture的方法播放的,

       3.把Material拖入RawImage里面的Material。(视频播放、Rendertexture相关可以见步骤1里的创建视频)

       Unity3D视频绿幕抠图的实现及优化_第7张图片

       

       感觉自己在一些细节还有许多了解不清楚的地方,或许有说错或者不必要的情况,欢迎指正。以后有机会一定要多去学习学习鸭!以上。

你可能感兴趣的:(Unity3D视频绿幕抠图的实现及优化)