Unity 中的照片/视频摄像头

启用相机访问功能

必须声明“网络摄像头”功能,应用才能使用相机

  1. 在 Unity 编辑器中,导航到“编辑”>“项目设置”>“玩家”页,转到“玩家设置”
  2. 选择“Windows Store”选项卡
  3. 在“发布设置”>“功能”部分,选中“网络摄像头”和“麦克风”功能

每次只能使用相机执行一次操作。 可以使用 Unity 2018 和更低版本中的 UnityEngine.XR.WSA.WebCam.Mode 或者 Unity 2019 和更高版本中的 UnityEngine.Windows.WebCam.Mode 来检查相机当前所处的模式。 可用模式为照片、视频或无。

拍照

命名空间(在 Unity 2019 之前):UnityEngine.XR.WSA.WebCam
命名空间(Unity 2019 及更高版本):UnityEngine.Windows.WebCam
类型:PhotoCapture

PhotoCapture 类型允许使用照片视频相机拍摄静态照片。 使用 PhotoCapture 拍照的一般模式如下

  1. 创建 PhotoCapture 对象
  2. 使用所需的设置创建 CameraParameters 对象
  3. 通过 StartPhotoModeAsync 启动照片模式
  4. 拍摄所需的照片
    • (可选)与该照片交互
  5. 停止照片模式并清理资源

PhotoCapture 的通用设置

对于所有三种用途,请从上述前三个步骤开始

首先创建 PhotoCapture 对象

private void Start()
{
    PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
}

接下来,存储该对象,设置参数,然后启动照片模式

private PhotoCapture photoCaptureObject = null;

void OnPhotoCaptureCreated(PhotoCapture captureObject)
{
    photoCaptureObject = captureObject;

    Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();

    CameraParameters c = new CameraParameters();
    c.hologramOpacity = 0.0f;
    c.cameraResolutionWidth = cameraResolution.width;
    c.cameraResolutionHeight = cameraResolution.height;
    c.pixelFormat = CapturePixelFormat.BGRA32;

    captureObject.StartPhotoModeAsync(c, false, OnPhotoModeStarted);
}

最后,你还将使用此处所述的相同清理代码

void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
    photoCaptureObject.Dispose();
    photoCaptureObject = null;
}

完成这些步骤后,可以选择要拍摄的照片类型。

将照片捕获到文件

最简单的操作是将照片直接捕获到文件中。 照片可以保存为 JPG 或 PNG。

如果成功启动了照片模式,请拍照并将其存储在磁盘上

private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result)
{
    if (result.success)
    {
        string filename = string.Format(@"CapturedImage{0}_n.jpg", Time.time);
        string filePath = System.IO.Path.Combine(Application.persistentDataPath, filename);

        photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);
    }
    else
    {
        Debug.LogError("Unable to start photo mode!");
    }
}

将照片捕获到磁盘后,退出照片模式,然后清理对象

void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
{
    if (result.success)
    {
        Debug.Log("Saved Photo to disk!");
        photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
    }
    else
    {
        Debug.Log("Failed to save Photo to disk");
    }
}

使用定位将照片捕获到 Texture2D

将数据捕获到 Texture2D 的过程类似于捕获到磁盘。

请按照上面的设置过程进行操作。

在 OnPhotoModeStarted 中,将某一帧捕获到内存中

private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result)
{
    if (result.success)
    {
        photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
    }
    else
    {
        Debug.LogError("Unable to start photo mode!");
    }
}

然后,将结果应用于纹理并使用上面的通用清理代码。

void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
    if (result.success)
    {
        // Create our Texture2D for use and set the correct resolution
        Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
        Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
        // Copy the raw image data into our target texture
        photoCaptureFrame.UploadImageDataToTexture(targetTexture);
        // Do as we wish with the texture such as apply it to a material, etc.
    }
    // Clean up
    photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}

可定位相机

若要将此纹理放在场景中并使用可定位相机矩阵来显示它,请将以下代码添加到 result.success 检查中的 OnCapturedPhotoToMemory

if (photoCaptureFrame.hasLocationData)
{
    photoCaptureFrame.TryGetCameraToWorldMatrix(out Matrix4x4 cameraToWorldMatrix);

    Vector3 position = cameraToWorldMatrix.GetColumn(3) - cameraToWorldMatrix.GetColumn(2);
    Quaternion rotation = Quaternion.LookRotation(-cameraToWorldMatrix.GetColumn(2), cameraToWorldMatrix.GetColumn(1));

    photoCaptureFrame.TryGetProjectionMatrix(Camera.main.nearClipPlane, Camera.main.farClipPlane, out Matrix4x4 projectionMatrix);
}

Unity 在其论坛上提供了将投影矩阵应用于特定着色器的示例代码

拍摄照片并与原始字节交互

若要与内存中帧的原始字节交互,请按照与将照片捕获到 Texture2D 时相同的设置步骤和 OnPhotoModeStarted 进行操作。 不同之处在于 OnCapturedPhotoToMemory,可以在其中获取原始字节并与之交互

