用 Unity 和 HTC Vive 实现高级 VR 机制(1)

原文:Advanced VR Mechanics With Unity and the HTC Vive Part 1
作者:Eric Van de Kerckhove
译者:kmyhy

VR 从来没有这样时髦过,但是游戏不是那么好做的。为了提供真实的沉浸式体验,游戏内部机制和物理必须让人觉得非常、非常的真实,尤其当你在和游戏中的对象进行交互的时候。

在本教程的第一部分,你会学习如何创建一个可扩展的交互系统,并在系统中实现多种抓取虚拟物品的方式,并飞快地将它们扔出去。

学完本教程后,你可以拥有几个灵活的交互系统并可以用在你自己的 VR 项目中。

注意:本教程适合于高级读者,不会涉及如何添加组件、创建新的游戏对象脚本或者 C# 语法这样的东西。如果你需要提升自己的 Unity 技能,请先阅读我们的 getting started with Unity 和 introduction to Unity Scriptin,然后在阅读本文。

开始

在本教程中,你将必须具备下列条件:

  • 安装好 Unity 5.6.0f3(或以上)
  • 一套带手柄的、安装好、电源开启,准备就绪的 HTV View。

如果你之前没有用过 HTC Vive,你可以去看我们之前的 HTC Vive tutorial,以了解如何在 Unity 中使用 HTC Vive。HTC Vive 是目前最好的头戴式显示器之一,它所支持的 room-scale 功能提供了精彩的沉浸式体验。

下载开始项目,解压缩,用 Unity 打开项目文件夹。

在项目窗口中看一下目录结构:

分别介绍如下:

  • Materials: 场景中用到的材质。
  • Models: 本文用到的所有模型。
  • Prefabs: 目前只有一个预制件,用于关卡中随处可见的柱子。
  • Scenes:游戏画面和灯光数据。
  • Scripts: 有几个现成的脚本;你自己的脚本也会放到这里。
  • Sounds: 弓箭射出的声音。
  • SteamVR: 放置 SteamVR 插件及其相关脚本、预制件和示例。
  • Textures: 包含了几乎所有模型都共用的纹理(为了效率),以及 book 对象的纹理。

打开 Scenes 文件夹下的 Game 场景。

看一下 Game 视图,你会发现场景中缺少了相机:

在下一节,我们来解决这个问题,添加必要的东西,让 HTC Vive 能够工作。

场景设置

将 SteamVR\Prefabs 目录中将 [CameraRig] 和 [SteamVR] 预制件拖进结构视图。

摄像机现在应该是在地上,但要将它放在木塔上。将 [CameraRig] 的 position 修改为 (X:0, Y:3.35, Z:0) 。现在 Game 视图应该是这个样子:

保存场景,按 Play 按钮试一下是否顺利。四处逛逛,起码用一支手柄试试看能够看到游戏中的控制器。

如果手柄不工作,别担心!在写到此处的时候,最新版的 SteamVR 插件(版本 1.2.1)在 Unity 5.6 中有一个 bug,导致手柄的动作没有被注册。

要解决这个问题,选择 [CameraRig]/Camera (head) 下选择的 Camera (eye),然后为它添加一个 SteamVR_Update_Poses 组件:

这个脚本手动修改手柄的位置和角度。再次运行这个场景,问题解决了。

在编写任何脚本之前,看一下项目中的这几个 tag:

这几个 tag 允许我们更加容易判断哪种种对象发生碰撞或者触象。

交互系统:InteractionObject

交互系统允许场景中的玩家和物理用一种灵活的、模块化的方式进行交互。替代为每个对象和控制器编写重复的代码,你将编写几个类给其它脚本进行继承。

第一个脚本是 RWVR_InteractionObject 类;所有能够被交互的对象都应该从此类继承。这个基类中包含了几个基本的变量和方法。

注意:为了避免和 SteamVR 创建冲突或者便于搜索,本文中所有 VR 脚本都使用 RWVR 前缀。

新建文件夹 Scripts/RWVR。新建类 RWVR_InteractionObject。

打开这个脚本,删除 Start() 和 Update() 方法。

添加下列变量,就在类声明的下方:

protected Transform cachedTransform; // 1
[HideInInspector] // 2
public  RWVR_InteractionController currentController; // 3

