使用视窗Viewport

简介

Viewports 想成投影游戏的荧幕. 为了看到游戏, 我们需要有一个表面来绘制它, 这个表面是作为根节点的 Viewport.

../../_images/viewportnode.png

Viewports 也可以添加到场景中, 以便绘制多个区域. 当我们绘制到一个不是根节点的 Viewport 时, 我们将该视区称为渲染目标. 我们可以通过访问它对应的 texture 属性来访问渲染目标的内容. 将任一 Viewport 作为渲染目标时, 我们要么可以同时渲染多个场景, 要么可以渲染到场景中某个对象的 texture 上, 例如渲染到动态天空盒的材质上.

Viewport 有多种使用情况, 包括:

  • 在2D游戏中渲染3D物体

  • 在3D游戏中渲染2D元素

  • 渲染动态纹理

  • 在游戏运行时生成过程纹理

  • 在同一场景中渲染多个摄像机

所有这些用例的共同点是, 你被赋予了在纹理上绘制物体的能力, 就好像它是另一个屏幕一样, 然后可以选择如何处理产生的纹理.

输入

Viewport 也负责将正确调整和缩放的输入事件传递给他们所有的子节点. 通常, 输入是由树中最近的 Viewport 接收的, 但是你可以通过将 'Disable Input' 选为 'on' 来设置 Viewport 不接收输入;这将允许树中最近的 Viewport 捕捉输入.

../../_images/input.png

关于Godot如何处理输入的更多信息, 请阅读 Input Event Tutorial.

侦听器

Godot支持3D声音(在2D和3D节点中);更多信息可以在 Audio Streams Tutorial 中找到. 为了使这种类型的声音能够被听到, Viewport 需要被启用为一个监听器(对于2D或3D). 如果你使用一个自定义的 Viewport 来显示你的 World, 别忘了启用这个功能!

摄像机 (2D和3D)

当使用 Camera / Camera2D 时, 摄像机将始终显示在最近的父节点上 Viewport (朝向根节点). 例如, 在下面的层次结构中:

../../_images/cameras.png

CameraA将显示根节点的 Viewport , 它将绘制MeshA. CameraB将被 Viewport 节点以及MeshB捕获. 即使MeshB在场景层次结构中, 它仍然不会被绘制到根节点的 Viewport 中. 类似地, 在 Viewport 节点中不会看到MeshA, 因为 Viewport 节点仅捕获层次结构中它下面的节点.

每个视窗 Viewport 只能有一个激活的摄像机, 因此, 如果有多个摄像机时, 请确保您需要的那个摄像机的 "current" 属性被设置上, 或者通过调用以下语句来使其成为当前摄像机:

camera.make_current()

默认情况下, 相机将渲染其世界中的所有对象. 在3D中, 相机可以使用他们的 cull_mask 属性和 VisualInstance's layer 属性来限制哪些对象被渲染.

缩放和拉伸

Viewport 有一个 "size" 属性, 表示 Viewport 的尺寸, 单位是像素. 对于 ViewportViewportContainers 的子节点, 这些值被重写, 但对于所有其他的节点, 这设置了它们的分辨率.

也可以通过调用 Viewport 来缩放2D内容, 并使其分辨率与指定的尺寸不同:

viewport.set_size_override(true, Vector2(width, height)) # Custom size for 2D.
viewport.set_size_override_stretch(true) # Enable stretch for custom size.

根节点的 Viewport 用到项目设置中的拉伸选项. 有关缩放和拉伸的更多信息, 请访问 Multiple Resolutions Tutorial

世界

对于3D, 一个 Viewport 将包含一个 World . 这基本上是将物理和渲染相联系的所有. 基于空间的节点将使用最接近的 ViewportWorld 进行注册. 默认情况下, 新创建的 Viewports 不包含 World , 而是使用与其父级 Viewport 相同的 Viewport (根 Viewport 总是包含 World , 默认情况下, 对象会被渲染到这个地方). 可以使用 "world" 属性, 在一个 Viewport 中设置一个 World , 这将隔离该 Viewport 的所有子节点与父级 Viewport World 的交互. 这在一些场景中特别有用, 例如, 你可能想在游戏中显示一个单独的3D角色(比如在星际争霸中).

作为您想要创建的情况的帮助 Viewports 显示单个对象而不想创建 World, Viewport 可以选择使用自己的 World. 当您想要在2D World 中实例化3D角色或对象时, 这非常有用.

对于2D, 每个 Viewport 总是包含它自己的 World2D . 这在大多数情况下都足够了, 但是如果需要共享它们, 可以手动设置 Viewport 的 World2D .

