当前位置: 首页 > news >正文

Three.js WebXR沉浸式渲染简明教程

在前面文章中,我们了解了 VR 概念以及它们如何在 WebXR 中映射。 这使你可以考虑想要为用户提供的体验。 在本文中,我们将介绍如何将 WebXR 与 Three.JS 结合使用来创建针对大型异构用户群的沉浸式体验。

警告:WebXR API 仍在完善中(第一个公共工作草案刚刚发布),因此我将尽力更新本系列以反映更改并保持这些文章常青。 WebXR 设备 API 规范将出现新功能,如果它简化了代码,我将相应地更新本文。

在这里插入图片描述

用 3DConvert 在线工具快速转换3D模型的格式,无需本地安装。

1、Three.js 渲染管线快速概览

我不会花太多时间讨论 Three.JS 渲染管道的工作原理,因为它在互联网上有详细记录(例如此处)。 我将在下图中列出基础知识,以便更容易理解各个部分的去向。

在这里插入图片描述

2、WEBXR 设备 API 入门

在我们深入了解 WebXR API 本身之前,您应该知道 WebXR 设备 API Polyfill 可通过两种主要方式帮助开发人员:

如果设备/浏览器不支持 WebXR 设备 API,它将尝试使用陀螺仪和加速计等可用传感器对其进行填充,从而允许开发人员提供基本的纸板式体验或内联渲染。

如果浏览器支持旧版 WebVR API,它将在 WebVR 之上填充 WebXR 设备 API,从而允许开发人员首先利用为支持 WebVR 所做的所有工作(从而允许其利用其下方的 VR 运行时)。

用户可以进入的主要 3D 体验类型包括:

  • 基于台式计算机的键盘/鼠标,没有任何沉浸式支持。
  • 利用手机传感器的内联渲染或魔术窗口。 内联渲染是一种很好的方式来“逗弄”用户你的内容,向他们展示你的体验,希望这能让他们单击按钮并在 HMD 内进入更身临其境的体验。 这是一个例子:

在这里插入图片描述

  • 具有专用 VR 系统的沉浸式 VR、基于移动设备的 VR 或类似纸板的体验。

WebXR 设备 API 基于会话的概念。 例如,你请求会话,浏览器负责在 HMD 中启动渲染。 当你结束会话时,渲染会在 HMD 内停止,你可以像往常一样在页面上再次开始渲染。 会话有 3 种类型:沉浸式 VR、沉浸式 AR(本文未介绍)和内联。

这是一个非常简单的流程图,可以帮助你根据设备功能或 WebXR 设备 API 支持等因素决定提供哪种体验。

在这里插入图片描述

3、请求WebXR会话并呈现内容

本节介绍使用 WebXR 设备 API 请求会话和呈现内容所需步骤的高级流程。 我们将在某些步骤中详细介绍,主要关注渐进方面而不是渲染本身。

在本文中,我们将参考此处的一个简单演示。 它使用 WebXR 和 Three.JS,是本文中代码片段的基础。 完整来源位于此处。

4、检查 WEBXR 支持

你会发现不支持 WebXR 设备 API(即使使用 WebXR polyfill)的浏览器或设备并不罕见。 在这种情况下,你可以考虑基于键盘和鼠标的回退,而不是提供空页面,以便用户可以在体验中导航(类似于 3D 游戏)。

在 Three.JS 中,支持这一点相对简单。 你可以使用 PointerLockControls 类自动映射鼠标来移动相机(与 FPS 射击游戏的方式相同)。 使用指针锁定的好处是,当获取锁定时,它将发送鼠标移动的增量而不是它们在视口中的绝对位置。 另一个好处是,除非用户解锁指针(通常使用转义键,为您提供暂停体验的方法),否则光标无法离开浏览器窗口,并且光标将被隐藏。 这非常适合我们的需要。

this._controls = new THREE.PointerLockControls(this._camera);
this._scene.add(this._controls.getObject());