你可能会看到报错 “RWVR_InteractionController couldn’t be found”。目前请忽略它,后面我们会创建这个类。

上面代码分别解释如下:

  1. 为了改善性能,将 tranform 值缓存。
  2. 这个属性使下面的变量在检视器窗口中不可见,哪怕它是public 的。
  3. 当前对象正在交互的手柄。后面我们会用到这个手柄。

保存脚本,回到编辑器。

在 RWVR 下面新建一个 C# 文件 RWVR_InteractionController。打开它,删除 Start() 和 Update() 方法,保存。

打开 RWVR_InteractionObject ,之前的错误消失。

注意:如果错误仍然存在,关闭代码编辑器,点一下 Unity,然后再次打开脚本。

在刚刚添加的变量后面新增 3 个方法:

public virtual void OnTriggerWasPressed(RWVR_InteractionController controller)
{currentController = controller; 
}public virtual void OnTriggerIsBeingPressed(RWVR_InteractionController controller)
{
}public virtual void OnTriggerWasReleased(RWVR_InteractionController controller)
{currentController = null;
}

这 3 个方法会在手柄的扳机按下、按住和放开时调用。当手柄被按下时,controller 被赋值,当它释放时,controller 被移除。

所有方法都是虚方法,它们将在更复杂的脚本中覆盖,以便它们能使用这些控制器回调方法。

在 OnTriggerWasReleased 方法后新增方法:

public virtual void Awake()
{cachedTransform = transform; // 1if (!gameObject.CompareTag("InteractionObject")) // 2{Debug.LogWarning("This InteractionObject does not have the correct tag, setting it now.", gameObject); // 3gameObject.tag = "InteractionObject"; // 4}
}

分别解释如下:

  1. 缓存 transform 以改善性能。
  2. 检查 InteractionObjet 是否有指定的 tag 值。如果没有,执行 if 后面的代码。
  3. 在检视器中输出一个警告,告诉开发者忘记设置 tag。
  4. 及时设置 tag,以便对象能够像我们期望的工作。

这个交互系统严重依赖于 InteractionObject 和控制器的 tag 来区分特殊对象和其它对象。忘记设置 tag 是很可能的,所以我们专门为这个编写了脚本。这是一种“失效保险”的设计。小心使得万年船。

最后,在 Awake() 方法后添加方法:

public bool IsFree() // 1
{return currentController == null;
}public virtual void OnDestroy() // 2
{if (currentController){OnTriggerWasReleased(currentController);}
}

这些方法分别负责:

  1. 一个公有的 Boolean 方法,表示当前对象是否正在被控制器所用。
  2. 当对象被销毁,将它从当前控制器(如果有的话)中释放。这有助于解决一些莫名其妙的问题。

爆粗脚本,打开 RWVR_InteractionController。

现在它还是空的。我们马上会充实它!

交互系统: Controller

控制器脚本是最重要的部分,因为它是玩家和游戏之间的直接联系。尽可能地接受输入并返回用户正确的反馈很重要。

首先,在类声明下面添加变量:

public Transform snapColliderOrigin; // 1
public GameObject ControllerModel; // 2[HideInInspector]
public Vector3 velocity; // 3
[HideInInspector]
public Vector3 angularVelocity; // 4private RWVR_InteractionObject objectBeingInteractedWith; // 5private SteamVR_TrackedObject trackedObj; // 6

分段解释如下:

  1. 保存对手柄尖端的引用。后面我们会添加一个透明的球,表示你能够到触摸的位置以及距离你可以够到的地方有多远:

  2. 手柄的可见对象。上图中白色的部分。

  3. 手柄的速度和方向。可以用于计算当你做抛掷时物体如何飞出。
  4. 手柄的角度,在抛掷时计算物体的移动也会用到它。
  5. 手柄当前正在交互的 InteractionObjecdt 对象。用它来向当前对象发送事件。
  6. 用于获得真实手柄的引用。

继续在下面添加:

private SteamVR_Controller.Device Controller // 1
{get { return SteamVR_Controller.Input((int)trackedObj.index); }
}public RWVR_InteractionObject InteractionObject // 2
{get { return objectBeingInteractedWith; }
}void Awake() // 3
{trackedObj = GetComponent<SteamVR_TrackedObject>();
}

