互动大屏,unity透明视频的实现方法:

AR中常见的应用方式,在摄像机前播放部分透明的视频,让视频和相机中的场景有所交互等应用方式。这次主要介绍特殊Shader的编写和视频的简易制作,在Unity中不借助ARSDK打开摄像头,播放视频达到简易的AR的效果。

这边平面和视频有两种不同方法实现。下面分别介绍。

一、视频的处理(两种方式)

1.用原始视频+黑白剪影+Shader视频实现(一个视频被分为左右两部分)

大致介绍:将视频分为两部分,左边部分为正常视频,右边部分为其黑白剪影。在Unity中通过Shader获取右边剪影响应位置的颜色信息,如果右边对应坐标的颜色值为黑色,则左边这部分响应Alpha值为0(透明),反之为1(不透明)。

1.1.视频制作:

1.1.1首先准备视频素材。素材尽量能够”黑白分明”,不需要的部分尽量都为黑色,这样做出来效果比较好。这个素材不自带A通道,于是我们要想办法让他变得有A通道

互动大屏,unity透明视频的实现方法:_第1张图片


因为我的视频本身没有Alpha通道,为了达到把黑色部分都透明掉,所以先给这个视频加一个特效:颜色键

互动大屏,unity透明视频的实现方法:_第2张图片 互动大屏,unity透明视频的实现方法:_第3张图片

键颜色选择黑色,然后调整参数 得到下面合适的效果

互动大屏,unity透明视频的实现方法:_第4张图片

然后在该层下面新建一个白色固态层,轨道蒙版选择为Alpha


互动大屏,unity透明视频的实现方法:_第5张图片

这样想要显示的部分就出来了。

1.1.2 再新建一个合成,将之前那个合成拖到这个合成中,再将原视频拖进来。修改缩放,位置让他俩对称。

互动大屏,unity透明视频的实现方法:_第6张图片

然后导出这个合成到mp4文件。

如果你的素材自带A通道,那么同理步骤1.1.1,只是不用再加颜色键这个特效了

 这个固态层一定要在素材视频的下面!

(由于我不是专门做AE的,只是摸着石头过河,如果有大神有什么更方便的的方法,欢迎指正~)

 

1.2.1 Shader的编写。

刚才我们处理好了素材,接下来开始建立我们的Unity 项目。

创建一个Plane拖到相对相机合适的位置上,建立一个材质球,赋给Plane

导入刚才的视频。

MainCamera的投影方式设置成正交投影

Plane上添加VideoPlayer 并将视频拖上去,看看视频效果,再进行一次调整。我的视频拖上去是上下颠倒了的,所以进行了一次旋转。如果有音频,将音频拖到AudioSource上。

互动大屏,unity透明视频的实现方法:_第7张图片

可以看到,播放视频的时候,Material的贴图变成了一个视频,也就是说我们只要修改Shader就可以修改视频的效果。

互动大屏,unity透明视频的实现方法:_第8张图片

1.2.2 调整完毕后接下来开始写Shader

互动大屏,unity透明视频的实现方法:_第9张图片新建一个SurfaceShader ,打开编写。

在原始Shader代码上添加,修改一部分:

Properties
	{
		//原始代码保留
		_Num("Num",float) = 0.5 //方便调试的参数设置

	}
Tags { "Queue"="Transparent"  "RenderType"="Transparent"}
//修改标签,告诉引擎何时如何渲染这个对象
//标签是标准的键值对,常用如下:
//Queue 队列标签,决定对象渲染次序
//着色器决定对象所归属的渲染队列,任何透明物体可以通过这种方法在渲染不透明物体之后渲染。
//ShaderLab中有四种预定义的渲染队列
//BackGround  后台,这个渲染队列在所有队列前被渲染,用于渲染天空盒子之类的
//Geometry 几何体,这个是默认队列,用于大多数对象,不透明几何体大多用这个队列
//TransParent 透明,这个渲染队列在几何体队列之后被渲染,采用由后到前的次序。任何采用Alpha的混合对象(不对深度缓冲产生写操作的着色器)都在这里渲染。
//Overlay 覆盖 实现叠加效果,任何需要最后渲染的对象都应该放在此处

