Unity开发《一起来捉妖》教程 | 2.用摄像头图像做背景

洪流学堂,让你快人几步。你好,我是郑洪智。

洪流学堂公众号回复捉妖,可以获取本教程的源码工程


小新:“我们接下来是不是需要将显示融合到屏幕上,才能称得上是AR呢?”

大智:“没错,屏幕式AR就是通过摄像头获取现实世界的图像,显示在屏幕上,然后和虚拟的物体叠加显示。我们今天就来实现这个摄像头图像做背景。”

2.摄像头图像做背景

要使用摄像头图像做背景,那首先需要获取到摄像头的图像。在Unity中读取摄像头的数据主要需要用到WebCamTexture这个类。

核心代码如下:

IEnumrator Start()
{
    // 请求摄像头权限
    yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
    // 如果获取到摄像头权限
    if (Application.HasUserAuthorization(UserAuthorization.WebCam))
    {
        // 获取所有的摄像头设备
        WebCamDevice[] devices = WebCamTexture.devices;
        if (devices != null)
        {
            // 索引为0的摄像头一般为后置摄像头,参数分别为设备名称、图像宽度、高度、刷新率
            WebCamTexture tex = new WebCamTexture(devices[0].name, width, height, fps);
            // 实时获取摄像头的画面
            tex.Play();
        }
    }
}

大智:“那么如何将摄像头的图像作为游戏背景现实呢?”

小新:“这个简单,直接使用全屏的UI图片就可以了吧?”

大智:“再详细点呢?”

小新:“恩么。。。应该是使用UI里面的RawImage组件,然后将WebCamTexture赋值给这个组件的texture属性。”

大智:“那你想到一个问题没有?我们需要在这个背景上显示3D模型,也就是我们要抓的妖怪,如何设置呢?”

小新:“哦哦,对哦,那我们应该把UI设置为3D的UI,由于UI是跟随相机移动的,可以设置Canvas为Screen Space - Camera。

大智:“嗯,那我们试试看喽。”

接下来我们需要把相机的画面赋给这个RawImage组件的texture,最后完整的代码如下:

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class WebCameraBackground : MonoBehaviour
{
    RawImage cameraImage;
    private WebCamTexture webCamTex;

    IEnumerator Start()
    {
        cameraImage = GetComponent<RawImage>();

        // 请求摄像头权限
        yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
        // 如果获取到摄像头权限
        if (Application.HasUserAuthorization(UserAuthorization.WebCam))
        {
            // 获取所有的摄像头设备
            WebCamDevice[] devices = WebCamTexture.devices;
            if (devices != null)
            {
                // 索引为0的摄像头一般为后置摄像头,参数分别为设备名称、图像宽度、高度、刷新率
                webCamTex = new WebCamTexture(devices[0].name, 800, 1280, 30);
                // 实时获取摄像头的画面
                webCamTex.Play();

                cameraImage.texture = webCamTex;
            }
        }
    }
}

最后将这个代码拖到RawImage这个物体上,就可以发布到真机上测试查看了!

小新:“这发布出来以后有点不太对,只有手机横着的时候,摄像头显示的图像方向和实际是一致的,其他的都不对。”

大智:“这是因为WebCamTexture返回的图像,实际上会根据手机的朝向有一定的旋转,我们把下面的代码加进来测试一下看看。”

private void OnGUI()
{
    // 使用下面的旋转角度(顺时针)来旋转摄像头,以保证正确的朝向
    GUILayout.Label(webCamTex.videoRotationAngle.ToString());
    // 摄像头的视频是否垂直翻转
    GUILayout.Label(webCamTex.videoVerticallyMirrored.ToString());
}

使用竖屏时,你会看到第一个值显示的是90,意味着我们需要将图像顺时针旋转90度,才是正确的朝向。

小新:“我试着改了一下,还是不对,智哥你来吧。”

大智:“那我就直接上代码了,首先你把RawImage的Anchor的位置直接设置到中心就行。因为后面我们需要通过代码来修改这个RawImage的旋转和尺寸,否则会有冲突。”

具体完整代码如下:

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class WebCameraBackground : MonoBehaviour
{
    RawImage cameraImage;
    private RectTransform rectTransform;
    private WebCamTexture webCamTex;
    private int lastRotationAngle;

    IEnumerator Start()
    {
        cameraImage = GetComponent<RawImage>();
        rectTransform = GetComponent<RectTransform>();

        // 请求摄像头权限
        yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
        // 如果获取到摄像头权限
        if (Application.HasUserAuthorization(UserAuthorization.WebCam))
        {
            // 获取所有的摄像头设备
            WebCamDevice[] devices = WebCamTexture.devices;
            if (devices != null)
            {
                // 索引为0的摄像头一般为后置摄像头,参数分别为设备名称、图像宽度、高度、刷新率
                webCamTex = new WebCamTexture(devices[0].name, 800, 1280, 30);
                // 实时获取摄像头的画面
                webCamTex.Play();

                cameraImage.texture = webCamTex;
            }
        }
    }

    private void Update()
    {
        if (webCamTex != null && lastRotationAngle != webCamTex.videoRotationAngle)
        {
            OnOrientationChanged();
            lastRotationAngle = webCamTex.videoRotationAngle;
        }
    }

    private void OnOrientationChanged()
    {
        // 旋转rawimage,为什么加一个负号呢?因为rawimage的z轴是背对图像的,直接使用videoRotationAngle旋转,相对于图片是逆时针旋转
        transform.localRotation = Quaternion.Euler(0, 0, -webCamTex.videoRotationAngle);

        // 判断是否是竖屏,竖屏时由于旋转的关系,需要将width和height调换
        if (webCamTex.videoRotationAngle % 180 != 0)
            rectTransform.sizeDelta = new Vector2(Screen.height, Screen.width);
        else
            rectTransform.sizeDelta = new Vector2(Screen.width, Screen.height);
    }

    private void OnGUI()
    {
        // 使用下面的旋转角度(顺时针)来旋转摄像头,以保证正确的朝向
        GUILayout.Label(webCamTex.videoRotationAngle.ToString());
        // 摄像头的视频是否垂直翻转
        GUILayout.Label(webCamTex.videoVerticallyMirrored.ToString());
    }
}

小新:“旋转rawimage,为什么加一个负号呢?”

大智:“因为webCamTex.videoRotationAngle的返回值是相对于图像需要顺时针旋转一定的角度。也就是如下图所示:”

大智:“如果以Unity的左手坐标系来看的话,这个角度所对应的旋转轴应该是朝屏幕外的方向。但实际上rawimage的z轴是朝向屏幕内的,所以刚好相反,简单来计算就是取videoRotationAngle的负值即可。”

总结

大智:“今天学习了用Unity开发《一起来捉妖》的第二部分,实现了摄像头作为游戏的背景。”

小新:“加上背景以后呢,真的有些AR的感觉了呢。”

洪流学堂公众号回复捉妖,可以获取本教程的源码工程

今日思考题

大智:“动手将今天的工程部署到真机上体验一下。”
小新:“好嘞!”
大智:“收获别忘了分享出来!也别忘了分享给你学Unity的朋友,也许能够帮到他。”

推荐阅读


《大话Unity2019》,大智带小新学Unity2019的有趣经历,让你学Unity更简单。

发布了138 篇原创文章 · 获赞 72 · 访问量 29万+

猜你喜欢

转载自blog.csdn.net/zhenghongzhi6/article/details/90082519