您的位置:首页 > 移动开发 > Unity3D

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

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

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

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

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

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

1.1.视频制作:

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



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





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



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





这样想要显示的部分就出来了。
1.1.2 再新建一个合成,将之前那个合成拖到这个合成中,再将原视频拖进来。修改缩放,位置让他俩对称。



然后导出这个合成到mp4文件。
如果你的素材自带A通道,那么同理步骤1.1.1,只是不用再加颜色键这个特效了
 这个固态层一定要在素材视频的下面!
(由于我不是专门做AE的,只是摸着石头过河,如果有大神有什么更方便的的方法,欢迎指正~)
 
1.2.1 Shader的编写。
刚才我们处理好了素材,接下来开始建立我们的Unity
项目。
创建一个Plane拖到相对相机合适的位置上,建立一个材质球,赋给Plane。
导入刚才的视频。
将MainCamera的投影方式设置成正交投影。
在Plane上添加VideoPlayer
并将视频拖上去,看看视频效果,再进行一次调整。我的视频拖上去是上下颠倒了的,所以进行了一次旋转。如果有音频,将音频拖到AudioSource上。



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



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


新建一个SurfaceShader
,打开编写。
在原始Shader代码上添加,修改一部分:
Properties
{
//原始代码保留
_Num("Num",float) = 0.5 //方便调试的参数设置

}
Tags { "Queue"="Transparent"  "RenderType"="Transparent"}
//修改标签,告诉引擎何时如何渲染这个对象
//标签是标准的键值对,常用如下:
//Queue 队列标签,决定对象渲染次序
//着色器决定对象所归属的渲染队列,任何透明物体可以通过这种方法在渲染不透明物体之后渲染。
//ShaderLab中有四种预定义的渲染队列
//BackGround  后台,这个渲染队列在所有队列前被渲染,用于渲染天空盒子之类的
//Geometry 几何体
cd97
,这个是默认队列,用于大多数对象,不透明几何体大多用这个队列
//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上的材质。
最终效果如下:(我的视频没处理好,可能不够对称,效果不理想)



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



新建一个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"
}

在这边调整至合适得效果,得到结果如下





二、打开相机实现视频播放,大屏互动
这里使用的是第一种方法,即两个视频,一个彩色,一个黑白。
首先新建一个RawImage,用来显示相机拍摄到的画面,再把Cavas的渲染模式设置成ScreensSpaceCamera,将RendererCamera设置成MainCamera,然后将之前的Plane调整到合适的位置。





我们用脚本打开摄像头,在进行下一步操作。
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();//开始
}

}
}

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



添加按键脚本:
添加按键脚本
//按键调用
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事件赋值。



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



因此就要在这边动态修改这个_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);

}
}
现在能达到预期的效果了





本文内容部分参考自Think加速想象力出版的《AR与VR开发实战》教程,更多学习资料也请关注www.arvrthink.com。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  AR unity 互动大屏