请注意,THREE.PointerLockControls 不会为你锁定指针。 通常,你希望与一个按钮进行交互来开始,该按钮会提醒用户将要发生某些事情。 这是执行此操作的一段简化代码:

document.body.addEventListener( 'click', _ => {// Ask the browser to lock the pointerdocument.body.requestPointerLock = document.body.requestPointerLock ||document.body.mozRequestPointerLock ||document.body.webkitRequestPointerLock;document.body.requestPointerLock();
}, false);
THREE.PointerLockControls 将在鼠标移动时负责更新相机。

最后要处理的部分是键盘移动,这非常简单:

document.addEventListener('keydown', event => {this._onKeyDown(event)}, false);
document.addEventListener('keyup', event => {this._onKeyUp(event)}, false);

在你的处理程序中,只需更新存储在某个变量中的相机的位置即可。 在演示中,我存储了预期的运动方向,因为我通过速度来平滑运动,这样用户看起来就不会跳跃位置。

完成更新渲染函数内的位置后,请更新 THREE.PointerLockControls:

let controls_yaw = this._controls.getObject();
controls_yaw.translateX(new_position.x);
controls_yaw.translateZ(new_position.z);

然后照常渲染场景并使用 requestAnimationFrame 再次循环:

this._renderer.render(this._scene, this._camera);
return requestAnimationFrame(this._update);

5、检查支持的WebXR会话模式

如果你的浏览器支持 WebXR(无论是本机还是通过 polyfill),需要查询 XR 会话支持的模式来决定后续步骤,例如,添加一个按钮以进入沉浸式模式。 下面是一个尝试确定是否支持沉浸式 VR 模式的示例:

navigator.xr.supportsSession('immersive-vr').then(() => {this._createPresentationButton();
}).catch((err) => {console.log("Immersive VR is not supported: " + err);
});

如果Promise得到解析,那么你可以在页面中添加一个按钮,通知用户他们可以使用他们拥有的 HMD 进入 VR 模式。 你还可以使用内联会话进行查询,看看是否可以在页面内渲染某些内联内容(也称为魔术窗口)。

6、调整以添加对 WEBXR 设备 API 的支持

如果将 Three.JS 渲染循环设置为在 2D 屏幕上进行常规渲染,则需要对其进行调整以支持使用 WebXR 进行渲染。 首先,以下是渲染如何与 WebXR 设备 API 一起工作的基本流程,无论是沉浸式会话还是内联会话。

在这里插入图片描述

让我扩展一下该图中的一些方框:

请求会话

navigator.xr.requestSession({mode: 'request-mode'})

mode参数可以是 immersive-vr、 immersive-ar 或 inline。 请记住,如果在你询问支持和请求会话之间 XR 设备不再可用,请妥善处理拒绝。

请求参考空间

当 requestSession Promise成功解决后,可以使用以下方式请求参考空间:

xrSession.requestReferenceSpace({ type:'type' })

本文前面讨论了各种可能的类型和子类型。 如果要指定子类型,请使用以下代码:

xrSession.requestReferenceSpace({ type:'type', subtype:'subtype' })

我建议你请求体验所需的功能最少的参考空间,因为它允许你支持更广泛的现有 XR 设备。

7、设置 XR 层

我不会在这里深入讨论细节,因为这里的 WebXR 设备 API 解释器对此进行了详细介绍。 然而,我将介绍正在进行内联渲染的具体示例,然后用户想要“升级”他们的体验以切换到更沉浸的模式。

现在,如果你想要使用内联模式然后切换到沉浸式模式,则需要两个画布:一个用于渲染沉浸式内容,另一个用于渲染内联内容。 每个画布都有自己的 webgl 上下文。 需要两个上下文的主要原因是,当你要求 GL 上下文与 XR 兼容(使用 makeXRCompatible)时,底层实现确保在连接 HMD 的 GPU 上创建上下文。 这与具有多个 GPU 的计算机非常相关,其中 HMD 可能连接到包含附加 GPU 的外壳。