代码解释如下:

  1. 这个变量通过 trackedObj 获得了一个对真实 SteamVR 手柄的引用。
  2. 返回和手柄进行交互的 InteractionObjecdt。对这个对象进行再次封装,是为了对其他类保持只读。
  3. 最后,保持一个和当前控制器相绑定的 TrackedObject 组件的引用,以便后面用到。

然后是这个方法:

private void CheckForInteractionObject()
{Collider[] overlappedColliders = Physics.OverlapSphere(snapColliderOrigin.position, snapColliderOrigin.lossyScale.x / 2f); // 1foreach (Collider overlappedCollider in overlappedColliders) // 2{if (overlappedCollider.CompareTag("InteractionObject") && overlappedCollider.GetComponent<RWVR_InteractionObject>().IsFree()) // 3{objectBeingInteractedWith = overlappedCollider.GetComponent<RWVR_InteractionObject>(); // 4objectBeingInteractedWith.OnTriggerWasPressed(this); // 5return; // 6}}
}

这个方法从控制器的碰撞体的某个范围内查找 InteractionObject。一旦找到一个,就将赋给 objectBeingInteractedWith。

代码解释如下:

  1. 创建一个碰撞体的数组,保存 OverlapSpherer() 方法找到的所有碰撞体,查找的位置和 scale 是 snapColliderOrigin,这是一个透明球体,如上图所示,我们后面会添加它。
  2. 遍历整个数组。
  3. 如果找到的碰撞体 tag 值等于 InteractionObject,同时它又是自由的,继续。
  4. 保存碰撞体的 RWVR_InteractionObject 在 objectBeingInteractedWidth。
  5. 调用 objectedBeingInteractedWith 的 OnTriggerWasPressed 方法,将当前控制器传递给它。
  6. 退出循环,完成查找。

新增方法,调用刚刚的这个方法:

void Update()
{if (Controller.GetHairTriggerDown()) // 1{CheckForInteractionObject();}if (Controller.GetHairTrigger()) // 2{if (objectBeingInteractedWith){objectBeingInteractedWith.OnTriggerIsBeingPressed(this);}}if (Controller.GetHairTriggerUp()) // 3{if (objectBeingInteractedWith){objectBeingInteractedWith.OnTriggerWasReleased(this);objectBeingInteractedWith = null;}}
}

代码非常简单:

  1. 当扳机被按下时,调用 CheckForInteractionObject() 方法,说明有可能发生了一次交互。
  2. 当扳机被按住时,同时有一个对象被抓住时,调用这个对象的 OnTriggerIsBeingPressed()。
  3. 当扳机被松开,同时有一个对象被抓住时,调用这个对象的 OnTriggerWasReleased() 方法,并停止交互。

这些检查确保玩家的所有输入都能被传递到正在和他们交互的 InteractionObject 对象。

添加两个方法,记录控制器的速度和角速度:

private void UpdateVelocity()
{velocity = Controller.velocity;angularVelocity = Controller.angularVelocity;
}void FixedUpdate()
{UpdateVelocity();
}

FixedUpdate() 以固定帧率调用 UpdateVelocity() ,后者更新 velocity 和 angularVelocity 变量。然后,你会将这两个值传递给一个刚体,以确保扔出去的东西能够更真实的移动。

有时候需要隐藏手柄,以确保体验更加浸入式,避免遮住视线。再添加两个方法:

public void HideControllerModel()
{ControllerModel.SetActive(false);
}public void ShowControllerModel()
{ControllerModel.SetActive(true);
}

这些方法简单地启用或禁用代表了控制器的 GameObject。

最后加入这两个方法:

public void Vibrate(ushort strength) // 1
{Controller.TriggerHapticPulse(strength);
}public void SwitchInteractionObjectTo(RWVR_InteractionObject interactionObject) // 2
{objectBeingInteractedWith = interactionObject; // 3objectBeingInteractedWith.OnTriggerWasPressed(this); // 4
}

代码解释如下:

  1. 这个方法造成了控制器中的压电线型驱动器(这个词不是我编造的)振动多次。它振动的时间越长,震动感就越强烈。它的强度是 1-3999。
  2. 这个方法将激活的 InteractionObject 换成参数指定的对象。
  3. 将指定的 InterationObject 变成激活状态。
  4. 在新的 InteractionObject 对象上调用 OnTriggerWasPressed() 方法,并传入当前控制器。

