假如你是一位“数字包工头”,接到一单委托:把一间空荡荡的毛坯房,装修成客户戴上 VR 头显就能走进去参观的“虚拟样板间”。Three.js 就是你的建材超市——场景、相机、渲染器、灯光、模型,通通从这儿选购;WebXR 则是客户进门的那道“虚拟安检门”,负责把头显和浏览器安全地接起来。 装修过程分七步走:先搭房间骨架(场景、相机、渲染器)→ 装好安检门(WebXR 会话)→ 调出立体感(双眼视差)→ 给客户配手柄(交互)→ 控制家具数量(性能优化)→ 加氛围彩蛋(沉浸体验)→ 最后竣工验收、交钥匙(发布调试)。每一步都有对应的 Three.js 代码。读完这篇,你就能亲手搭出一间“在浏览器里就能逛的 VR 房子”,全程不用安装任何专用 VR 开发包——这正是 Web 端 VR 最迷人的地方。
对 AI 从业者的价值:空间计算时代的交互界面正在从屏幕走向场景,掌握 Three.js + WebXR 能让 3D 内容直接抵达 VR 头显,是构建 AI 可视化产品、虚拟展厅和沉浸式训练系统的基础技能。
可视化演示
动态演示Three.js实战的核心机制,通过源头/管道/节点/终端四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Three.js实战:WebX WebXR设备接入与兼容性 立体渲染与VR相机配置 核心机制可视化 Three.js实战整体架构与关键组件
核心架构
装修毛坯房,第一步不是急着买沙发,而是先分清房间里三个谁也离不开的角色:房间本身、看房人的眼睛、把画面画下来的画师。Three.js 里恰好也有三个一一对应的对象——Scene(场景容器)、Camera(相机)、Renderer(渲染器)。
Scene 就是装下所有家具(3D 物体)的客厅,往里 add() 模型、灯光、辅助网格;Camera 决定“从哪个角度、看多远”——VR 里一般用 PerspectiveCamera(透视相机),视野角度、近裁剪面、远裁剪面都要设好;Renderer 则是那个每帧都被逼着重画一次的画师,它把场景里的一切用 WebGL 画到屏幕上。
三者配合的节奏叫“渲染循环”。Three.js 的 renderer.setAnimationLoop() 会以显示器的刷新频率反复执行“清空→更新→绘制”,VR 里通常按头显的 90Hz 或 120Hz 来跑。搭好这三件套,样板房的骨架就立起来了。
房间、眼睛、画师就位,毛坯房有了雏形。接下来,得让客户真正“进得来”——这就轮到 WebXR 安检门登场了。
- W 是画布的像素宽度相当于样板房窗户的横向尺寸,比如 1920。
- H 是画布的像素高度相当于窗户的纵向尺寸,比如 1080。
- aspect 是宽高比宽度除以高度得到约 1.78,相机用它修正透视,画面才不被拉扁或拉长。
WebXR接入
装修过半,客户到了门口。先别急着开门,门口得有一道安检闸机:确认客户戴的是 VR 头显、浏览器支持虚拟现实、并且客户本人点头授权。这扇“闸机”就是 WebXR API。
技术步骤是:先调用 navigator.xr.isSessionSupported('immersive-vr') 探测设备是否支持沉浸式 VR;支持的话,在用户点击按钮的“手势”里调用 navigator.xr.requestSession('immersive-vr') 申请会话——浏览器规定必须由用户主动触发,就像闸机必须由客户自己刷脸。拿到会话后交给 Three.js:renderer.xr.enabled = true; renderer.xr.setSession(session),渲染器就会接管循环,把画面送进头显的两块屏幕里。
桌面端还可以渲染一份“镜像画面”到普通浏览器窗口,方便围观和调试——相当于样板房门口放一台闭路电视,没戴头盔的人也能实时看到房内景象。这套“能力检测→请求会话→镜像呈现”的流程,就是 Web 端 VR 设备接入的标准动作。
客户刷脸成功、闸机放行,人已经站在样板房里了。但等等——他怎么看到的是立体的?这就要讲双眼视差的魔法。
- FPS 是每秒帧数VR 体验流畅度的核心指标,90fps 表示画师一秒钟重画 90 次。
- Δt 是单帧耗时当前帧与下一帧之间隔了多少秒,刷新率越高这个值越小。
- 两者互为倒数知道了单帧耗时,就能推出帧率;做性能分析时二者经常互相换算。
立体渲染
很多人以为 VR 的“立体感”来自屏幕有多高清。其实真正的秘密在 3D 电影里:左右眼看到的画面略有不同,大脑把两幅图一叠,就脑补出了远近。样板房里所有物品,都是靠这个“视差”骗过大脑的。
技术上,每只眼睛看到的画面,其实是相机位置往左/右平移了一点点(瞳距 IPD,成人约 6-7 厘米)之后各自拍出来的。Three.js 的 WebXR 渲染在底层就是这么干的:渲染器内部用左右两个视角各渲染一遍场景(立体相机),再把两张图画到头显的两块屏上。物体离你越近,它在左右眼画面里的横向错位越大——这个错位就叫视差(disparity)。
反过来说,VR 系统、AI 视觉算法,都能从视差反推深度:视差大 = 近,视差小 = 远。这篇“立体渲染原理”,是后面做手柄交互、做深度估计的共同地基。
门里的世界一下子“鼓”出来了。客户傻站着可不行,得让他伸手摸一摸——下一节给客户配一支“激光教鞭”。
- d 是双眼视差同一物体在左右眼画面中的横向位移差,单位与 b、f 保持一致。
- b 是双眼间距(瞳距)左右眼之间的水平距离,人眼大约 0.064 米。
- f 是相机焦距决定成像大小,焦距越长,同样深度下视差越大。
- z 是物体深度物体到相机的直线距离,z 越大视差越小,表示物体越远。
手柄交互
参观样板房光看不摸没意思。给客户手里塞一支“激光教鞭”:按下扳机,教鞭射出一道激光,指到哪,哪就被选中;松开,东西就被抓起放下。这套“指哪儿打哪儿”的交互,在 Three.js 里叫手柄控制器 + 射线检测。
技术上,renderer.xr.getController(0) 拿到左手柄、getController(1) 拿到右手柄,它们的 3D 位置和朝向会跟随真实手柄实时同步。手柄上每个按键都是事件:selectstart(按下主键)、selectend(松开)、squeezestart(握紧)。最关键的是“射线检测”——Three.js 的 Raycaster 会从手柄出发发射一条无限长的射线,和场景里的每个物体做相交测试,第一个被撞到的物体就是目标。
拿到目标后,常见做法是把目标“吸附”到手柄上,模拟抓取;再配合物体被选中时的高亮色,手感就有了。这套机制和 AI 自动驾驶里“发射射线探测前方障碍物”用的是同一套几何学。
客户能开灯、能拿起桌上的笔,玩得不亦乐乎。可是东西一多,样板房开始掉帧——装修的下半场,得给房间“减负”了。
- O 是射线起点即手柄在三维空间中的位置,相当于激光笔的笔尖。
- D 是射线方向手柄朝向的单位方向向量,长度为 1。
- t 是距离参数t≥0 时,P(t) 是从起点出发、沿方向走了 t 距离的那个点。
- P(t) 是射线上任意点手柄朝哪里指,哪一刻撞到物体,就由这个参数方程决定。
性能优化
样板房里塞满了几千件家具,客户走两步就撞一下,画师也累得手抖——画面开始“卡顿”。VR 里卡顿不是小事,直接让人头晕恶心。所以装修后半程的主题只有一个:给画师减负,保住 90fps。
掉帧的头号原因是“绘制调用”(draw call)太多——每件家具都要单独喊画师画一次,几千件就是几千次。对策是合并:把相同材质的静态物体合并成一个网格(mergeGeometries),或用 InstancedMesh 一次画几千个相同物体;贴图用 KTX2 压缩格式减小显存压力;远处物体用 LOD(多级细节)换成低模版本,比如远处的挂画用一张贴图代替立体模型。
关于光照,能用烘焙贴图把光影“印”进纹理,就绝不放一堆实时灯——移动端 VR 的 GPU 撑不住。优化做得好不好,最终看的是帧时间:一帧的总耗时不能超过预算。
房间瞬间顺畅了。接下来不是加家具,而是加“氛围”——让客户觉得这不是一间房,而是一个世界。
- T_CPU 是逻辑计算耗时场景更新、动画、射线检测等 CPU 花费的毫秒数。
- T_GPU 是绘制耗时网格提交、像素填充等 GPU 花费的毫秒数。
- 1/90 是单帧预算90Hz 头显每秒要 90 帧,一帧只有大约 11.1 毫秒可用。
- 两者之和不能超预算超过 11.1ms 就掉帧,所以 CPU、GPU 都要“抢时间”。
沉浸交互
样板房要想让人“不想摘头盔”,得靠氛围布置:地上铺一张“传送毯”,客户踩一下就能瞬移到下一个区域;背景播放舒缓的音乐;你盯着哪盏灯,它就自动亮起。这套氛围设计在 VR 里叫沉浸交互。
最常用的是传送移动(Teleport):手柄瞄准地面生成一条抛物线,松开按键后角色瞬移到落点——比摇杆移动更不晕。空间锚点(Anchor)能让虚拟家具稳定地“钉”在现实世界的某个位置,走动后回来它还在。环境音效用位置音频(PositionalAudio),声音大小随你离声源远近变化。视线反馈则检测相机正前方碰撞到的物体,实现“盯着看 2 秒就触发”的注视交互。
所有瞬移、镜头转场,都不建议硬切,而是做插值平滑——从一个位置“飞”到另一个位置,给大脑一点过渡时间,晕动症会轻很多。这个插值思想在 AI 动画生成、路径平滑里也随处可见。
客户在样板房里瞬移、听音乐、看灯亮起,直呼不想走。装修基本完工,剩下最后一步:竣工验收、交钥匙。
- P0 是起点位置瞬移前角色的坐标,比如客厅中央。
- P1 是终点位置传送垫指向的目标坐标,比如阳台门口。
- t 是插值进度从 0 到 1 逐渐增大,t=0 表示刚出发,t=1 表示到达。
- P(t) 是当前帧位置每帧取一个新的 t 计算位置,角色就平滑地“飞”过去。
发布调试
房子装好了,不能直接把钥匙扔给客户——先做竣工验收:检查水电(浏览器安全要求)、实测每一间房(真机测试)、出体检报告(性能数据),最后把钥匙(链接)郑重交到客户手里。
第一道硬门槛是安全上下文:WebXR 必须在 HTTPS 下运行,因为要调摄像头、传感器等敏感能力。开发时可以用自签证书给局域网 IP 加 HTTPS;上线则配置正式证书。第二道是真机调试:把手机/头显的 USB 调试打开,电脑浏览器里通过远程调试面板能看到头显里渲染的实时画面和 console 日志,就像隔着玻璃看客户在房里走。第三道是性能体检:用 WebXR 的帧时间统计记录每帧毫秒数,对照 11.1ms 预算找瓶颈。
另外要记住:立体渲染意味着左右眼各画一遍,像素工作量天然翻倍,所以渲染分辨率、像素率都要心里有数。全部检查通过后,把页面部署到静态服务器,生成一个链接发给客户——客户戴上头显打开链接就能参观,一单 WebVR 项目就此交付。
钥匙交付,客户戴着头显在样板房里流连忘返。你用 Three.js + WebXR 亲手搭建了第一间“浏览器里的 VR 房子”——全程没装任何专用 VR 开发包。
- W 是单眼渲染宽度头显单眼屏幕的像素宽,比如 1832。
- H 是单眼渲染高度对应像素高,比如 1920。
- 2 是左右眼渲染次数立体渲染要让左右眼各看一遍,像素工作量翻倍。
- N 是总像素量约 703 万像素,这就是 GPU 每帧要填满的“画布面积”。
AI 应用场景
虚拟看房展厅
阶段:空间计算与三维可视化
解决的问题:传统样板间成本高、覆盖范围小,客户必须到现场才能看房;用 Three.js + WebXR 把户型模型搬到网页里,客户戴上 VR 头显打开链接即可身临其境,无需安装任何 App。
具体案例:某地产商把待售户型导出为 GLB 模型部署到云服务器,客户戴 PICO 4 打开网址,用手柄瞬移、开灯、看窗外景色,平均停留 12 分钟,留资率比图文页提升 3 倍。
工业装配培训
阶段:人机交互与模拟训练
解决的问题:真实设备培训成本高、有安全风险,学员操作失误可能损坏机器;VR 里可以无限次拆装、零成本试错。
具体案例:车企用 Three.js 搭建发动机虚拟拆装车间,学员用手柄射线拾取零件、旋转观察、按顺序装配;系统记录装配错误率并给出提示,新人培训周期从两周缩短到四天。
线上沉浸式艺术馆
阶段:沉浸式内容呈现
解决的问题:线下展馆受地域和排期限制,珍贵展品不便外借;VR 艺术馆用视线反馈和位置音频复刻现场观展体验。
具体案例:某博物馆把 30 件展品放进 VR 展厅,参观者“盯住”展品 2 秒,耳边自动响起导览语音(位置音频随距离变化),还可瞬移到不同展厅,上线首月访问量破十万。
Python 演示
# -*- coding: utf-8 -*-"""用双眼视差公式演示 VR 立体渲染的深度感知。""" IPD = 64 # 双眼间距(虚拟瞳距)FOCAL = 50 # 相机焦距 def disparity(depth): """视差:距离越近,左右眼看到的横向偏移越大。""" return IPD * FOCAL / depth def depth_from_disparity(d): """由视差反推深度,模拟大脑或视觉算法的深度感知。""" return IPD * FOCAL / d def main(): scene = [ ("near vase", 200), ("mid chair", 500), ("far frame", 1200), ] print("== stereo camera simulation ==") for name, z in scene: d = disparity(z) z_est = depth_from_disparity(d) print("%s: depth %d -> disparity %.2fpx -> depth %.0f" % (name, z, d, z_est)) if __name__ == "__main__": main()
def disparity(depth):定义视差计算函数,输入物体距离相机的深度。return IPD * FOCAL / depth实现公式 d = b×f / z:深度越大,视差越小。def depth_from_disparity(d):从视差反推深度,模拟大脑或视觉算法的深度感知。d = disparity(z)对场景里每个物体算一次双眼视差。z_est = depth_from_disparity(d)用反推的深度与原深度对比,验证公式自洽。
常见误区
误区很多初学者以为只有装 Unity、Unreal 加头显厂商 SDK 才能做 VR,浏览器做不出真正的沉浸式体验。
正解WebXR 是浏览器原生的 VR 标准,Three.js 这类 WebGL 库直接调用它就能驱动头显。就像样板房不需要从国外进口整套装修队,本地建材超市(浏览器)本来就配有 VR 通道。
为何常见:传统 VR 开发教材以桌面引擎为主,“必须装大软件”的印象深入人心。
误区以为在场景里多放几盏实时灯、多开阴影,渲染效果就越接近真实世界。
正解移动端 VR 的 GPU 算力有限,实时阴影和多光源是掉帧重灾区。正确做法是烘焙光照贴图,把光影“印”进纹理,运行时不再计算。
为何常见:桌面端显卡性能强,跑得动实时灯光,容易让人误以为移动端也扛得住。
误区做普通网页习惯了 30fps 也能用,以为 VR 场景只要不崩溃、能看到东西就算成功。
正解VR 里 90fps 是底线,掉帧会直接引发晕动症,用户几秒钟就摘头盔。每帧 11.1ms 的预算是硬约束,必须用帧时间工具持续监控。
为何常见:桌面浏览器卡顿只是“体验差”,而 VR 卡顿是“生理难受”,两者的严重程度完全不同。
误区以为把网页上的 click 事件换成手柄按钮,让用户“点物体”就是手柄交互了。
正解手柄在三维空间里没有鼠标指针,必须用手柄发射射线做相交检测,还要区分 select(点选)和 squeeze(握抓)等多通道输入,抓取时要让物体跟随手柄运动。
为何常见:没在头显里实操过,很容易把二维交互习惯带到三维里。
误区开发普通网页习惯在 `http://localhost` 直接跑,以为 WebXR 也这样访问就行。
正解WebXR 会调用传感器等敏感能力,浏览器规定它必须运行在安全上下文(HTTPS)中,否则设备检测、会话申请都会失败。局域网调试也要用自签证书配 HTTPS。
为何常见:普通 Web 开发很少关心安全上下文,遇到 WebXR 报错时不容易往这个方向想。
面试题
Q1. Three.js 中负责把 3D 场景最终画到屏幕上的核心对象是?
- A.场景对象Scene
- B.相机对象Camera
- C.渲染器Renderer← 正确答案
- D.灯光对象Light
答案:C。Scene 是装物体的房间,Camera 决定看哪里,真正把画面画出来的是 Renderer(渲染器)。就像样板房里摆家具、定视角的是别人,最终把画面呈现在屏幕上的画师是渲染器。
Q2. 调用 WebXR 创建沉浸式会话之前,浏览器通常要求什么?
- A.用户手势确认授权← 正确答案
- B.安装显卡驱动程序
- C.强制重启浏览器
- D.连接外置服务器
答案:A。WebXR 属于敏感能力,浏览器规定必须由用户主动点击等手势触发 requestSession 调用,相当于安检闸机必须由客户自己刷脸,不能由网页偷偷放行。
Q3. 人眼感知物体远近的最核心视觉线索是?
- A.左右眼画面的视差← 正确答案
- B.显示器刷新频率
- C.背景音乐的音量
- D.网页加载的速度
答案:A。左右眼看到略有不同的画面,大脑通过视差大小判断远近——视差大近、视差小远。刷新率、音量、加载速度都不是人眼判断深度的方法。
Q4. 手柄指向并选中三维场景中的物体,最常用的方法是?
- A.发射射线检测物体← 正确答案
- B.遍历全部网页元素
- C.比较像素颜色值
- D.读取系统剪贴板
答案:A。Three.js 用 Raycaster 从手柄发射射线,与场景物体做相交测试,命中第一个物体就是选中目标。其余选项都是二维或无关手段。
Q5. 下列哪些做法能有效降低 VR 场景的渲染负担?
- A.合并静态的网格← 正确答案
- B.烘焙光照贴图← 正确答案
- C.开启实时阴影
- D.按距离切换LOD← 正确答案
答案:ABD。合并网格减少绘制调用、烘焙光照避免运行时算光、LOD 远处换低模都是典型优化手段;实时阴影开销大,在移动端 VR 应尽量避免。
Q6. 90Hz 头显的单帧预算约 11.1ms,若 CPU 逻辑耗时 6ms,GPU 绘制最多可用多少?
- A.约5.1毫秒← 正确答案
- B.约11.1毫秒
- C.约6.0毫秒
- D.约17.1毫秒
答案:A。帧预算是一帧的总时间蛋糕,CPU 和 GPU 要平分这块蛋糕:11.1 − 6 = 5.1ms。超过就会掉帧,所以做性能优化时常把 CPU 和 GPU 耗时分开量。
Q7. 关于 WebXR 会话渲染,下列说法正确的是?
- A.左右眼各渲染一遍← 正确答案
- B.桌面画面自动消失
- C.需要安全上下文HTTPS← 正确答案
- D.必须安装专用插件
答案:AC。立体渲染要让左右眼各看一遍,像素工作量翻倍;WebXR 必须在 HTTPS 安全上下文下运行。桌面端仍可有镜像画面,也不需要安装任何插件。
Q8. VR 场景中“指哪儿走哪儿”的瞬移移动方式通常称为?
- A.传送移动Teleport← 正确答案
- B.页面滚动条拖拽
- C.双击屏幕放大
- D.浏览器地址跳转
答案:A。Teleport 传送是 VR 中最不晕的移动方式:手柄瞄准地面、松开按键瞬移到落点。其他选项都是二维网页交互。
Q9. 下列符合 Three.js VR 性能优化最佳实践的是?
- A.大量物体用实例化渲染← 正确答案
- B.每帧动态新建材质
- C.场景使用烘焙光照← 正确答案
- D.所有物体开实时阴影
答案:AC。InstancedMesh 实例化能一次绘制大量相同物体,烘焙光照把光影预计算进贴图,都是 VR 友好的做法;每帧新建材质会造成频繁分配,实时阴影在移动端开销过大。
Q10. 瞳距 b=0.064m、焦距 f=0.05m,某物体在距相机 2m 处,其双眼视差约为多少?
- A.约1.6毫米← 正确答案
- B.约0.5毫米
- C.约3.2毫米
- D.约6.4毫米
答案:A。代入视差公式 d = b×f / z = 0.064×0.05 / 2 = 0.0016m = 1.6mm。这个量级说明:双眼视差其实非常微小,但人脑对它的敏感度极高,几毫米的差异就能判断出远近。