Unity 抠 绿幕视频 -- 效果极好

Posted 长江很多号

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity 抠 绿幕视频 -- 效果极好相关的知识,希望对你有一定的参考价值。

码字不易,转载请注明出处喔
https://blog.csdn.net/newchenxf/article/details/119575690


分两件事来说,一个是Unity怎么播放视频,二是播放了,怎么把绿色的抠掉!

1 Unity播放视频

1.1 把绿幕视频放到Assets中

这个太简单了,直接视频拖到Assets下的某个目录,例如video目录

1.2 创建播放视频的对象RawImage

Hierarchy面板,UI -> Raw Image。

1.3 创建RawImage输入源即纹理Render Texture

这个将为视频播放的输出,也是RawImage的输入。
在Project面板,你要的目录下,右键,创建Render Texture

建立好了,在Inspector面板,改一下纹理size,最好和视频源一样的宽高。

1.4 Raw Image的纹理设置

把它的Texture设置为第三步新建的texture。(直接把Project面板的texture文件,拖到箭头区域即可)。Texture可以认为是Raw Image的输入源。

1.5 给RawImage加视频组件

即选中Raw Image,在Inspector面板中,给他添加VideoPlayer组件。
即Add Component,选择VideoPlayer。

1.6 配置Video Player

视频来源,即Video Clip,选择为前面导入的绿幕视频

Render Mode选择为Render Texture,然后,目标Target Texture选择为前面第三步新建的texture。

流程搭建完成了。
视频的输出到一个texture,这个texture又是Raw Image的输入。 Raw Image最终被显示到屏幕上。

完成到6,已经可以播放视频了。只不过,出来的是原始数据,带绿幕的!

1.7 补充:调整输出宽高

如果你想调试的时候,显示窗口是1080P,然后画面也铺满,怎么办?
一方面,调整显示窗口宽高:
即Game窗口做如下修改:


当然了,如果你默认Game窗口没出来,则手动打开一下:

别问我这个窗口干嘛的,这个是调试窗口。模拟真实输出的画面。点击这个:

就会自己出来了。这够基础了,跑偏了。。囧

好了,输出窗口改好了,接下来,就是把Raw Image的宽高,也设置为1080P,即Inspector做如下修改。然后挪一下Raw Image位置,屏幕居中显示,就妥了。

2 视频处理,即抠图

再次回到Raw Image的Inspector面板。

发现我们的Material是没有做任何设置的。

Material是干嘛的?下文做个介绍

2.1 Material材质简介

材质,又称物体质地,表示一个物体的色彩、纹理、光滑度、透明度、反射率、折射率、发光度等属性。
在Unity层面来说,材质的本质是一个Shader实例。

Shader又是什么?它是渲染管线中的GPU图像算法程序。可以理解为,一个对象 渲染的方式

所以,我们如果添加一个材质,就代表要怎么渲染这个对象。

2.2 添加shader

这是一个大神贡献的shader算法(源码见附录),专业抠绿幕!把这个文件,放到Assets下的Material目录中:

// Created by Oliver Davies. Enjoy. 
// oliver@psyfer.io