注意:WebXR 设备 API 社区内部进行了一次讨论,以避免可能需要 2 个会话,从而更轻松地从一种体验切换到另一种体验。 这是正在讨论的问题。 还有一项工作正在进行中,以避免必须请求 2 个会话(一个内联,一个沉浸式),这将简化相当多的代码。

以下是使用 Three.JS 设置 XR 层的代码:

await this._renderer.context.makeXRCompatible();
this._xrSession.baseLayer = new XRWebGLLayer(this._xrSession, this._renderer.context);

8、调整渲染以支持沉浸式 VR 渲染

每当你使用 WebXR 设备 API 进行渲染时,渲染循环都需要更新。 首先也是最重要的,你不会在窗口上请求新的动画帧,而是在会话本身上请求新的动画帧。 原因是 XRSession 将根据 HMD 建议的刷新率以正确的频率调用你的代码。

在VR中,刷新率很可能与主屏幕的刷新率不同。 例如,一些一体式 HMD 的刷新率为 72 Hz,而 Oculus* Rift 或 HTC* Vive 等高端 VR HMD 的刷新率为 90 Hz,我们预计会看到 120 Hz 的 HMD 在不久的将来刷新率。 以更高刷新率渲染 VR 体验的主要原因之一是为用户提供流畅、更舒适的体验(这也有助于对抗晕动病)。

通常你会有这样的东西:

this._xrSession.requestAnimationFrame(this._render);

渲染函数如下所示:

function _render(timestamp, xrFrame);

xrFrame参数是携带当前XR设备渲染当前帧所需信息的对象。 在继续渲染之前,需要对 Three.JS 进行一些记录,以确保渲染可以与 WebXR 配合使用:

// Disable autoupdating because these values will be coming from the
// xrFrame data directly.
this._scene.matrixAutoUpdate = false;// Make sure not to clear the renderer automatically, because we will need
// to render it ourselves twice, once for each eye.
this._renderer.autoClear = false;// Clear the canvas manually.
this._renderer.clear();

下一步是最终进入 WebXR 特定的渲染位。 首先,你需要绑定层,这意味着你将告诉 Three.JS 渲染器渲染到 XRSession 层:

let xrLayer = this._xrSession.baseLayer;
this._renderer.setSize(xrLayer.framebufferWidth, xrLayer.framebufferHeight, false);
this._renderer.context.bindFramebuffer(this._renderer.context.FRAMEBUFFER, xrLayer.framebuffer);

然后你需要获取设备的姿态(姿态是旋转和位置,如果有的话):

let pose = xrFrame.getViewerPose(xrReferenceSpace);
if (!pose)return;

该姿态将为你提供渲染 XR 设备场景所需的所有信息。 然后你将需要渲染视图。 WebXR 设备 API 具有视图概念,通常映射到 VR HMD 内的屏幕数量。 通常,你将有 2 个视图(每只眼睛一个):

在这里插入图片描述

从那里你可以迭代视图并渲染每只眼睛:

for (let view of pose.views) {let viewport = xrSession.baseLayer.getViewport(view);this._renderEye(pose.viewMatrix, view.projectionMatrix, viewport);
}

渲染每只眼睛将如下所示:

_renderEye(viewMatrixArray, projectionMatrix, viewport) {// Set the left or right eye half.this._renderer.setViewport(viewport.x, viewport.y, viewport.width, viewport.height);let viewMatrix = new THREE.Matrix4();viewMatrix.fromArray(viewMatrixArray);// Update the scene and camera matrices.this._camera.projectionMatrix.fromArray(projectionMatrix);this._camera.matrixWorldInverse.copy(viewMatrix);this._scene.matrix.copy(viewMatrix);// Tell the scene to update (otherwise it will ignore the change of matrix).this._scene.updateMatrixWorld(true);this._renderer.render(this._scene, this._camera);// Ensure that left eye calcs aren't going to interfere.this._renderer.clearDepth();
}

“视图”方法的最佳部分是,你可以继续为内联会话重用相同的渲染代码,因为唯一的区别(除了矩阵值之外)是你只有一个要渲染的视图。