保存脚本,回到编辑器。为了让控制器按照我们的想法工作,还需要做一些调整。

在结构视图中选中两个控制器。它们都是[ CameraRig ]的子对象。

给它们各添加一个刚体。这允许它们使用固定连接,并和其它物体进行交互。

反选 Use Gravity,勾选 Is Kinematic。控制器不需要受物理的影响,因为在真实世界中,它们被你抓在手上。

将 RWVR_Interaction 控制器组件提交给两个手柄。我们待会要配置它。

展开 Controller(left),右键点击它,选择 3D Object > Sphere,为它添加一个球体。

选中球体,命名为 SnapOrigin,按 F 键让它在场景视图中居中。你会在地板中央看到一个巨大的白色半球体。

设置它的 Position 为 (X:0, Y:-0.045, Z:0.001) ,Scale 设为 (X:0.1, Y:0.1, Z:0.1)。这会将球放到控制器的前端。

删除 Sphere Collider 组件,因为物理检查通过代码进行。

最后,将它的 Mesh Renderer 修改为 Transparent 材质,让球体透明。

复制 SnapOrigin,将 SnapOrigin(1) 拖到 Controller(right)上,变成右手柄的子对象。命名为 SnapOrigin。

最后一步是创建控制器,使用它们的模型和 SnapOrigin。

选择并展开 Controller(left),将它的 SnapOrigin 子对象拖到 Snap Collider Origin 一栏中,将 Model 拖到 Controller Model 一栏。

在 Controller(right) 上重复同样的动作。

现在来放松一下!打开手柄电源,运行这个场景。

将手柄举到头盔前面,看看球体是否能够看见并和控制器粘在一起。

测试完后,保存场景,准备进入交互系统的使用!

用交互系统抓取物体

你可能看到附近有这些东西:

你只能看着它们,但无法把它们拿起来。你最好尽快解决这个问题,否则你怎么去读我们那本精彩的 Unity 教程呢?:]

为了和这些刚体进行交互,你需要创建一个新的 RWVR_InteractionObject 子类,用它来实现抓和扔的功能。

在 Scripts/RWVR 目录下创建新的 c# 脚本,名为 RWVR_SimpleGrab。

用代码编辑器打开它,删除里面的 Start() 和 Update() 方法。

将这一句:

public class RWVR_SimpleGrab : MonoBehaviour

修改为:

public class RWVR_SimpleGrab : RWVR_InteractionObject

这样这个类就继承了 RWVR_InteractionObject,后者提供了获得控制器输入的钩子,这样它就能对输入进行适当的处理。

在类声明下面声明几个变量:

public bool hideControllerModelOnGrab; // 1
private Rigidbody rb; // 2

很简单:

  1. 一个标志,用于表示控制器模型是否应该在该物体被拿起时隐藏。
  2. 为了性能和简单起见,缓存了刚体组件。

在变量声明之后添加方法:

public override void Awake()
{base.Awake(); // 1rb = GetComponent<Rigidbody>(); // 2
}
  1. 调用基类的 Awake() 方法。这会缓存对象的 Transform 组件并检查 InteractionObject 的 tag 是否赋值。
  2. 保存刚体组件,以便后面使用。

然后是一些助手方法,用于将对象用 FixedJoint 附着在手柄上,或者从手柄上放开。

在 Awake() 方法后面添加:

private void AddFixedJointToController(RWVR_InteractionController controller) // 1
{FixedJoint fx = controller.gameObject.AddComponent<FixedJoint>();fx.breakForce = 20000;fx.breakTorque = 20000;fx.connectedBody = rb;
}
private void RemoveFixedJointFromController(RWVR_InteractionController controller) // 2
{if (controller.gameObject.GetComponent<FixedJoint>()){FixedJoint fx = controller.gameObject.GetComponent<FixedJoint>();fx.connectedBody = null;Destroy(fx);}
}

这两个方法分别用于:

  1. 这个方法接收一个控制器作为参数,然后创建一个 FixedJoint 组件添加到手柄上,配置这个连接,使它不是那么容易掉,最后连接上当前的 InteractionObjecdt。在连接上添加一个力是为了防止用户将对象移过其他坚固的物体上,否则可能导致一些奇怪的物理问题。
  2. 将参数指定的控制器的 FixedJoint 组件(如果有的话)断开。所连接的对象将被删除,然后销毁 FixedJoint。

