【Unity】相机 Cameras

1 前言

        主要介绍官方文档中相机模块的内容。

        关于“9动态分辨率”,这部分很多API文档只是提了一下,具体细节还需要自己深入API才行。

2 摄像机介绍

        Unity 场景在三维空间中表示游戏对象。由于观察者的屏幕是二维屏幕,Unity 需要捕捉视图并将其“平面化”以进行显示。它使用摄像机来实现这一点。在 Unity 中,我们可以通过将一个Camera组件添加到游戏对象来创建摄像机,也可以右键创建相机,如创建Cube一样。

2.1相机的视野

        摄像机的视野是由它的Transform和 Camera 组件来定义。Transform来定义相机的位置、旋转,Z轴是视野方向,Y轴是屏幕顶端,Camera 组件的设置还定义了视图区域的大小和形状。当相机移动和旋转时,显示的视图会随之移动和旋转。

2.2 透视与正交相机

        现实世界中的摄像机(实际相当于人眼)在观察外界事物时,物体距离视点越远,看起来越小。这种众所周知的透视效果在艺术和计算机图形领域广泛应用,对于创建现实场景至关重要。这就是透视摄像机,当然,Unity是支持的。但有时需要专门在没有这种透视效果的条件下渲染视图,例如,需要创建一种与真实世界的对象不完全相同的地图或信息显示效果,显示的对象不随距离变远而缩小,实现这种效果的摄像机称为正交摄像机,Unity 摄像机也有这样的选项。在透视和正交模式下观察场景称为摄像机投影。透视(左)与正交(右)效果:

2.3 视野区域形状

        对于从当前位置能“观察”的最远距离方面,透视和正交摄像机都存在一定的限制。该限制由垂直于摄像机向前 (Z) 方向的平面定义。此平面称为远裁剪面,因为与摄像机距离较远的对象将被“裁剪”(即,不在渲染范围内)。摄像机附近还有一个相应的近裁剪面;可观察的距离范围位于这两个平面之间。

        在非透视模式下,无论距离远近,对象大小不变。这表示,正交摄像机的视体由两个裁剪面之间的长方体定义。如图:

        使用透视摄像机时,对象会随其与摄像机的距离增大而缩小。这表示场景中可视部分的宽度和高度随着距离的增大而增大。因此,透视摄像机的视体不是一个长方体,而是金字塔形状,其顶点位于摄像机位置而底部位于远裁剪面。不过,该形状并不是严格的金字塔形,因为顶部被近裁剪面截断了,这种被截断的金字塔形状称为视锥体。由于视锥体的高度并非常量,视锥体由其宽度与高度之比(称为宽高比)以及顶部与底部之间在顶点处的夹角(称为视野即 FOV)定义。请参阅关于了解视锥体的页面,了解更多详细说明。视椎体如图:

2.4 相机视图的背景

        在渲染场景之前,我们可以设置相机如何处理物体之间的空白区域的背景。例如,我们可以选择在背景上渲染场景之前采用纯色填充背景,或者绘制天空或远处的背景,甚至保留前一帧的内容。有关配置此设置的信息,请参阅 Camera Inspector 参考中的 Background 属性。有关绘制天空的信息,请参见天空

3 使用多个相机

        创建一个场景后,默认是包含一个相机的,这在大多数情况下就已经足够了。但我们也可以在场景中添加任意数量的摄像机,还能以不同的方式组合它们的视图,如下所述。

3.1 切换相机

        默认情况下,摄像机会用它所渲染的画面覆盖整个屏幕,因此同一时刻只能看到一个摄像机的画面(即 depth 属性具有最高值的摄像机)。通过在脚本中禁用一个摄像机并启用另一个,即可从一个摄像机的镜头“切”到另一个,从而得到场景的多个镜头画面。在某些情况下,例如要在俯瞰地图视角和第一人称视角之间切换时,就可以采用这种方法。

using UnityEngine;

public class ExampleScript : MonoBehaviour {
    public Camera firstPersonCamera;
    public Camera overheadCamera;

    // 调用此函数可禁用 FPS 摄像机,并启用overhead相机。
    public void ShowOverheadView() {
        firstPersonCamera.enabled = false;
        overheadCamera.enabled = true;
    }
    
