一种Unity多屏输出方案
前言
在两个显示器上显示同一个摄像机的内容,其实最简单的办法,就是在显示器设置中,复制主屏的画面。对于更多的屏幕的输出,也可以采用分屏器等硬件设备来实现。但是如果想对一个屏幕输出结果做一些处理,这种方式就木有办法解决了,实现的办法有很多种,本文主要介绍一种基于 RenderTexture 的方法。
实现思路
在了解需求的最开始,本来想通过两个 Camera 来分别对应两个显示器的输出,这种方法虽然能够解决问题,但是最大的缺点是,将会渲染两遍场景内物体,为了优化这一点,需要利用 RenderTexture 来实现,具体的实现思路如下:
- 创建两个Camera,Camera_01 输出到 Display 1,Camera_02 输出到 Display_02;
- 为了避免 Display_02 重复渲染两遍,将其 CullMask 设置为 Nothing;
- 在 Camera_01 上,通过 OnRenderImage 回调,将渲染结果存储到 RenderTexture;
- 在 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),但是在其他硬件上相差应该不会太大。
这种方式的优点如下:
- 实现简单高效;
- 其额外性能开销,与场景的复杂度无关;
缺点是只能显示与主屏相同的结果,或者进行一些图像后处理,比如夜视、颜色调整等等,没有办法针对场景内单独的物体进行渲染调整。