NSDT工具推荐Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割

本教程将带你完成使用 Cesium for Unreal 构建虚拟现实 (VR) 应用程序的过程。 它将讨论将你的应用程序部署到tethered和独立头戴式设备,并将提供 C++ 和蓝图的示例代码。

我们将学习如何:

  • 在 Unreal 中创建 VR-ready 项目
  • 创建一个适用于你的 VR 设备的简单用户控制器
  • 直接从 Unreal 在 VR 设备中预览你的关卡
  • 将应用程序打包并部署到独立头戴式设备

请先做好以下准备工作:

  • VR 设备,例如 Oculus Quest、HTC Vive 或 Valve Index。
  • 与 VR 设备兼容的计算机。
  • 虚幻引擎的已安装版本(至少 4.26 或更高版本)。
  • Cesium for Unreal 插件的安装版本。
  • 带有 C++ 工作负载的桌面开发的 Visual Studio 2019。

1、搭建项目

在 Unreal 中创建一个新项目,并在 New Project Categories 下选择 Games。 点击下一步。

在选择模板下,选择空白模板。 点击下一步。

在项目设置下,可以选择 C++ 或蓝图。 本教程将为这两种项目类型提供说明。

同样在“项目设置”下,选择“可缩放 3D 或 2D”、“禁用光线跟踪”、“移动设备/平板电脑”和“无初学者内容”。 为你的项目命名(例如,“CesiumVRTutorial”)。

单击创建项目。 你的项目将被创建。

项目完全加载后,转到顶部菜单中的“编辑”>“插件”。 找到 Cesium for Unreal 并确保它已启用。 如果不是,请启用它,然后重新启动 Unreal。

在顶部菜单中,转到“文件”>“新关卡...”,然后在出现的窗口中选择“空关卡”。 新关卡即将开启。

打开新关卡后,转到顶部菜单中的文件 > 当前另存为...。 为关卡命名(例如“Main”)并将其保存在 Content 文件夹或子文件夹中。

在屏幕的左侧,应该会看到一个 Cesium 选项卡。 点击它。

在 Cesium 选项卡中,单击连接按钮。 网络浏览器将打开并显示登录你的 Cesium ion 帐户的说明。 按照说明进行操作,然后在出现提示时返回 Unreal。

Cesium 选项卡现在应该有一些新选项,包括一个标记为 Cesium World Terrain + Bing Maps Aerial Imagery 的选项。 单击该选项旁边的 + 按钮,将 Cesium World Terrain 添加到你的关卡中。

翻回 Place Actors 选项卡,该选项卡通常位于 Cesium 选项卡旁边。

在放置 Actors 选项卡中,选择 Lights。 将平行光拖到关卡中。 完成此步骤后,你应该能够在关卡预览窗口中看到渲染的 Cesium World Terrain。

如果希望包含一个天空盒,请从 Place Actors 面板添加一个 BP_Sky_Sphere。

此时,你应该能够使用 Unreal 的 VR 预览功能测试关卡。 我们将在下一节中添加一个自定义用户控制器,但现在,请按照设备的说明连接到你的计算机(请参阅上面的先决条件部分)。

将 VR 设备连接到计算机。 可能需要重新启动 Unreal 才能确认该设备。

在关卡预览窗口上方的工具栏中,找到播放按钮。 根据你的设置,它可能会显示在工具栏中。 如果存在,请单击“播放”按钮旁边的小箭头。 如果不存在,请单击工具栏右侧的双箭头(工具提示 = 单击以展开工具栏),然后选择活动播放模式。

选择 VR 预览。 戴上你的 VR 耳机,环顾关卡。 你还不能在关卡周围传送,但你应该会在你下方看到Cesium世界地形。

设置我们下一步需要的动作映射。 转到编辑 > 项目设置...。 将出现一个新窗口。 在新窗口的左侧栏中,转到“引擎”>“输入”。