写完这些方法,我们可以实现来自于基类的几个 OnTrigger 方法,以处理用户输入。首先添加 OnTriggerWasPressed() 方法:

public override void OnTriggerWasPressed(RWVR_InteractionController controller) // 1
{base.OnTriggerWasPressed(controller); // 2if (hideControllerModelOnGrab) // 3{controller.HideControllerModel();}AddFixedJointToController(controller); // 4
}

这个方法在玩家按下扳机抓住一个对象时添加 FixedJoint 连接。代码分为几个阶段:

  1. 覆盖基类的 OnTriggerWasPressed() 方法。
  2. 如果 hideControllerModelOnGrab 标志为 true,隐藏控制器模型。
  3. 添加一个 FixedJoint 到控制器。

最后一步是添加 OnTriggerWasReleased() 方法:

public override void OnTriggerWasReleased(RWVR_InteractionController controller) // 1
{base.OnTriggerWasReleased(controller); //2if (hideControllerModelOnGrab) // 3{controller.ShowControllerModel();}rb.velocity = controller.velocity; // 4rb.angularVelocity = controller.angularVelocity;RemoveFixedJointFromController(controller); // 5
}

这个方法移除参数指定的控制器的 FixedJoint,将控制器的速度传递给刚体,以实现真实的抛掷效果。代码解释如下:

  1. 覆盖基类的 OnTriggerWasReleased() 方法。
  2. 调用基类方法解绑控制器。
  3. 如果 hideControllerModelOnGrab 标志为 true,再次显示控制器模型。
  4. 将控制器的速度和角速度传递给对象的刚体。这样当你放开对象时,对象会表现出真实的行为。例如,如果你扔出一个球,你会将手柄从后向前做一个抛物线动作。球应当获得旋转和向前的力,就像是在真实世界中你将动能传递给它一样。
  5. 删除 FixedJoint。

保存脚本,返回编辑器。

骰子和书在 Prefabs 文件夹中都有相应的预制件。在项目视图中打开这个文件夹:

选择 Book 和 Die 预制件,将 RWVR_Simple Grab 组件添加到二者。同时开启 Hide Controller Model。

保存场景运行游戏。尝试拿起几本书或骰子,扔到一边。

在下一节,我将介绍另一种抓取对象的方法:吸附。

拿起对象和吸附对象

在手柄所在的位置和角度拿起东西是可以的,但有时候将手柄吸附到物体的某个位置可能更有用。例如,如果用户看到一只枪,当他们拿起枪时会希望枪被指向右边。这就是 snapping (吸附)的意思。

为了吸附对象,你需要创建另外一个脚本。在 Scripts/RWVR 目录创建新的 C# 脚本,命名为 RWVR_SnapToController。用代码编辑器打开它,删除 Start() 和 Update() 方法。

将这句:

public class RWVR_SnapToController : MonoBehaviour

改成:

public class RWVR_SnapToController : RWVR_InteractionObject

这允许脚本具备所有 InteractionObject 的功能。

添加变量声明:

public bool hideControllerModel; // 1
public Vector3 snapPositionOffset; // 2
public Vector3 snapRotationOffset; // 3private Rigidbody rb; // 4
  1. 一个标志,表示手柄模型是否要在玩家抓住对象时隐藏。
  2. 当抓住对象时添加的位置。该对象默认会用这个位置吸附到手柄上。
  3. 同上,只是这个变量用于表示角度。
  4. 引用了对象的刚体组件。

然后增加方法:

public override void Awake()
{base.Awake();rb = GetComponent<Rigidbody>();
}

和 SimpleGrab 脚本一样,覆盖了基类的 Awake() 方法,然后保存刚体组件。

接下来是几个助手方法,这才算是这个脚本的肉戏。

添加如下方法:

private void ConnectToController(RWVR_InteractionController controller) // 1
{cachedTransform.SetParent(controller.transform); // 2cachedTransform.rotation = controller.transform.rotation; // 3cachedTransform.Rotate(snapRotationOffset);cachedTransform.position = controller.snapColliderOrigin.position; // 4cachedTransform.Translate(snapPositionOffset, Space.Self);rb.useGravity = false; // 5rb.isKinematic = true; // 6
}