原文链接:Three.js WebXR渲染入门 — BimAnt

相关文章:

Three.js WebXR沉浸式渲染简明教程

在前面文章中,我们了解了 VR 概念以及它们如何在 WebXR 中映射。 这使你可以考虑想要为用户提供的体验。 在本文中,我们将介绍如何将 WebXR 与 Three.JS 结合使用来创建针对大型异构用户群的沉浸式体验。 警告:WebXR API 仍在完善中&#xf…...

flask使用cookie (设置cookie与查看cookie内容)

1.flask包cookie的使用 设置cookie app.route(/set_cookie) def set_cookie():resp make_response(Setting cookie)resp.set_cookie(username, John)return resp查看cookie: app.route(/get_cookie) def get_cookie():username request.cookies.get(username)return Welco…...

信息学奥赛一本通——1281:最长上升子序列

文章目录 题目【题目描述】【输入】【输出】【输入样例】【输出样例】 AC代码 题目 【题目描述】 一个数的序列 b i b_i bi​&#xff0c;当 b 1 < b 2 < . . . < b S b_1<b_2<...<b_S b1​<b2​<...<bS​的时候&#xff0c;我们称这个序列是上升…...

vue3+antv x6自定义节点样式

先大致定下节点样式&#xff0c;需要展示标题&#xff0c;输入/输出连接桩&#xff0c; 参考样子大概是 https://x6.antv.antgroup.com/examples/showcase/practices#class 这是根据antv x6配置 非自定义节点 图表案例 结果 数据格式大概是 nodes:[{title:鸟,id:node1,ports…...

Arcgis中直接通过sde更新sqlserver空间数据库失败

问题 背景 不知道有没有人经历过这样一个情况,我们直接在Arcgis中通过sde更新serserver数据库会失败,就是虽然在sde更新sqlserver数据库,但是在Navicat中通过sql语句来查询,发现数据并没有更新,如:上图中,更新数据库后,第一张图是sde打开的sqlserver数据库,它的数据库…...

使用gewe框架进行微信群组管理(一)

友情链接&#xff1a;geweapi.com 点击访问即可。 管理员操作 小提示&#xff1a; 添加、删除、转让多个wxid时仅限于添加/删除管理员&#xff0c;1添加 2删除 3转让 请求URL&#xff1a; http://域名地址/api/group/admin 请求方式&#xff1a; POST 请求头&#xff1a…...

【Linux】UDP协议——传输层

目录 传输层 再谈端口号 端口号范围划分 认识知名端口号 两个问题 netstat与iostat pidof UDP协议 UDP协议格式 UDP协议的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 传输层 在学习HTTP等应用层协议时&#xff0c;为了便于理解&#xff…...

【Linux进阶之路】进程(上)

文章目录 前言一、操作系统加载过程二、进程1.基本概念2.基本信息①运行并观察进程②创建子进程③僵尸与孤儿进程&#xff08;父子进程衍生出来的问题&#xff09;1. 僵尸进程&#xff08;Zombie状态&#xff09;2. 孤儿进程 3.基本状态①操作系统的状态&#xff08;统一&#…...

爬虫018_urllib库_cookie反爬_post请求百度翻译获取百分翻译内容_以及详细翻译内容---python工作笔记037

然后我们来看如何用urllib发送post请求,这里我们 用百度翻译为例 我们翻译一个spider,然后我们看请求,可以看到有很多 找到sug这个 可以看到这里的form data,就是post请求体中的内容 然后我们点击preview其实就是 返回的实际内容 然后请求方式用的post 然后我们把上面的信息…...

【Nginx】Nginx网站服务

国外主流还是使用apache&#xff1b;国内现在主流是nginx&#xff08;并发能力强&#xff0c;相对稳定&#xff09; nginx&#xff1a;高新能、轻量级的web服务软件 特点&#xff1a; 1.稳定性高&#xff08;没apache稳&#xff09;&#xff1b; 2.系统资源消耗比较低&#xf…...

go语言从0基础到安全项目开发实战