Shader "Unlit/ChromaKey"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _KeyColor("KeyColor", Color) = (0,1,0,0)
        _TintColor("TintColor", Color) = (1,1,1,1)
        _ColorCutoff("Cutoff", Range(0, 1)) = 0.2
        _ColorFeathering("ColorFeathering", Range(0, 1)) = 0.33
        _MaskFeathering("MaskFeathering", Range(0, 1)) = 1
        _Sharpening("Sharpening", Range(0, 1)) = 0.5

        _Despill("DespillStrength", Range(0, 1)) = 1
        _DespillLuminanceAdd("DespillLuminanceAdd", Range(0, 1)) = 0.2
    }
    SubShader
    {
        Tags
        {
            "RenderPipeline"="HDRenderPipeline"
            "RenderType"="HDUnlitShader"
            "Queue" = "Transparent+1"
        }

        Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off
        cull off

        Pass
        {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
				//float4 uvgrab : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            float4 _MainTex_ST;
            float4 _KeyColor;
            float4 _TintColor;
            float _ColorCutoff;
            float _ColorFeathering;
            float _MaskFeathering;
            float _Sharpening;
            float _Despill;
            float _DespillLuminanceAdd;
			//sampler2D _GrabTexture;
			//float4 _GrabTexture_TexelSize;

			#define GRABXYPIXEL(kernelx, kernely) tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x + _GrabTexture_TexelSize.x * kernelx, i.uvgrab.y + _GrabTexture_TexelSize.y * kernely, i.uvgrab.z, i.uvgrab.w)))



            // Utility functions -----------

            float rgb2y(float3 c) 
            {
                return (0.299*c.r + 0.587*c.g + 0.114*c.b);
            }

            float rgb2cb(float3 c) 
            {
                return (0.5 + -0.168736*c.r - 0.331264*c.g + 0.5*c.b);
            }

            float rgb2cr(float3 c) 
            {
                return (0.5 + 0.5*c.r - 0.418688*c.g - 0.081312*c.b);
            }

            float colorclose(float Cb_p, float Cr_p, float Cb_key, float Cr_key, float tola, float tolb)
            {
                float temp = (Cb_key-Cb_p)*(Cb_key-Cb_p)+(Cr_key-Cr_p)*(Cr_key-Cr_p);
                float tola2 = tola*tola;
                float tolb2 = tolb*tolb;
                if (temp < tola2) return (0);
                if (temp < tolb2) return (temp-tola2)/(tolb2-tola2);
                return (1);
            }

            float maskedTex2D(sampler2D tex, float2 uv)
            {
                float4 color = tex2D(tex, uv);
                
                // Chroma key to CYK conversion
                float key_cb = rgb2cb(_KeyColor.rgb);
                float key_cr = rgb2cr(_KeyColor.rgb);
                float pix_cb = rgb2cb(color.rgb);
                float pix_cr = rgb2cr(color.rgb);

                return colorclose(pix_cb, pix_cr, key_cb, key_cr, _ColorCutoff, _ColorFeathering);
            }

            //-------------------------

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
				//o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y) + o.vertex.w) * 0.5;
				//o.uvgrab.zw = o.vertex.zw;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                // Get pixel width
                float2 pixelWidth = float2(1.0 / _MainTex_TexelSize.z, 0);
                float2 pixelHeight = float2(0, 1.0 / _MainTex_TexelSize.w);


				//float2 uv = i.uv.xy;
				//half4 grab = GRABXYPIXEL(0,0);

                // Unmodified MainTex
                float4 color = tex2D(_MainTex, i.uv);

                // Unfeathered mask
                float mask = maskedTex2D(_MainTex, i.uv);

                // Feathering & smoothing
                float c = mask;
                float r = maskedTex2D(_MainTex, i.uv + pixelWidth);
                float l = maskedTex2D(_MainTex, i.uv - pixelWidth);
                float d = maskedTex2D(_MainTex, i.uv + pixelHeight); 
                float u = maskedTex2D(_MainTex, i.uv - pixelHeight);
                float rd = maskedTex2D(_MainTex, i.uv + pixelWidth + pixelHeight) * .707;
                float dl = maskedTex2D(_MainTex, i.uv - pixelWidth + pixelHeight) * .707;
                float lu = maskedTex2D(_MainTex, i.uv - pixelHeight - pixelWidth) * .707;
                float ur = maskedTex2D(_MainTex, i.uv + pixelWidth - pixelHeight) * .707;
                float blurContribution = (r + l + d + u + rd + dl + lu + ur + c) * 0.12774655;
                float smoothedMask = smoothstep(_Sharpening, 1, lerp(c, blurContribution, _MaskFeathering));
                float4 result = color * smoothedMask;

                // Despill
                float v = (2*result.b+result.r)/4;
                if(result.g > v) result.g = lerp(result.g, v, _Despill);
                float4 dif = (color - result);
                float desaturatedDif = rgb2y(dif.xyz);
                result += lerp(0, desaturatedDif, _DespillLuminanceAdd);
                
                return float4(result.xyz, smoothedMask);
            }
            ENDCG
        }
    }
}

接着,还是Assets/Materail目录下,自定义一个Material:

然后,改个名字,然后,选中这个Material,Inspector面板显示其具体参数。它的Shader默认配置了Standard。咱们改成Unlit -> ChromaKey

这个Unlit-> ChromaKey哪里来的?
就是在上面的shader脚本,第一句话定义的:

Shader "Unlit/ChromaKey"

当你导入这个shader,则菜单上就会显示这个选项了!

选好以后,面板的各种可选项,和代码是息息相关的:

你可以就着代码仔细研究一下哈。

2.3 RawImage的Material定制

上面自己加了Material,现在就可以拖到Raw Image的Materail选项了。

2.4 播放效果


这是我这里的播放效果,你需要调整箭头的一些参数,才能保证抠图效果完美。默认参数可能不会抠对,可能又蒙层啥的。

附录

Unity 工程源码

https://github.com/newchenxf/UnityCutGreenVideo

其他参考资料

  1. https://blog.csdn.net/qq_42672770/article/details/108068718
  2. https://blog.csdn.net/cs874300/article/details/89294433
  3. shader算法源码
  4. https://www.reddit.com/r/vfx/comments/ikfcrv/created_a_free_chromakey_green_screen_shader_for/

以上是关于Unity 抠 绿幕视频 -- 效果极好的主要内容,如果未能解决你的问题,请参考以下文章

AE扣绿幕视频教程

AI目标分割能力,无需绿幕即可实现快速视频抠图

无需绿幕的全自动视频抠图工具|unscreen

完美抠图王冰冰!字节实习生实现4K60帧视频实时抠图,连头发丝都根根分明!...

完美抠图王冰冰!字节实习生实现4K60帧视频实时抠图,连头发丝都根根分明!...

完美抠图王冰冰!字节实习生开发的AI,实现4K60帧视频实时抠图,连头发丝都根根分明...