    // 调用此函数可启用 FPS 摄像机,并禁用overhead相机。
    public void ShowFirstPersonView() {
        firstPersonCamera.enabled = true;
        overheadCamera.enabled = false;
    }
}

3.2 渲染画中画

        通常情况下,我们希望至少有一个摄像机视图覆盖整个屏幕(默认设置),但在屏幕的一个小区域内显示另一个视图往往也很有用。例如,我们可能会在驾驶游戏中显示一个后视镜,或者在第一人视角的屏幕角落里显示俯瞰小地图。我们可以使用摄像机的 Viewport Rect 属性设置摄像机在屏幕上的矩形的大小。

        视图矩形的坐标相对于屏幕经过“标准化”,底边和左边是 0.0 坐标,而顶边和右边是 1.0,坐标值为 0.5 表示位于中间位置。除了视图大小之外,还应将具有较小视图的摄像机的 depth 属性设置为一个高于背景摄像机的值。确切的值无关紧要,总之规则是具有较高 depth 值的摄像机的渲染画面会覆盖在较低值摄像机的渲染画面之上。

4 使用物理摄像机 (Physical Camera)

        摄像机组件的 Physical Camera 属性在 Unity 摄像机上模拟真实摄像机格式。这可用于从同样模拟真实摄像机的 3D 建模应用程序导入摄像机信息。

Unity 提供的设置与大多数 3D 建模应用程序的物理摄像机设置相同。控制摄像机视野的两个主要属性是 Focal LengthSensor Size

  • Focal Length:传感器和摄像机镜头之间的距离,即焦距。此属性决定了垂直视野。Unity 摄像机处于 Physical Camera 模式时,改变 Focal Length 也会相应改变视野。焦距越小,视野越大,反之亦然。

  • Sensor Size:捕捉图像的传感器的宽度和高度,表示传感器大小。这些数值决定了物理摄像机的宽高比。可从对应于真实摄像机格式的几个预设传感器大小中进行选择,或设置自定义大小。传感器宽高比与渲染的宽高比(在 Game 视图中设置)不同时,可以控制 Unity 如何将摄像机图像与渲染的图像匹配(请参阅下文中关于 Gate Fit 的信息)。

4.1 Lens Shift

        Lens Shift(镜头位移,组件上的属性) 从传感器上水平和垂直地偏移摄像机的镜头。这样一来便可以改变焦点中心,并在渲染的帧中重新定位拍摄对象,确保很少或完全没有失真。

        这种方法在建筑摄影中很常见。例如,如果要拍摄一座高楼,可以旋转摄像机。但这会使图像失真,导致平行线看起来发生会聚。如图:

如果把镜头上移,而不是旋转摄像机,就可以改变构图以包含楼顶,但平行线保持直线。如图:

同样,可以使用水平镜头位移方法来拍摄宽大的对象,避免由于旋转摄像机而可能产生的失真。如图:

4.1.1 Lens Shift和视锥体倾斜

        镜头移位的一个副作用是会使摄像机的视椎体倾斜。这意味着摄像机的中心线与其视锥体之间的角度在一侧要小于另一侧。如图:

这里再给个动图演示:

即往那边移,视椎体就往哪边倾斜。

        此功能可用于根据视角来创造视觉效果。例如,在赛车游戏中,可能希望将视角保持在接近地面的较低位置。镜头移位是一种不用脚本即可实现视锥体倾斜的方式。

        有关更多信息,请参阅关于使用斜视锥体的文档。

4.2 Gate Fit

        Camera 组件的 Gate Fit 属性决定了 Game 视图和物理摄像机传感器具有不同宽高比时会发生什么情况。

在 Physical Camera 模式中,一个摄像机有两个“门”。

  • 根据 Aspect 下拉菜单中设置的分辨率,在 Game 视图中渲染的区域被称为“分辨率门”。(Game视图可看到的)

  • 摄像机实际看到的区域(由 Sensor Size 属性定义)被称为“胶片门”。(摄像机拍摄到的)

两个门具有不同宽高比时,Unity 让分辨率门“适应”胶片门。有几种适应模式,但是这些模式都会产生以下三种结果之一。

  • 裁剪 (Cropping):适应后,胶片门超过分辨率门时,Game 视图在满足宽高比的情况下渲染尽可能多的摄像机图像面积,并裁掉其余部分。

  • 过扫描 (Overscanning):适应后,胶片门超过分辨率门时,Game 视图仍然对摄像机视野之外的场景部分进行渲染计算。【感觉是不是应该是胶片门小于分辨率门?文档上是超过。】

  • 拉伸 (Stretching):Game 视图渲染完整的摄像机图像,将其水平或垂直拉伸以适应宽高比。

