如何在Unity中动态地向整个模板缓冲区写入数据

3

我想要实现什么?

我想在Unity3D中实现这样一个效果:将几个摄像机叠加到一起。每个摄像机可以绘制到屏幕的特定区域。如果可能的话,我希望这些区域可以动态地改变。 我正在使用最新版本的unity和URP。

从技术上讲我怎么看:

出于实现和性能原因,似乎写入模板缓冲区是正确的方法。这样,我可以为每个相机仅渲染所需的屏幕部分。而且,使用模板之后就很容易了,因为Unity中的前向渲染设置已经提供了这些功能。

我无法弄清楚的问题:

问题是,我不知道如何有效地写入整个模板缓冲区(每帧)。最好的方式是使用计算着色器(或者可能是简单的脚本),直接在某些计算之后写入值。有没有办法这样做?如果有,怎么做?

另一种选择可能是在每个相机前面放置一个透明的四边形,并像那样写入模板缓冲区。但是 1)似乎片段缓冲区中存在SV_StencilRef关键字,但Unity尚不支持?2)无论如何我仍然会失去性能。

感谢任何有关如何解决这个问题的帮助/想法。

编辑(澄清):我想要能够渲染自由形状,而不仅仅是矩形,这样就无法使用标准的ViewportRect。 经过一些搜索,我发现Voronoi分割屏幕与我想要实现的内容非常相似(从技术角度来看)。(查看此处


关于...https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@7.2/manual/camera-stacking.html? - Lotan
1
@Lotan 这很可能是 OP 目前在 URP 场景中使用的默认多个相机的方式 ;) - derHugo
2个回答

3

如果我理解正确,您只需要调整不同相机的视口矩形(https://docs.unity3d.com/ScriptReference/Camera-rect.html)来确定哪个相机应该渲染屏幕的哪个部分。

回复评论:不,它没有被拉伸。以下是使用四个相机的示例:

创建一个带有四个相机的场景,在其中添加此脚本并将相机添加到脚本的数组中。我添加了_movingObject只是为了看到一些运动,但这并不是必要的。

using UnityEngine;

public class CameraHandler : MonoBehaviour
{
    [SerializeField] private Transform _movingObject;
    [SerializeField] private float _posMod = 10.0f;

    [SerializeField] private float _cameraPosMod = 0.1f;
    [SerializeField] private Camera[] _cameras;

    private void Update()
    {
        float t = Time.time;
        float x = Mathf.Sin(t);
        float y = Mathf.Cos(t);

        if (_movingObject) _movingObject.position = new(x * _posMod, 1.0f, y * _posMod);

        Vector2 center = new(0.5f + x * _cameraPosMod, 0.5f + y * _cameraPosMod);

        // bottom left camera
        _cameras[0].rect = new(0.0f, 0.0f, center.x, center.y);

        // bottom right camera
        _cameras[1].rect = new(center.x, 0.0f, 1.0f - center.x, center.y);

        // upper left camera
        _cameras[2].rect = new(0.0f, center.y, center.x, 1.0f - center.y);

        // upper right camera
        _cameras[3].rect = new(center.x, center.y, 1.0f - center.x, 1.0f - center.y);
    }
}

这很可能行不通。改变矩形 - 就像你说的那样 - 会改变相机渲染场景的哪个部分...但由于相机堆栈,它仍然会被拉伸到整个屏幕上,据我所知。 - derHugo
好的,使用这种方法意味着我必须为每个相机使用矩形。在我的情况下,我希望能够使用自由形式 - 因此仅使用相机视口矩形是不够/不可能的。 如果有一种方法可以解决问题,那就是最好的解决方案,但我认为这是不可能的。 - Eyap

2

虽然这不是关于模板缓冲的答案,但我最近有一个(希望)类似的用例。

主要问题:在URP相机堆栈中

  • 如果相机设置为Base,它将覆盖整个屏幕
  • 您无法调整任何Overlay相机上的视口

实际上,您可以通过代码尝试设置viewport ->结果是您的相机仅渲染场景的正确部分... 它会拉伸到整个屏幕^^


最终我做的是

  • Leave all content and cameras at the origin position

  • Apply according masks to filter the content per camera

  • Make your camera Overlay (as usual)

  • go through a custom Camera.projectionMatrix

    m_Camera.projectionMatrix = Matrix4x4.Translate(projectionOffset) * Matrix4x4.Perspective(m_Camera.fieldOfView, m_Camera.aspect, m_Camera.nearClipPlane, m_Camera.farClipPlane);
    

    where the projectionOffset is an offset in viewport space (normalized 0 - 1) from the bottom left corner.

    For example in my case I wanted a minimap at 400, 400 pixels from the top-right corner so I did

    var topRightOffsetPixels = new Vector2(400, 400);
    var topRightOffsetViewport = Vector2.one - new Vector2(topRightOffsetPixels.x * 2 / Screen.width, topRightOffsetPixels.y * 2 / Screen.height);
    m_Camera.projectionMatrix = Matrix4x4.Translate(topRightOffsetViewport) * Matrix4x4.Perspective(m_Camera.fieldOfView, m_Camera.aspect, m_Camera.nearClipPlane, m_Camera.farClipPlane);
    

另请参阅Matrix4x4.Perspective


这绝对很有趣!但正如其他评论中所说,我需要能够绘制自由形状(主要是三角形,但也对各种类型感兴趣)。 据我所知,您的方法只能用于矩形。 - Eyap
这并不是真正与矩形相关的问题..相机是一个矩形..但你可以用它来渲染任何形状..或者我可能误解了你的担忧? - derHugo
换句话说:只有通过覆盖相机渲染的任何对象才会覆盖第一个相机的图像=>如果您的对象是任何形式的,那么只有这些像素将被替换..背景等仅由主相机渲染。 - derHugo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接