在此示例中,你将创建一个通过 SetPixels() 进一步处理或应用于纹理的列表

void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
    if (result.success)
    {
        List<byte> imageBufferList = new List<byte>();
        // Copy the raw IMFMediaBuffer data into our empty byte list.
        photoCaptureFrame.CopyRawImageDataIntoBuffer(imageBufferList);

        // In this example, we captured the image using the BGRA32 format.
        // So our stride will be 4 since we have a byte for each rgba channel.
        // The raw image data will also be flipped so we access our pixel data
        // in the reverse order.
        int stride = 4;
        float denominator = 1.0f / 255.0f;
        List<Color> colorArray = new List<Color>();
        for (int i = imageBufferList.Count - 1; i >= 0; i -= stride)
        {
            float a = (int)(imageBufferList[i - 0]) * denominator;
            float r = (int)(imageBufferList[i - 1]) * denominator;
            float g = (int)(imageBufferList[i - 2]) * denominator;
            float b = (int)(imageBufferList[i - 3]) * denominator;

            colorArray.Add(new Color(r, g, b, a));
        }
        // Now we could do something with the array such as texture.SetPixels() or run image processing on the list
    }
    photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}

视频视频

命名空间(在 Unity 2019 之前):UnityEngine.XR.WSA.WebCam
命名空间(Unity 2019 及更高版本):UnityEngine.Windows.WebCam
类型:VideoCapture

VideoCapture 的作用类似于 PhotoCapture。 唯一的两个差别是,必须指定每秒帧数 (FPS) 值,并且只能作为 .mp4 文件直接保存到磁盘。 VideoCapture 的使用步骤如下

  1. 创建 VideoCapture 对象
  2. 使用所需的设置创建 CameraParameters 对象
  3. 通过 StartVideoModeAsync 启动视频模式
  4. 开始录制视频
  5. 停止录制视频
  6. 停止视频模式并清理资源

首先创建 VideoCapture 对象 VideoCapture m_VideoCapture = null;

void Start ()
{
    VideoCapture.CreateAsync(false, OnVideoCaptureCreated);
}

接下来,设置所需的录制参数并启动。

void OnVideoCaptureCreated(VideoCapture videoCapture)
{
    if (videoCapture != null)
    {
        m_VideoCapture = videoCapture;

        Resolution cameraResolution = VideoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
        float cameraFramerate = VideoCapture.GetSupportedFrameRatesForResolution(cameraResolution).OrderByDescending((fps) => fps).First();

        CameraParameters cameraParameters = new CameraParameters();
        cameraParameters.hologramOpacity = 0.0f;
        cameraParameters.frameRate = cameraFramerate;
        cameraParameters.cameraResolutionWidth = cameraResolution.width;
        cameraParameters.cameraResolutionHeight = cameraResolution.height;
        cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;

        m_VideoCapture.StartVideoModeAsync(cameraParameters,
                                            VideoCapture.AudioState.None,
                                            OnStartedVideoCaptureMode);
    }
    else
    {
        Debug.LogError("Failed to create VideoCapture Instance!");
    }
}

启动后,开始录制

void OnStartedVideoCaptureMode(VideoCapture.VideoCaptureResult result)
{
    if (result.success)
    {
        string filename = string.Format("MyVideo_{0}.mp4", Time.time);
        string filepath = System.IO.Path.Combine(Application.persistentDataPath, filename);

        m_VideoCapture.StartRecordingAsync(filepath, OnStartedRecordingVideo);
    }
}

开始录制后,可以更新 UI 或行为以启用停止。 此处只需进行日志记录。

void OnStartedRecordingVideo(VideoCapture.VideoCaptureResult result)
{
    Debug.Log("Started Recording Video!");
    // We will stop the video from recording via other input such as a timer or a tap, etc.
}

稍后,需要使用计时器或用户输入等方式来停止录制。

// The user has indicated to stop recording
void StopRecordingVideo()
{
    m_VideoCapture.StopRecordingAsync(OnStoppedRecordingVideo);
}

录制停止后,停止视频模式并清理资源。

void OnStoppedRecordingVideo(VideoCapture.VideoCaptureResult result)
{
    Debug.Log("Stopped Recording Video!");
    m_VideoCapture.StopVideoModeAsync(OnStoppedVideoCaptureMode);
}

void OnStoppedVideoCaptureMode(VideoCapture.VideoCaptureResult result)
{
    m_VideoCapture.Dispose();
    m_VideoCapture = null;
}

疑难解答

  • 没有可用的解决方法
    • 确保在项目中指定了“网络摄像头”功能

下一个开发检查点

如果遵循我们规划的 Unity 开发检查点旅程,则你正处于探索混合现实平台功能和 API 的过程之中。 从这里,你可以继续了解下一个主题:

或直接跳到在设备或模拟器上部署应用:

你可以随时返回到 Unity 开发检查点

另请参阅