要在 Scene 视图中查看这些门,并查看它们如何相互适应,请选择摄像机并查看其视锥体。分辨率门是摄像机远裁剪面。胶片门是位于视锥体底部的第二个矩形。

4.2.1 Gate Fit 模式

        选择的 Gate Fit 模式决定了 Unity 如何调整分辨率门的大小(因而调整摄像机的视锥体)。胶片门始终保持相同大小。以下部分提供了关于每种 Gate Fit 模式的更多详细信息。

4.2.1.1 Vertical

        Gate Fit 设置为 Vertical 时,Unity 让分辨率门适应胶片门的高度(Y 轴)。对传感器宽度 (Sensor Size > X) 进行的任何更改都不会影响渲染的图像(分辨率门)。

        如果传感器宽高比大于 Game 视图宽高比,Unity 会在两侧裁剪渲染的图像(PS:丢弃了两侧的内容):

如果传感器宽高比小于 Game 视图宽高比,Unity 会在两侧对渲染的图像进行过扫描(PS:两侧绿色是胶片门没有覆盖的区域,进行过扫描):

4.2.1.2 Horizontal

        Gate Fit 设置为 Horizontal 时,Unity 让分辨率门适应胶片门的宽度(X 轴)。对传感器高度 (Sensor Size > Y) 进行的任何更改都不会影响渲染的图像。(基本和Vertical一样,只是一个适应Y轴,一个适应X轴)

        如果传感器宽高比大于 Game 视图宽高比,Unity 会在顶部和底部对渲染的图像进行过扫描:

如果传感器宽高比小于 Game 视图宽高比,则会在顶部和底部裁剪渲染的图像。

4.2.1.3 None

        Gate Fit 设置为 None 时,Unity 让分辨率门适应胶片门的宽度和高度(X 轴和 Y 轴)。Unity 会拉伸渲染的图像以适应 Game 视图宽高比,即会以胶片门的图像为基准,然后拉伸到分辨率门的尺寸。

如图所示,两个都是None模式下的经过适应拉伸的,右下角显示的小窗口就是分辨率门的尺寸,可以看到左侧图像被拉宽了,右侧图像被拉窄了,都是基于胶片门图像来拉伸到分辨率门的尺寸的。另外,此时远裁剪面就是胶片门。

4.2.1.4 Fill 和 Overscan

Gate Fit 设置为 FillOverscan 时,Unity 根据分辨率门和胶片门的宽高比,自动进行垂直或水平适应。

  • Fill 让分辨率门适应胶片门的较小轴,并裁剪摄像机图像的其余部分。(这种模式下,胶片门>=分辨率门)

  • Overscan 让分辨率门适应胶片门的较大轴,并对摄像机图像边界以外的区域进行过扫描。(这种模式下,胶片门<=分辨率门)

PS:具体可以自己动手试试看看,这里说小、大轴说得太模糊了,可以理解为分辨率门匹配两轴,门小时匹配的轴是小轴,门大时匹配的轴是大轴。

5 摄像机和深度纹理

        相机可以生成深度(depth)、深度+法线(normals)或运动矢量(vector)纹理。这是一个简化的G-buffer纹理,可以用于后期处理效果或实现自定义光照模型。这些主要用于效果,例如,后期处理效果经常使用深度信息。

        深度纹理中的像素值在0 ~ 1之间,呈非线性分布。精度通常为32位或16位,具体取决于所使用的配置和平台。当从深度纹理读取时,返回0到1范围内的高精度值。如果我们需要获得与Camera的距离,或者其他0-1的线性值,请使用helper macros手动计算。

        大多数现代硬件和图形 API 都支持深度纹理。下面列出了特殊要求:Direct3D 11+ (Windows), opengl3 + (Mac/Linux), Metal (iOS)和流行的控制台支持深度纹理。

        可使用脚本中的Camera.depthTextureMode变量来启用摄像机的深度纹理模式。还可以使用Shader Replacement功能来自行构建类似纹理。