创建两个动作映射——将它们命名为“LineTrace”和“Teleport”——并将它们分别绑定到 VR 设备右侧控制器上的不同按钮。 下面的示例使用 Oculus Touch 控制器,用于 Oculus Quest 2。

2、创建Pawn

接下来,我们将制作一个简单的用户控制器,允许你在 VR 中的关卡中移动。 控制器将“从头开始”制作(即使用内置的虚幻组件),而不是依赖于预制的 Pawn,这样你就可以了解从头到尾的过程——并学习如何进行更改以适合你的使用案例。

用户控制器将使用虚拟激光指示器在关卡中移动。 用户将能够使用正确的控制器指向地形并按下按钮。 只要他们按住那个按钮,他们就会在控制器指向的地方看到一个点。 然后,他们可以按第二个按钮传送到那个地方。

在下一步中,我们将设置 Pawn 以允许用户在关卡中移动。 如果你的项目使用 C++,请继续阅读步骤 。 如果希望使用蓝图,请跳至步骤 2b。

2a: C++


如果不想使用 C++ 编写代码并希望改用蓝图事件图,请转至下面的步骤 2b。

通过转到虚幻编辑器左上角的文件 > 新建 C++ 类…添加一个新的 C++ 类。

选择Pawn作为父类。

单击下一步按钮。 在接下来的页面上,将新类的名称设置为“VRPawn”。 单击绿色的创建类按钮。

Visual Studio 应该会自动打开文件。 如果没有,请转到“文件”>“打开 Visual Studio”,在 Visual Studio 中打开项目。

在 Visual Studio 中,使用右侧的解决方案资源管理器找到并打开以下文件:Source//.Build.cs。

在该文件中,你会看到一行以 PublicDependencyModuleNames.AddRange 开头并包含一个字符串数组。 将“HeadMountedDisplay”添加到数组,以告知 Unreal 我们要使用该模块。 该行应如下所示:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });

让我们修改我们的 Pawn 的头文件(VRPawn.h,它位于 Source/ 文件夹中)以包含我们需要进行激光笔传送的字段。 首先,在文件的顶部,会看到如下所示的文本:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "VRPawn.generated.h"

我们需要对其进行编辑,以便我们在代码中使用一些必要的组件(例如相机和运动控制器)。修改它,使其看起来像这样:

#pragma once

#include <Camera/CameraComponent.h>
#include <Components/SceneComponent.h>
#include <Components/StaticMeshComponent.h>
#include <CoreMinimal.h>
#include <GameFramework/Pawn.h>
#include <MotionControllerComponent.h>

#include "VRPawn.generated.h"

在 VRPawn.h 的更下方,你将看到一个标记为受保护的部分。 在该部分中,添加这些组件,它们将作为物理耳机和控制器的虚拟对应物、用于指示用户指向位置的光标以及用于指向和传送的一些参数:

  UMotionControllerComponent* LeftController;
  UMotionControllerComponent* RightController;
  UStaticMeshComponent* LineTraceCursor;
  float LineTraceLength = 1000000;
  float OffsetFromGround = 4000;

我们需要使用 UPROPERTY 宏标记这些字段,以便我们可以在蓝图编辑器中访问它们。 我们可以通过添加带有几个说明符的 UPROPERTY 宏来做到这一点。 添加宏,使最终结果如下所示:

  UPROPERTY(BlueprintReadWrite, EditAnywhere)
  UMotionControllerComponent* LeftController;

  UPROPERTY(BlueprintReadWrite, EditAnywhere)
  UMotionControllerComponent* RightController;

  UPROPERTY(BlueprintReadWrite, EditAnywhere)
  UStaticMeshComponent* LineTraceCursor;

  UPROPERTY(BlueprintReadWrite, EditAnywhere)
  float LineTraceLength = 1000000;

  UPROPERTY(BlueprintReadWrite, EditAnywhere)
  float OffsetFromGround = 4000;