一.环境搭建并helloworld 搭建环境比较简单 1.1安装SDK 到以下链接下 Go下载 - Go语言中文网 - Golang中文社区 下载windows版本64位zip包 https://studygolang.com/dl/golang/go1.20.7.windows-amd64.zip 1.2配置环境变量 不配置的话就只能在bin目录下才能运行go命令 …...

Kubernetes Service 工作原理

本文介绍了 Kubernetes Service 的概念、原理和具体使用。 作者&#xff1a;沈亚军 爱可生研发团队成员&#xff0c;负责公司 DMP 产品的后端开发&#xff0c;爱好太广&#xff0c;三天三夜都说不完&#xff0c;低调低调… 本文来源&#xff1a;原创投稿 爱可生开源社区出品&am…...

面部表情识别4:C++实现表情识别(含源码,可实时检测)

面部表情识别4&#xff1a;C实现表情识别(含源码&#xff0c;可实时检测) 目录 面部表情识别4&#xff1a;C实现表情识别(含源码&#xff0c;可实时检测) 1.面部表情识别方法 2.人脸检测方法 3.面部表情识别模型(Python) &#xff08;1&#xff09; 面部表情识别模型的训练…...

提升Element UI分页查询用户体验与交互:实现修改未保存提示

我实现的功能是在 element ui 的分页组件中进行分页查询时&#xff0c;如果当前有未保存的修改数据就提示用户&#xff0c;用户可以选择是否放弃未保存的数据。确认放弃就重新查询数据&#xff1b;选择不放弃&#xff0c;不重新查询&#xff0c;并且显示条数选择框保持原样&…...

UML-时序图

目录 时序图 时序图构成: 对象: 消息: 生命线(激活): 活动条: 时序图举例: 时序图 时序图也叫顺序图、序列图. 时序图描述按照时间的先后顺序对象之间的动作过程&#xff0c;是由生命线和消息组成 时序图构成: 对象: 对象是类的实例&#xff0c;对象是通过类来创建的&…...

Seata - 入门笔记

1、事务 访问并可能更新数据库中数据库中各种数据线的一个程序执行单元 原子性&#xff1a;事务是一个不可分割的工作单位&#xff0c;一个事务要么都做要么都不做 一致性&#xff1a;必须是使数据库从一个一致性到另一个一致性的状态&#xff0c;中间状态不能被观察到 隔离…...

springboot使用aop排除某些方法,更新从另外一张表,从另外一张表批量插入

AOP 在Spring Boot中使用AOP时&#xff0c;如果想要排除某些方法不被切面所影响&#xff0c;可以通过使用切面表达式中的!within关键字来实现。以下是一个示例&#xff1a; Aspect Component public class MyAspect {Before("execution(* com.example.service.*.*(..)) …...

Go 语言面试题(二):实现原理

文章目录 Q1 init() 函数是什么时候执行的&#xff1f;Q2 Go 语言的局部变量分配在栈上还是堆上&#xff1f;Q3 2 个 interface 可以比较吗&#xff1f;Q4 两个 nil 可能不相等吗&#xff1f;Q5 简述 Go 语言GC(垃圾回收)的工作原理Q6 函数返回局部变量的指针是否安全&#xff…...

SAP MM学习笔记16-在库品目评价

在库品目评价是指评估物料。具体比如物料价格&#xff0c;数量&#xff0c;保管场所等发生变化的时候&#xff0c;判断是否发生了变化&#xff0c;要不要生成 FI票&#xff0c;用哪个FI科目来进行管理等内容就叫在库品目评价。 在库品目评价有很多层级&#xff0c;这里先讲3兄弟…...

Azure通过自动化账户实现对资源变更

Azure通过自动化账户实现对资源变更 创建一个自动化账户第一种方式 添加凭据&#xff08;有更改资源权限的账户&#xff0c;没有auth认证情况&#xff09;创建一个Runbook&#xff0c;测试修改 AnalysisServices 定价层设置定时任务&#xff1a;开始定时任务&#xff1a; 第二种…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...