这个方法和 SimpleGrab 脚本中的方法不同,它不使用 FixedJoint 连接,而是将它自己作为控制器的子对象。也就是说控制器和所吸附的对象是无法被外力所打断的。在这个教程中,这种方式会很稳定,但在你自己的项目中你更应该采取 FixedJoint 连接。

代码解释如下:

  1. 接收一个控制器参数,用于连接它。
  2. 将对象的 parent 设置为该控制器。
  3. 让对象的方向和控制器保持一定的偏移。
  4. 让对象的位置和控制器保持一定的偏移。
  5. 关闭重力,否则它会从你的手上掉落。
  6. 开启运动学特征。当附着到手柄上后,这个对象不会受福利引擎的影响。

现在来添加放开对象的方法:

private void ReleaseFromController(RWVR_InteractionController controller) // 1
{cachedTransform.SetParent(null); // 2rb.useGravity = true; // 3rb.isKinematic = false;rb.velocity = controller.velocity; // 4rb.angularVelocity = controller.angularVelocity;
}

这个方法简单地将对象从父对象中解除,重置刚体并应用控制器的速度。详细解释一下:

  1. 方法参数指定要松开对象的控制器。
  2. 将对象的父对象解开。
  3. 重新打开重力,并再次使对象再次变成非运动学的。
  4. 应用控制器的速度给对象。

覆盖如下方法以实现 snapping 操作:

public override void OnTriggerWasPressed(RWVR_InteractionController controller) // 1
{base.OnTriggerWasPressed(controller); // 2if (hideControllerModel) // 3{controller.HideControllerModel();}ConnectToController(controller); // 4
}

代码非常简单:

  1. 覆盖 OnTriggerWasPressed(),以添加吸附逻辑。
  2. 调用机类方法。
  3. 如果 hideControllerModel 标志为 true,隐藏控制器模型。
  4. 将对象连接到控制器。

然后是 release 方法:

public override void OnTriggerWasReleased(RWVR_InteractionController controller) // 1
{base.OnTriggerWasReleased(controller); // 2if (hideControllerModel) // 3{controller.ShowControllerModel();}ReleaseFromController(controller); // 4
}

同样十分简单:

  1. 覆盖 OnTriggerWasReleased() 方法。
  2. 调用基类的方法。
  3. 如果 hideControllerModel 标志为 true,重新显示手柄的模型。
  4. 将对象从控制器上放开。

保存脚本返回编辑器。从 Prefabs 目录中将 RealArrow 预制件拖到结构视图。

选择 arrow,设置它的 position 为 (X:0.5, Y:4.5, Z:-0.8)。它会悬浮在石板上方:

在结构视图中,将 RWVR_Snap To Controller 组件附加到箭支上,这样你就可以和它交互,同时将它的 Hide Controller Model 设为 true。最后点击检视器窗口上方的 Apply 按钮,将修改应用到该预制件。

对于这个对象,不需要修改 offset,默认它的握持部位就可以了。

保存并运行场景。抓住箭支,然后扔出去。唤醒你内心野兽吧!

注意,箭支握在手上的位置总是固定的,不管你如何拿起它。

本教程的内容就到此为止了,试玩一下游戏,感受一下交互中的变化。

结束

从此处下载最终项目。

在本教程中,你学习了如何创建可扩展的交互系统,你已经通过这个交互式系统找出了几种抓取物品的方法。

在第二部分的教程中,你将学习如何扩展这个系统,制作一套功能完备的弓和箭,以及一个功能完备的背包。

如果你想学习更多关于用 Unity 编写杀手游戏,请阅读我们的Unity Games By Tutorials。

在这本书中,你将创建 4 个完整的游戏:

  • 一个 twin-stick 射击游戏
  • 一个第一人称射击游戏
  • 一个塔防游戏(带 VR 支持!)
  • 一个 2D 平台游戏
    学完这本书后,你将能够编写自己的游戏运行在 Windows、macOS、iOS及更多平台。

本书完全针对 Unity 初学者,将他们的 Unity 技能升级到专家水准。本书假设你有一定的编程经验(任何语言)。

感谢你阅读本教程!如果有任何意见和建议,请留言!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/30170.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2020中国VR大会(虚拟现实中的真实行走漫游+WebVR+SLAM for AR+VR仿真+VR智能)

