U3DC.COM | 优三帝研究院

Menu

一种Unity多屏输出方案

前言

在两个显示器上显示同一个摄像机的内容,其实最简单的办法,就是在显示器设置中,复制主屏的画面。对于更多的屏幕的输出,也可以采用分屏器等硬件设备来实现。但是如果想对一个屏幕输出结果做一些处理,这种方式就木有办法解决了,实现的办法有很多种,本文主要介绍一种基于 RenderTexture 的方法。

实现思路

在了解需求的最开始,本来想通过两个 Camera 来分别对应两个显示器的输出,这种方法虽然能够解决问题,但是最大的缺点是,将会渲染两遍场景内物体,为了优化这一点,需要利用 RenderTexture 来实现,具体的实现思路如下:

  1. 创建两个Camera,Camera_01 输出到 Display 1,Camera_02 输出到 Display_02;
  2. 为了避免 Display_02 重复渲染两遍,将其 CullMask 设置为 Nothing;
  3. 在 Camera_01 上,通过 OnRenderImage 回调,将渲染结果存储到 RenderTexture;
  4. 在 Camera_02 上,读取上面存储的 RenderTexture,拷贝到后缓冲中;

具体实现

实现只有两部分,Camera_01 输出缓存,Camera_02 读取缓存并输出显示,先看Camera_01 实现缓存输出,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 主屏缓存输出,将Camera 的渲染结果,拷贝到
/// RenderTexture 中,提供外部进行使用
/// </summary>
[RequireComponent(typeof(Camera))]
public class DisplayOutput : MonoBehaviour
{
    private RenderTexture _renderTexture;

    public RenderTexture RenderTexture
    {
        get
        {
            return _renderTexture;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (_renderTexture == null)
        {
            _renderTexture = new RenderTexture(source.width,source.height,source.depth);
        }

        UnityEngine.Profiling.Profiler.BeginSample("Copy Render Texture");

        Graphics.Blit(source, _renderTexture);
        Graphics.Blit(source, destination);

        UnityEngine.Profiling.Profiler.EndSample();
    }
}

上面代码的主要功能就是将 Camera 的渲染结果拷贝到一个 RenderTexture 进行缓存,并将结果输出到当先屏幕,这里有一点需要注意,Graphic.Blit(source,destination) 必须在 Graphic.Blit(source,_renderTexture) 之后进行调用,否则编辑器会报警告: OnRenderImage() possibly didn't write anything to the destination texture ,这并不是 Unity 的 Bug,而就这样的设计的,用户手册中给出的解释如下:

When OnRenderImage finishes, Unity expects that the destination render texture is the active render target. Generally, a Graphics.Blit or manual rendering into the destination texture should be the last rendering operation.

好了,接着说上面的实现,在 RenderTexture 创建完成之后,就需要在 Camera_02 上输出显示,具体的代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Camera))]
public class RenderTextureReceiver : MonoBehaviour
{
    public DisplayOutput input;

    private bool init;

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {

        if (input == null || input.RenderTexture == null)
        {
            Graphics.Blit(source, destination);
        }
        else
        {
            if (!init)
            {
                Camera srcCamera = input.GetComponent<Camera>();
                Camera tarCamera = GetComponent<Camera>();

                int display = tarCamera.targetDisplay;

                tarCamera.CopyFrom(srcCamera);
                tarCamera.cullingMask = 0;
                tarCamera.clearFlags = CameraClearFlags.SolidColor;
                tarCamera.targetDisplay = display;

                init = true;
            }

            UnityEngine.Profiling.Profiler.BeginSample("Set Render Texture");

            Graphics.Blit(input.RenderTexture, destination);

            UnityEngine.Profiling.Profiler.EndSample();
        }
    }
}

总结

经过简单测试,这种方法的 CPU 实现占用低于 1ms(测试结果根硬件有很大差别,我的测试环境是 3.2GHZ i5 处理器,显卡是 AMD Radeon R9 M380),但是在其他硬件上相差应该不会太大。

这种方式的优点如下:

缺点是只能显示与主屏相同的结果,或者进行一些图像后处理,比如夜视、颜色调整等等,没有办法针对场景内单独的物体进行渲染调整。

文献与引用

huosk.github.io/2019/11

打赏
— 于 共写了2709个字
— 文内使用到的标签:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据