#pragma surface surf NoLighting alpha:auto
//开启alpha:auto 自动混合Alpha 而且不要光照
	
//添加
//Alpha决定了贴图的透明度
fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten)
		{
			fixed4 c;
			c.rgb = s.Albedo;
			c.a = s.Alpha;
			return c;
		}

//别忘了声明一下_Num
float _Num;

//表面着色程序:
	void surf (Input IN, inout SurfaceOutput o) //表面着色函数 每个顶点的颜色 都在 o 中反映
		{
			o.Emission = tex2D(_MainTex, IN.uv_MainTex).rgb;			
			
			//这里给输出的Alpha赋值
			//由于我的视频没处理好,不是很对称。。。
			//对称的话应该是0.5
			//这里和0.43比较的是UV贴图的x轴的坐标(0~1,0.5就表示横坐标的一半)
			//右半边视频不显示,所以赋值alpha=0
			if (IN.uv_MainTex.x >= 0.43)
			{
				o.Alpha = 0;
			}
			else
			{
				//左半边视频的Alpha值和右半边黑白视频的RGB的值一样
				//因为我这边处理的A黑白视频不是很好,所以获得了右半边UV的RGB后得比较一下
				//再给Alpha赋值
				o.Alpha = tex2D(_MainTex, float2(IN.uv_MainTex.x + 0.43, IN.uv_MainTex.y)).rgb;			
				
			}

		}

写好了Shader,再把这个Shader给Plan上的材质。

最终效果如下:(我的视频没处理好,可能不够对称,效果不理想)

互动大屏,unity透明视频的实现方法:_第10张图片

2.视频+Shader实现

这次视频只用到了纯黑色背景的视频,所以在Shader中我们要将黑色剔除。

互动大屏,unity透明视频的实现方法:_第11张图片

新建一个UnlitShader 下面是Shader代码

 

Shader "Unlit/Transparent Chroma" { //扣除黑色
 	Properties {
                _MainTex ("Base (RGB)", 2D) = "white" {}
                _MaskCol ("Mask Color", Color)  = (1.0, 0.0, 0.0, 1.0)
                _Sensitivity ("Threshold Sensitivity", Range(0,1)) = 0.5  //敏感程度
                _Smooth ("Smoothing", Range(0,1)) = 0.5
        }
        SubShader {
                Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
                LOD 100
                ZTest Always Cull Back ZWrite On Lighting Off Fog { Mode off }
                CGPROGRAM
                #pragma surface surf Lambert alpha:auto 
 	struct Input
{
                    float2 uv_MainTex;
                };
 
                sampler2D _MainTex;
                float4 _MaskCol;
                float _Sensitivity;
 	float _Smooth;
 
                void surf (Input IN, inout SurfaceOutput o) {
                        half4 c = tex2D (_MainTex, IN.uv_MainTex);
 
                        float maskY = 0.2989 * _MaskCol.r + 0.5866 * _MaskCol.g + 0.1145 * _MaskCol.b;
float maskCr = 0.7132 * (_MaskCol.r - maskY);
 	float maskCb = 0.5647 * (_MaskCol.b - maskY);
 
float Y = 0.2989 * c.r + 0.5866 * c.g + 0.1145 * c.b;
 	float Cr = 0.7132 * (c.r - Y);
 	float Cb = 0.5647 * (c.b - Y);
 
 	float blendValue = smoothstep(_Sensitivity, _Sensitivity + _Smooth, distance(float2(Cr, Cb), float2(maskCr, maskCb)));
o.Alpha = 1.0 * blendValue;
o.Emission = c.rgb * blendValue;               
                }
                ENDCG
        }
        FallBack "Diffuse"
}

在这边调整至合适得效果,得到结果如下 互动大屏,unity透明视频的实现方法:_第12张图片 互动大屏,unity透明视频的实现方法:_第13张图片