2020中国VR大会 一、虚拟现实中的真实行走漫游1、Problem&#xff08;1&#xff09; Immersive virtual reality 沉浸式虚拟现实&#xff08;2&#xff09;Potential solutions&#xff08;3&#xff09;Challenges 2、Existing methods walking&#xff08;1&#xff09;Redir…

HTC VIVE丨12. VRTK抓取机制——(VR交互机制2-Grab)

设置物体被抓取的位置及朝向 Grab Attach Mechanic Script&#xff1a;抓取机制的设置 Precision Grap&#xff1a;是否采用精确抓取。如果不勾选&#xff0c;抓取物体和手柄保持一致。选取后&#xff0c;抓取时会在碰撞点抓取&#xff0c;和下条配合使用 Right / Left Snao Ha…

移动端vr技术探索之VrPanoramaView

VR技术的热度每年都在增长&#xff0c;在购物、旅游等方面运用度很高。该项目引用了Google的vr:sdk-panowidget依赖库&#xff0c;通过VrPanoramaView&#xff0c;简单实现在手机上查看全景照片&#xff0c;下面是项目介绍。 google官网开发指南 支持模式&#xff1a;支持vr和…

VRTK杂谈(网络篇)(Yanlz+VIVE+Oculus+)

《VRTK杂谈》 版本 作者 参与者 完成日期 备注 VRTK_Overview_V01_1.0 严立钻 2018.09.09 ##《VRTK杂谈》发布说明&#xff1a; “VRTK杂谈”是对VRTK的一个探索&#xff0c;这是一个最初级探索&#xff1b; “VRTK杂谈”&#xff1a;定义在一…

VR技术分享交流

VR技术分享交流 虚拟现实(virtual reality,简称VR)是利用电脑模拟产生一个三维空间的虚拟世界&#xff0c;提供用户关于视觉等感官的模拟&#xff0c;让用户感觉仿佛身历其境&#xff0c;可以及时、没有限制地观察三维空间内的事物。用户进行位置移动时&#xff0c;电脑可以立…

苹果Meta都在冲的Pancake技术,中国VR团队YVR竟抢先交出产品答卷

萧箫 发自 凹非寺量子位 | 公众号 QbitAI 你听说过Pancake吗&#xff1f; 不是最新的蛋糕品类&#xff0c;而是时下VR行业最受关注和期待的光学技术&#xff0c;甚至被称为“划时代的方案”。 据了解&#xff0c;Pancake光学方案的应用&#xff0c;能够给VR带来画面清晰度和产品…

VR多人协同(Photon Server Pun2 VRIF)

VR多人协同&#xff08;多人游戏本地服务器&#xff09;&#xff08;Photon Server & Pun2 & VRIF) 内容介绍&#xff1a; 采用Photon Server作为本地服务器&#xff0c;然后使用Pun2连接本地服务进行VR多人交互 Photon Server部署 1、下载并解压&#xff1a; photon…

从《头号玩家》说起,聊聊当前的 VR 技术到底差在哪?

《头号玩家》口碑炸裂&#xff0c;给似乎已进入低谷期的 VR 产业带来了新一轮的热度。VR 技术因其充分的沉浸性、高真实性和高交互性等特点&#xff0c;在 2016 年曾受到广泛关注&#xff0c;并被寄予厚望。但由于一些老生常谈的问题&#xff0c;如成本过高、内容质量低、技术瓶…

unity 通过使用 photon networking Pun 实现 HTC Vive VR的多人联网。进阶版 《三》

啊哈&#xff0c;开始还是些闲话来引出思绪哈。之前以为没有人看我写的就停更了。今天偶然想起进来一看&#xff0c;发现回复私信希望可以继续讲的&#xff0c;很开心。瞬间就有了动力&#xff0c;哈哈。从我自己身上就看到了反馈的重要性&#xff0c;为何微博&#xff0c;微信…

unity 通过使用 photon networking Pun 实现 HTC Vive VR的多人联网

正文&#xff1a; 基于photon networking 来实现 VR 的多人联网。我之前文章将的是如何使用unity 自带的网络组件来实现VR多人联网。但是unet 他的问题是只能实现局域网联网。广域网的话貌似也可以&#xff0c;但是应该还是需要一个服务器人员。而且unet 比较蛋疼的一点是&…