在文件底部(右大括号之前)添加另一个标记为私有的部分,后跟一个冒号 (:)。 您在本节中声明的变量和函数将无法被其他类访问,如果其他类不需要看到它们,这是一个很好的做法。

声明这些函数,这将允许用户指向一个位置并传送到它:

private:
  void _startLineTracing();
  void _stopLineTracing();
  void _teleport();

在私有部分也添加以下字段。 尽管我们不需要在蓝图编辑器中修改 _root 或 _camera,但使用 UPROPERTY 宏标记它们仍然是一个好习惯,这样 Unreal 就可以正确跟踪它们。

  UPROPERTY()
  USceneComponent* _root;

  UPROPERTY()
  UCameraComponent* _camera;

  bool _isLineTracing;
  bool _isLineTraceHitting;
  FVector _lineTraceHitLocation;

现在,打开 VRPawn.cpp。 已经为我们添加了一个构造函数,但我们需要添加一些代码来初始化我们的组件。 将以下行添加到构造函数(以 AVRPawn::AVRPawn() 开头的函数):

  _root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
  _camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
  LeftController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("LeftController"));
  RightController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("RightController"));
  LineTraceCursor = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("LineTraceCursor"));

现在,当我们的 Pawn 被构建时,组件也将被构建。 我们还需要在转换层次结构中定义它们之间的关系。 在刚刚添加的代码下方,插入以下内容:

  RootComponent = _root;
  _camera->SetupAttachment(_root);
  LeftController->SetupAttachment(_root);
  RightController->SetupAttachment(_root);
  LineTraceCursor->SetupAttachment(_root);

此外,我们需要确保 UMotionController 组件跟踪 VR 设备物理控制器的位置和方向,并且它们在 VR 中可见。 将这些行添加到构造函数中:

  LeftController->MotionSource = FName("Left");
  RightController->MotionSource = FName("Right");
  LeftController->bDisplayDeviceModel = true;
  RightController->bDisplayDeviceModel = true;

最后,让我们在构建 Pawn 时隐藏线迹光标,这样它就不会挡路。 添加此行:

  LineTraceCursor->SetVisibility(false);

让我们为头文件中声明的两个新函数添加定义。 这些本质上只是跟踪我们映射到线跟踪操作的控制器按钮是否被按下。 松开按钮后,隐藏线迹光标(我们的激光笔的点)

void AVRPawn::_startLineTracing() { _isLineTracing = true; }

void AVRPawn::_stopLineTracing() {
  _isLineTracing = false;
  _isLineTraceHitting = false;
  LineTraceCursor->SetVisibility(false);
}

使用我们的激光指示器,我们需要指定每一帧发生的事情,这取决于指示器是否打开——以及它是否击中了任何东西。 我们在 Tick 函数中执行此操作。 下面的代码处理这个。

void AVRPawn::Tick(float deltaTime) {
  Super::Tick(deltaTime);

  // If the user is pressing the line trace button, do the line trace.
  if (_isLineTracing) {
    // Use the right controller to aim the line trace.
    FVector start = RightController->GetComponentLocation();
    FVector direction = RightController->GetForwardVector();
    FVector end = start + (direction * LineTraceLength);

    ECollisionChannel channel = ECollisionChannel::ECC_WorldStatic;
    FCollisionQueryParams params(FName(TEXT("")), true, this);
    FHitResult hit;

    if (GetWorld()->LineTraceSingleByChannel(OUT hit, start, end, channel, params)) {
      // If the line trace hit something, move the cursor to that spot.
      _isLineTraceHitting = true;
      _lineTraceHitLocation = hit.Location;
      LineTraceCursor->SetVisibility(true);
      LineTraceCursor->SetWorldLocation(_lineTraceHitLocation);
    } else {
      // If the line trace didn't hit anything, hide the cursor.
      _isLineTraceHitting = false;
      LineTraceCursor->SetVisibility(false);
    }
  }
}