关于如何工作的例子, 请分别参阅演示项目 3D in 2D2D in 3D .

Capture(捕获)

可以查询 Viewport 内容的捕获. 对于根 Viewport , 这实际上是一个屏幕截图. 这可以通过以下代码完成:

# Retrieve the captured Image using get_data().
var img = get_viewport().get_texture().get_data()
# Flip on the Y axis.
# You can also set "V Flip" to true if not on the root Viewport.
img.flip_y()
# Convert Image to ImageTexture.
var tex = ImageTexture.new()
tex.create_from_image(img)
# Set Sprite Texture.
$sprite.texture = tex

但是如果你在 _ready() 中使用, 或者从 Viewport 的 初始化的第一帧开始使用, 你会得到一个空的纹理, 因为没有什么可以作为纹理获得. 你可以用来处理它, 例如:

# Wait until the frame has finished before getting the texture.
yield(VisualServer, "frame_post_draw")
# You can get the image after this.

视区容器

如果 ViewportViewportContainer 的子节点, 它将变为活动状态并显示其内部的任何内容. 布局看起来像这样:

../../_images/container.png

如果 ViewportViewportContainerStretch 设置为 true , 则 Viewport 将完全覆盖其父 ViewportContainer 的区域. 注意: ViewportContainer 的大小不能小于 Viewport 的大小.

渲染

由于以下事实 Viewport 是进入另一个渲染表面的入口, 它会暴露一些可能与项目设置不同的渲染属性. 第一个是MSAA, 您可以选择为每个使用不同级别的MSAA Viewport, 默认行为是DISABLED. 您还可以设置 Viewport 以使用HDR, 当您想要在纹理中存储超出0.0 - 1.0范围的值时,HDR非常有用.

如果你知道 Viewport 将被如何使用, 可以把它的用法设置为3D或2D.Godot就会根据选择限制 Viewport 的绘制方式;默认是3D. 与3D使用模式相比,2D使用模式的速度稍快, 占用的内存也少. 如果视窗没有在3D中渲染任何东西, 将 :ref:`Viewport <class_Viewport> ` 的使用属性设置为2D是一个好主意.

注解

如果需要在视图中渲染3D阴影, 请确保将视图的 Shadow Atlas Size阴影贴图集大小 属性设置为大于0的值. 否则, 阴影将不会被渲染. 作为参考, 项目设置默认定义为4096.

Godot还提供了一种自定义内部绘制方式的方法 Viewports 使用"Debug Draw". Debug Draw允许您指定以下四个选项之一 Viewport 将显示在其中绘制的内容. 默认情况下禁用Debug Draw.

../../_images/default_scene.png

禁用Debug Draw绘制的场景

其他三个选项是Unhaded,Overdraw和Wireframe. 无阴影在不使用光照信息的情况下绘制场景, 因此所有对象都显示为其反射颜色的扁平颜色.

../../_images/unshaded.png

Debug Draw设置为Unshaded的相同场景

Overdraw使用添加剂混合绘制半透明的网格, 以便您可以看到网格重叠的方式.

../../_images/overdraw.png

Debug Draw设置为Overdraw的同一场景

最后, 绘制的场景中线框仅使用网格中里边缘的三角形.

注解

线框模式的效果只在编辑器中可看见, 在项目运行时不可见.

渲染目标

当渲染到一个 Viewport 时, 里面的东西在场景编辑器中是看不到的. 为了显示内容, 你必须在某个地方绘制 Viewport's ViewportTexture. 这可以通过代码使用, 例如:

# This gives us the ViewportTexture.
var rtt = viewport.get_texture()
sprite.texture = rtt

或者可以通过选择"New ViewportTexture"在编辑器中指定它

../../_images/texturemenu.png

然后选择您想要使用的 Viewport.

../../_images/texturepath.png

每一帧, Viewport 的纹理都会被清除, 并使用默认的透明色(或者如果 Transparent Bg 被设置为 true ). 这可以通过设置 Clear Mode 为Never或Next Frame来改变. 顾名思义,Never意味着纹理将永远不会被清除, 而Next Frame将在下一帧清除纹理, 然后将自己设置为Never.

默认情况下, 当 ViewportViewportTexture 在一个帧中被绘制时, 会发生重新渲染. 如果是可见, 将被渲染;否则, 它将不会被渲染. 这个行为可以改为手动渲染一次, 或者总是渲染, 不管是否可见. 这种灵活性使用户可以渲染一次图像, 然后使用纹理, 而不需要承担每一帧渲染的消耗.

一定要查看Viewport演示! 可以下载演示档案中的Viewport文件夹, 或https://github.com/godotengine/godot-demo-projects/tree/master/viewport