Unity CEO:玩家不在乎AR还是VR,他们只想要优质内容

近期&#xff0c;Unity CEO John Riccitiello在接受英国金融时报采访时&#xff0c;透露了自己对于游戏的前生今世&#xff0c;以及AR/VR和未来的看法。他认为&#xff0c;打造一个新平台需要大量优质内容&#xff0c;创意对于新平台很重要&#xff0c;不管AR和VR技术有什么区别…

【话题讨论】-浅谈VR与AR

一、引言 随着ICT基础技术的发展&#xff0c;我们现在社会中的基础ict设施已经逐步完善&#xff0c;从而我们的社会也开始出现科幻片中才会有的场景&#xff0c;比如&#xff1a;我们可以构件一个虚拟3D沙盘。 还有我们熟悉的各类智能眼镜&#xff0c;已经进入到千家万户&…

助力 VR/AR 等复杂图像场景极致高清,火山引擎夺得 NTIRE 大赛双料冠军

动手点关注 干货不迷路 近日&#xff0c;CVPR Workshop 下属的 NTIRE2023大赛公布比赛结果&#xff0c;在双目超分双三次插值保真赛道和 360 全景图像超分赛道上&#xff0c;火山引擎多媒体实验室凭借自主研发的算法获得了双料冠军&#xff0c;技术能力达到行业领先水平。 NTIR…

GPT-4进行数据分析的成本不到人类分析师的1%;北京将新增算力建设项目;迈富时赴港上市丨每日大事件...

‍ ‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 企业动态 网易云音乐前CEO朱一闻进入AI教育领域创业 近日&#xff0c;据报道&#xff0c;网易云音乐前CEO朱一闻已在AI教育领域创业。朱一闻已在杭州完成团队搭建&#xff0c;核心成员包括网易云音乐早期创始员工、海康威…

施一公:我直到博士毕业,对研究也没兴趣,很迷茫,不知道将来干什么

来源 | 学术志 作者 | 施一公 我从来没有机会来北大做学术讲座&#xff0c;或是跟同学们在一块聊聊天&#xff0c;因此我非常珍惜这个机会。借这个机会我想把我的经历分享一下&#xff0c;推心置腹&#xff0c;毫无保留地分享。我之所以愿意把我的一些经历讲出来&#xff0c;是…

CSDN接入AIGC辅助创作,对此你怎么看?

catalogue &#x1f31f; 写在前面&#x1f31f; GitChat&#x1f31f; 百万粉丝计划&#x1f31f; CSDN接入AIGC&#xff1f;&#x1f31f; 写在最后 &#x1f31f; 写在前面 哈喽&#xff0c;大家好&#xff0c;我是几何心凉&#xff0c;这是一份全新的专栏&#xff0c;得到…

解决国外链接下载软件速度慢的方法

无论是下载vagrant&#xff0c;还是vscode&#xff0c;还是centos7&#xff0c;只要你进了官网&#xff0c;点击下载&#xff0c;打开浏览器的下载管理器&#xff0c;你就会看到这个东西&#xff08;这里以vscode为例&#xff09; 右键它&#xff0c;选择复制下载地址&#xf…

访问外国网站太慢

访问外国网站太慢&#xff0c;如trello\github 1.访问https://www.ipaddress.com. 2.搜索跳转&#xff0c;随便选择一个ip, Type要A 3.改电脑的hosts文件&#xff0c;将域名解析直接指向IP&#xff0c;绕过DNS步骤 hosts文件路径&#xff1a;** C:\Windows\System32\driver…

git克隆速度太慢的解决方法

https://blog.csdn.net/hzwwpgmwy/article/details/79043251 第一步&#xff1a; 直接把网址对应的IP放进hosts文件&#xff0c;这样就省去了DNS解析的时间&#xff0c; 步骤如下&#xff1a; 1、查找域名对应的ip地址&#xff0c;并修改hosts文件 nslookup github.global…

什么原因导致香港服务器变慢?解决办法

香港服务器慢有哪些解决方法?导致香港服务器慢的原因有很多&#xff0c;解决香港服务器慢也需要根据具体原因和选择正确的香港服务器配置线路来决定的&#xff0c;下面我们就看看影响香港服务器慢的原因有哪些以及解决方法。 香港服务器慢的原因有哪些? 导致香港服务器慢的原…