有三种可能的深度纹理模式:

  • DepthTextureMode.Depth:深度纹理。

  • DepthTextureMode.DepthNormals: 深度和视图空间法线打包成一个纹理。

  • DepthTextureMode.MotionVectors:当前帧的每个屏幕纹理像素(texel)的每像素屏幕空间运动。打包到RG16 纹理中。

这些是标志(flag),因此可以指定上述纹理的任何组合。

5.1 DepthTextureMode.Depth 纹理

        此模式将构建一个屏幕大小的深度纹理。

        渲染深度纹理时使用的着色器通道与用于阴影投射物渲染的着色器通道相同(ShadowCaster 通道类型)。因此,通过扩展,如果着色器不支持阴影投射(即在着色器或任何后备着色器中没有阴影投射物通道),则使用该着色器的对象将不会显示在深度纹理中。

  • 让着色器 fallback 到另一个具有阴影投射通道的着色器,或者

  • 如果我们在使用 surface shaders,那么添加 addshadow 指令也能让它们生成阴影通道。

请注意,仅“不透明”对象(这些对象的材质和着色器设置为使用小于等于 2500 的 render queue )会渲染到深度纹理中。

5.2 DepthTextureMode.DepthNormals 纹理

        这将构建屏幕大小的 32 位(8 位/通道)纹理,其中视图空间法线编码到 R&G 通道中,而深度编码到 B&A 通道中。法线使用立体投影(Stereographic projection)进行编码,深度是打包到两个 8 位通道中的 16 位值。

        UnityCG.cginc include文件具有一个 helper 函数 DecodeDepthNormal,可从编码的像素值中解码深度和法线。返回的深度在 0 到 1 范围内。

        关于如何使用深度和法线纹理的例子,请参考 Replacing shaders at runtime 或在 Post-processing and full-screen effects中替换环境遮挡(Ambient Occlusion)。

5.3 DepthTextureMode.MotionVectors 纹理

        这将构建屏幕大小的 RG16(16 位浮点/通道)纹理,其中屏幕空间像素运动编码到 R&G 通道中。像素运动在屏幕 UV 空间中进行编码。

        当从这个纹理运动中采样时,从编码像素返回的范围是[-1,1]。这将是上一帧到当前帧的UV偏移量。

5.4 提示和技巧

        摄像机的Inspector面板会指示摄像机何时渲染深度或深度+法线纹理。

        从摄像机请求深度纹理的方式(Camera.depthTextureMode)可能意味着在禁用需要深度纹理的效果后,摄像机可能仍会继续渲染深度纹理。若摄像机上存在多个效果,其中每个效果都需要深度纹理,则无法在禁用单个效果的情况下自动禁用深度纹理渲染。

        在实现复杂的着色器或图像效果时,切记平台之间的渲染差异。尤其是,在图像效果中使用深度纹理通常需要对 Direct3D和抗锯齿进行特殊处理。

        在某些情况下,深度纹理可能直接来自本机 Z 缓冲区。如果在深度纹理中看到了瑕疵,请确保使用该纹理的着色器未写入 Z 缓冲区(使用 ZWrite Off)。

5.5 着色器变量

        深度纹理可用于在着色器中作为全局着色器属性进行采样。通过声明名为 _CameraDepthTexture 的采样器,我们将能够为摄像机采样主深度纹理。

        _CameraDepthTexture 始终引用摄像机的主深度纹理。相比之下,我们可以使用 _LastCameraDepthTexture 来引用任何摄像机渲染的最后一个深度纹理。例如,如果我们使用辅助摄像机渲染脚本中的半分辨率深度纹理并希望将其提供给后期处理着色器,这种做法可能很有用。

        运动矢量纹理(启用时)在着色器中可用作全局着色器属性。通过声明名为“_CameraMotionVectorsTexture”的采样器,我们可以为当前执行渲染的摄像机采样纹理。

5.6 部分内部原理

        深度纹理可以直接来自实际的深度缓冲(depth buffer),或者在单独的通道中渲染,这取决于所使用的渲染路径和硬件。通常当使用延迟着色渲染路径(Deferred Shading rendering path)时,深度纹理是“免费的”,因为它们是G-buffer渲染的产物。

        当 DepthNormals 纹理在单独通道中渲染时,此过程通过

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值