现在,让我们添加处理传送的功能。 首先在 VRPawn.cpp 的底部添加一个空定义:

void AVRPawn::_teleport() {

}

如果满足以下条件,我们只希望用户能够传送:(a) 他们的激光指示器打开并且 (b) 激光指示器击中地形上的一个点。 下面的代码检查这两个条件,如果其中一个为假,函数将返回而不做任何事情。 将此代码添加到函数的顶部:

  if (!_isLineTracing || !_isLineTraceHitting) {
    return;
  }

如果我们通过了这两项测试,我们就可以将用户传送到 _lineTraceHitLocation——但需要注意一点。 我们不想将它们传送到激光笔击中地面的确切位置,因为那样它们的视线就会与地面平齐。 相反,让我们添加一个垂直偏移量,让用户离地面一定距离:

  FVector newLocation = _lineTraceHitLocation;
  newLocation.Z += OffsetFromGround;
  SetActorLocation(newLocation);

最后,我们需要修改 SetupPlayerInputComponent 函数,使其看起来像下面的代码。 这会将我们的函数连接到我们在本教程第一部分中映射的 LineTrace 和 Teleport 动作事件,这些事件将由 VR 设备的物理按钮触发。

void AVRPawn::SetupPlayerInputComponent(UInputComponent* playerInputComponent) {
  Super::SetupPlayerInputComponent(playerInputComponent);

  InputComponent->BindAction("LineTrace", IE_Pressed, this, &AVRPawn::_startLineTracing);
  InputComponent->BindAction("LineTrace", IE_Released, this, &AVRPawn::_stopLineTracing);
  InputComponent->BindAction("Teleport", IE_Pressed, this, &AVRPawn::_teleport);
}

单击“文件”>“全部保存”以保存我们刚刚修改的文件。

单击构建 > 构建解决方案。

返回虚幻编辑器。 如果您在关卡预览窗口中没有看到 Cesium World Terrain,请关闭并重新打开编辑器。

通过在屏幕底部的内容浏览器中单击添加/导入来添加新的蓝图类。 在创建基本资产下,单击蓝图类。

展开所有类并搜索“VRPawn”以找到你在 C++ 中创建的类。 单击它并按选择。

将你的蓝图命名为“BP VRPawn”。
双击 BP_VRPawn 在编辑器中将其打开。 如果看到文本 Open Full Blueprint Editor,请单击它。

你已经成功创建了 Pawn 的核心。 现在,继续第 3 步。

2b:蓝图

如果不想使用蓝图事件图而是使用 C++ 代码,请转至上面的步骤 2a。

通过在屏幕底部的内容浏览器中单击添加/导入来添加新的蓝图类。 在创建基本资产下,单击蓝图类。

在下一个窗口中,单击 Pawn。

将你的蓝图命名为“BP_VRPawn”。

双击 BP_VRPawn 在编辑器中打开它。

点击左上角的Add Component按钮,搜索Camera组件。 单击它以将其添加到你的 Pawn。 给它起个名字(例如,“相机”)。

使用添加组件按钮添加两个运动控制器组件。 给它们命名(例如,“LeftController”和“RightController”)。

使用添加组件按钮添加一个静态网格体组件,它将代表我们的激光笔的点。 给它起一个名字(例如,“LineTraceCursor”)。

确保添加的所有四个组件都是 DefaultSceneRoot 的直接子级。 如果它们中的任何一个被添加为彼此的子项,请单击并将它们拖到 DefaultSceneRoot 上,然后单击 Attach。

在Components窗口中选择LeftController,然后查看Editor右侧的Details选项卡。 向下滚动以找到可视化部分并选中显示设备模型旁边的框。 还要找到“运动控制器”部分并确保“运动源”值设置为“左”。

对 RightController 重复上述步骤。 对于这一个,将“运动源”值设置为“右”。