二、打开相机实现视频播放,大屏互动

这里使用的是第一种方法,即两个视频,一个彩色,一个黑白。

首先新建一个RawImage,用来显示相机拍摄到的画面,再把Cavas的渲染模式设置成ScreensSpaceCamera,将RendererCamera设置成MainCamera,然后将之前的Plane调整到合适的位置。

互动大屏,unity透明视频的实现方法:_第14张图片互动大屏,unity透明视频的实现方法:_第15张图片

我们用脚本打开摄像头,在进行下一步操作。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video; //VideoPlayer的命名空间

public class UsingCamera : MonoBehaviour
{

     public WebCamTexture webTex;    //相机捕捉到的图片
    public string deviceName;   //相机设备名
    public Button startBT, pauseBT, StopBT;
    public RawImage rowImg;  //相机画面展示
    public VideoClip playClip; //要播放的视频
public VideoPlayer videoPlayer; //Plane的VideoPlayer
public Material mat; //平面材质
    // Use this for initialization
    void Start()
    {
        StartCoroutine(CallCamera()); //调用相机
 		videoPlayer.clip = null;
    }

    // Update is called once per frame
    void Update()
    {
        if (webTex != null)
        {
            rowImg.texture = webTex;
        }
    }

    IEnumerator CallCamera() //打开摄像头的协程
    {
        yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);//获取用户许可
        if (Application.HasUserAuthorization(UserAuthorization.WebCam))
        {
            WebCamDevice[] devices = WebCamTexture.devices;
            deviceName = devices[0].name;
            //设置摄像机的区域和帧率
            webTex = new WebCamTexture(deviceName, Screen.width, Screen.height, 20);
            webTex.Play();//开始
        }

    }
}

由于视频在开始时不加载,要触发某个事件才播放,这里先将VideoPlayerPlayOnAwake去掉,然后添加触发,这里我们用三个Button来触发视频加载播放,加载并开始,暂停,结束并销毁。

在场景中在建三个按钮

互动大屏,unity透明视频的实现方法:_第16张图片


添加按键脚本:

添加按键脚本
 //按键调用
    public void OnStartBTClick()
    {
        if (videoPlayer.clip == null)
        {
            videoPlayer.clip = playClip;
            videoPlayer.Play();
        }
    }

    public void OnPauseBTClick()
    {
        if(videoPlayer.clip != null)
            videoPlayer.Pause();

    }

    public void OnStopBTClick()
    {
        if (videoPlayer.clip != null)
        {
            videoPlayer.Stop();            
            videoPlayer.clip = null;
        }
    }

最后将脚本挂在一个物体上,并赋值,再将Button事件赋值。

互动大屏,unity透明视频的实现方法:_第17张图片

但是这样做会在加载或者不加载的时候有白色的图片挡着:

互动大屏,unity透明视频的实现方法:_第18张图片


因此就要在这边动态修改这个_Num来调整透明度

将脚本中Start和按键事件添加代码:

void Start()
    {
      //
        mat.SetFloat("_Num", 0); //为了保证加载的时候没有白色的遮挡
    }


    IEnumerator startPlayVideo()   //如果开始设置太快还是会有白色闪烁,所以用了协程延时    
    {
        yield return new WaitForSeconds(0.2f);
        mat.SetFloat("_Num", 0.43f);
    }

    //按键调用
    public void OnStartBTClick()
    {
        if (videoPlayer.clip == null)
        {
            videoPlayer.clip = playClip;
            videoPlayer.Play();
            StartCoroutine(startPlayVideo());
        }
    }

    public void OnStopBTClick()
    {
        if (videoPlayer.clip != null)
        {
            mat.SetFloat("_Num", 0);
           
        }
    }
现在能达到预期的效果了

互动大屏,unity透明视频的实现方法:_第19张图片互动大屏,unity透明视频的实现方法:_第20张图片


本文内容部分参考自Think加速想象力出版的《ARVR开发实战》教程,更多学习资料也请关注www.arvrthink.com




你可能感兴趣的:(AR)