在左下角,找到变量旁边的 + 符号。 当你将光标悬停在它上面时,它会变成黄色,并且会出现 Variable 一词。 单击五次以添加五个新变量。

依次单击每个新变量,并在选中时查看右侧的 Details 面板。 如下图所示设置它们。 在设置每个默认值之前,你需要单击左上角的编译按钮。

通过单击窗口顶部附近的选项卡打开事件图。

右键单击事件图表中的任意位置并搜索我们在项目设置中创建的第一个动作事件——名为 LineTrace 的事件。 点击它。 将出现一个节点。

对我们创建的第二个动作事件执行相同的操作 - 名为 Teleport 的事件。

现在图形应该具有我们需要的三个事件节点:Event Tick、InputAction LineTrace 和 InputAction Teleport。

添加并连接节点(在事件图表中右键单击并搜索每个节点)以使从事件节拍节点流出的图表如下图所示。

当需要访问一个组件或变量(例如,LineTraceCursor)时,可以正常搜索它,也可以将它从左下角的变量部分拖到事件图中。 除非屏幕截图中的节点标记为 SET,否则选择“获取”选项。

该图检查用户是否正在激活他们的激光指示器。 如果是,它将点移动到他们指向的地形上的点。

使用 Break 节点访问 LineTraceByChannel 节点的 Out Hit 输出的 Location 值。

设置连接到 InputAction LineTrace 节点的图形,如下图所示。 该图跟踪激光笔是打开还是关闭,具体取决于用户何时按下和释放与其关联的按钮。

设置连接到 InputAction Teleport 节点的图形,如下图所示。 此图将用户移动到激光指示器的点 - 如果指示器打开并对准地面。

通过单击事件图旁边的选项卡打开构造脚本。

设置连接到构造脚本节点的图形,如下图所示。 此图会在构建 Pawn 时隐藏线迹光标,在需要时将其移开。

单击窗口左上角的保存和编译按钮。

3、配置线迹光标

无论是执行步骤 2a 还是步骤 2b,都请继续此处。

这些步骤将配置线迹光标的外观(即激光笔的点)。 本教程使用内置网格和材质,但你可以随意创建自己的资产或根据需要调整这些设置。

在编辑器中打开 BP_VRPawn 后,单击组件列表中的静态网格体组件(即 LineTraceCursor)。

选中静态网格体后,在右侧的详细信息面板中找到静态网格体部分。 单击下拉菜单,单击“查看选项”,然后确保选中“显示引擎内容”旁边的框。 如果不是,请检查它。

在“搜索资源”框中,搜索 1x1x1 球体。 有多个球体,因此请确保拥有正确的球体。

在 Details 面板中找到 Transform 部分,并将 Scale 设置为 (500.0, 500.0, 500.0)。

在 Details 面板中找到 Materials 部分,并将 Element 0 设置为 WidgetMaterial_Current。 (如果愿意,请随意使用自己的材料。)

4、整理和测试

在BP_VRPawn的Details面板中,搜索Auto Possess Player,设置为Player 0。

单击窗口左上角的保存和编译按钮。

关闭BP_VRPawn并返回关卡预览窗口。

将 BP_VRPawn 拖入关卡并将其移动到你选择的起始位置。

正如在本教程第一部分所做的那样,单击“播放”按钮旁边的小箭头或工具栏右侧的双箭头(工具提示 = 单击以展开工具栏),然后选择“活动播放模式”。

选择 VR 预览。 戴上你的 VR 头盔。 你应该能够环顾你的关卡并看到你的控制器。

如果按下绑定到 LineTrace 操作的按钮(右触发器,如果你完全按照这些步骤操作),应该能够使用右控制器指向地形并在您指向的位置看到一个球体。

按住 LineTrace 按钮,应该能够按下绑定到 Teleport 操作的按钮(如果你完全按照步骤操作,则为 A 按钮)移动到所选位置正上方的位置。


原文链接:Building a VR Application

BimAnt翻译整理,转载请标明出处