想象一下这样的场景:你打开一个网页链接,就像平时打开新闻网站一样,然后戴上VR头显(一种戴在眼睛上的显示设备,可以让你看到虚拟世界),瞬间就进入了一个360度的虚拟工坊。你不用下载任何APP,不用安装任何插件,只是在浏览器地址栏输入网址、点了一下“进入”而已。 这件事,让很多第一次体验的人觉得“太神奇了”。而创造这种神奇的,正是WebXR Device API——一个让浏览器直接“长出”VR和AR能力的技术标准。你可以把它理解成“网页的第三只眼睛和另一双手”:URL是网页的大门,HTML和CSS是装潢,JavaScript是神经,而WebXR是让网页借出头显设备的“透视眼”和“遥控手臂”的接口。 今天这篇文章,就要用“在浏览器里开一间虚拟手工坊”的故事,带你完整走一遍WebXR开发的全流程——如何点亮这片虚拟空间、如何让你的手柄变成手中真实的“工具”、如何让左右眼各自看到略有偏差的画面从而产生立体感、如何在设备五花八门的现实世界做到“一套代码到处跑”。
这门知识对你做前端和空间计算的职业价值在于:它把VR/AR开发从“只有Unity、Unreal大厂引擎才能做”的门槛,拉到了“会HTML、CSS、JavaScript就能做”的网页标准层面,让你拥抱下一代的Web端空间计算入口。
可视化演示
展示WebXR会话生命周期五阶段流程、双目立体渲染IPD视差原理,以及手柄输入射线追踪与手势关节骨骼追踪。
本节配有交互动效(进入视口后自动加载,约 3KB)。
XR会话生命周期 从请求会话到渲染循环的完整流程 参考空间跟踪头显位姿变化 requestSession(mode) -> RAF loop -> render(frame) WebXR会话经历初始化请求激活渲染结束五阶段
空间计算入口
还记得我们说的“虚拟手工坊”吗?在以前,你若是想在VR里做手工,得先下载一个巨大的VR应用软件,像一个需要装修整个厂房的大工程。而现在,WebXR让你只需要“打开一扇网页的门”,门后面就已经是一个能施展手艺的工坊。入口从“必须安装的APP”变成了“随开随用的网页”。
这个转变背后,是浏览器这个老熟人突然学会了说“设备语言”。WebXR Device API就是一本翻译词典,让网页JavaScript能直接听懂VR头显的“眼睛看到什么”、手柄的“手指在做什么”。它把五花八门的硬件差异藏在了背后,给开发者呈现出一套统一的标准接口——这就是硬件API的标准化。
从职业角度看,这意味着你写的WebXR应用,不管用户用的是PC VR还是手机盒子,只要浏览器支持,你的代码不用推倒重来。一网站多设备复用,省下的开发成本,就像同一份设计图纸可以同时用于木工坊和铁匠坊一样。
核心接口全景
走进虚拟手工坊之前,我们需要先搞清楚这个空间是怎样被人“打开”和“使用”的。你想进入工坊,得先“申请进场”——在WebXR里这叫启动一个会话(Session)。会话建立后,浏览器会把你和头显硬件之间的专用通道打开,之后你才能持续获得画面和姿态数据。
会话分两种:一种是inline,就像在电脑屏幕上通过窗口偷看工坊;另一种是immersive-vr,真正把整个人丢进工坊,头显全屏接管视野。启动会话时,你会用到一个叫navigator.xr.requestSession()的方法,它就像一个“领取入场券”的窗口,浏览器会询问用户是否允许进入XR环境。
进入工坊后,里面的场景不会自己动,需要你持续“看一眼、换一帧”。WebXR用requestAnimationFrame()的XR变体来驱动这个循环——每一帧,浏览器都会问你的代码:“头显现在看到什么?”你答完,画面就更新一次。空间参考系(Reference Space)则是判断“你站在哪儿”的坐标系,比如local参考系是以你为中心的原点,local-floor参考系则把地面当作水平基准。
会话结束时要记得“退场”,调用session.end()释放设备资源。就像离开工坊后要把工具归位、关灯锁门——不做这一步,设备和浏览器会一直占着资源,后面的体验可能因此变卡顿。
循环往复,你的虚拟手工坊在这套“会话-帧-参考系”的骨架中运转起来。只要门票在手(会话有效),快门在跳(帧循环),坐标系稳定(参考系),这个工坊就永远为你敞开。
- 会话启动浏览器向用户申请进入XR模式的权限,就像工坊门口的门卫问你“要进入虚拟空间吗?”
- 帧驱动每一帧渲染前,XR系统会触发回调,让你更新场景再交给屏幕显示。
- 参考系XR系统用参考系来确定虚拟坐标原点,
local-floor让地面与你所在的真实地面重合。 - 帧对象XRFrame包含当前帧的所有设备状态——头显位置、手柄姿态、眼球追踪等,所有渲染信息都从它里面取。
姿态数据流
在虚拟手工坊里,你做木工时每一刀的方向都取决于你头的位置和转动的角度。你的头就是一架“摄像机”,而WebXR要做的第一件事,就是时刻告诉你这架摄像机在哪里、面朝哪里——这就是姿态(Pose)。
每次拿到XRFrame后,你会调用frame.getViewerPose(referenceSpace),它会返回一个XRViewerPose对象,里面装着视角的位置(position)和旋转(orientation)。位置是一个三维向量 (x, y, z),告诉你头部在空间中的坐标;旋转用四元数(quaternion)表示,包含四个数字 (x, y, z, w),比欧拉角更稳定,能避免万向锁问题——就像用一个陀螺仪而不是三个分别转动的圆环来记录你脑袋的朝向。
更具体地说,XRViewerPose里通常有多个view(视图),每一个view对应一只眼睛。每个view里都有一对矩阵:projectionMatrix(投影矩阵)和viewMatrix(视图矩阵)。投影矩阵模拟的是“镜头焦距”——决定了你能看到多宽的视野;视图矩阵则模拟“镜头位置”——决定了你从哪个角度看向世界。两者合起来,才最终得出你眼前的画面。
数据流就像你用眼睛扫视工坊的每一个角落,WebXR把这个“扫视”过程数字化:头部每移动一毫米,四元数和位置向量更新一次;每一帧渲染,矩阵乘法把整个世界重新投影一次。你的虚拟手工坊,本质上是你的头部运动“导演”的一场实时舞台剧。
- 获取姿态每一帧调用
getViewerPose获取头显当前的位置和朝向,这是渲染的基础数据。 - 位置向量位置向量描述头部在三维空间中的平移,单位是米。
- 四元数旋转四元数用四个分量描述旋转,避免欧拉角的万向锁。
- 矩阵乘法顶点变换顺序是模型矩阵→视图矩阵→投影矩阵,顺序不能颠倒。
手柄交互
工坊里最趁手的是你的双手,而在虚拟空间里,取而代之的是手柄。WebXR通过XRInputSource对象来描述每一个输入设备,你可以从frame.session.inputSources中拿到当前连接的所有手柄。每个手柄都有自己独立的姿态,就像你双手可以各拿各的工具一样。
手柄上最重要的交互结构是gamepad对象,它提取了真实手柄的按键和摇杆数据。当你扣动扳机时,gamepad.buttons[0].value会从0平滑过渡到1,就像扣动胶枪的扳机越用力出胶越多。触控板则映射为二维轴数据,像操纵一个微型的摇杆来调整工具的倾斜角度。
现代WebXR应用中最常用的交互模式是“选择射线”(Select Ray)。你会从手柄的尖端发射一条虚拟射线,射中场景中的某个物体后,再用扳机键“扣下”——这就完成了一次点击。射线检测(raycasting)会计算射线与物体表面的交点,决定哪个物体被选中。
手柄交互的本质,是把你的物理手部动作翻译成虚拟世界的“手势语义”。扳机是“抓取”,触控板是“移动”,射线是“瞄准”。你在真实工坊里的本能——伸手、拿取、放下——被这套数据流忠实地搬进了浏览器,工坊的木屑和铁锈也因此有了真实的触感。
- 输入源枚举从会话中获取所有连接的手柄设备。
- 手柄姿态每个输入源有自己的位置和朝向,与头显的姿态数据格式一致。
- 射线发射通过手柄姿态计算射线的原点与方向,起点是手柄尖端的虚拟位置。
- 选中事件用户扣下扳机触发
select事件,浏览器会告诉你射线命中了哪个物体。
立体渲染
走进虚拟手工坊,最打动你的是那种“深度感”——你能判断木料距离你多远、锤子在桌面还是空中。这种深度感从哪来?来自双眼视差:你的左右眼看到的世界有细微差别,大脑把这两幅图像合在一起,就产生了立体感。WebXR的立体渲染,就是在每一帧渲染两幅略微不同的画面,分别送给左右眼。
在代码层面,开发者在渲染循环中会遍历viewerPose.views数组,通常view[0]是左眼、view[1]是右眼。每个view都带有自己的projectionMatrix和viewMatrix——其中viewMatrix包含一个细微的水平偏移,正是这个偏移模拟了你两眼之间的距离(瞳距)。渲染器分别用左右眼的矩阵各绘制一遍整个场景,最后把两幅图输出到屏幕的左右半屏,由头显的光学镜片把两幅画面分别送到你的两只眼睛里。
这套流程的目标是保证两幅画面的差异足够真实:近处的物体左右眼的位移差更大,远处的物体位移差更小——这就是深度感知的原理。WebXR会自动为你设置好这些矩阵,你只需要循环遍历views数组,逐一渲染即可。
这种立体渲染不是简单的“复制两次画面”,而是真正模拟人类视觉系统的工作机制。你的工坊之所以看起来有纵深和质感,正是因为WebXR忠实地为你的两只眼睛送上了“不一样的风景”,让大脑在大脑皮层里自动重建了空间的深度。
- 视图数组viewerPose.views包含左右眼各自的视角数据,通常长度为2。
- 投影矩阵左右眼的投影矩阵几乎相同,模拟的是镜头的透视效果。
- 视图矩阵之差左右眼的viewMatrix在x方向上有微小偏移,这个偏移就是瞳距。
- 视差公式视差大小与物体距摄像机的距离成反比,构成了深度感知的基础。
设备兼容性
到访虚拟手工坊的客人形形色色:有人用高端PC VR头显,有人用便宜的手机盒子,还有人只是想先在手机上用AR模式瞥一眼工坊外观。作为工坊主人,你当然不能因为客人设备不同就拒绝服务,而要“量体裁衣”——这就是WebXR设备兼容性的意义。
WebXR的兼容策略首先从能力检测开始。启动应用时,你会调用navigator.xr.isSessionSupported('immersive-vr')来检测当前浏览器是否支持VR会话,返回一个布尔值。如果支持,就进入沉浸式VR模式;如果不支持,你可以查出是否支持inline模式,用普通的网页3D视图(类似一个浏览器内的3D展窗)作为降级方案。
其次,会话模式可以灵活切换。同一个应用可以在检测到高配设备时自动请求immersive-vr模式,在低配设备上退化为inline模式。更进一步,还有immersive-ar模式——让虚拟的工坊家具叠加到真实房间里。你不需要为每种模式写三套代码,只需要判断哪种模式可用,并为不同模式准备对应的渲染路径。
兼容性的核心哲学是“渐进增强”——从最基本的滚动网页到完整的沉浸式体验,每一层都是对前一个层级的增强。即使你的客人只有一部普通手机,工坊的大门也永远为他打开,只是看到的风景各有不同。
- 能力检测查询当前浏览器和硬件是否支持指定XR模式。
- 请求会话如果支持目标模式,就请求创建该模式的会话。
- 降级路径如果不支持,则回退到inline模式,用常规3D场景替代。
- 渲染适配不同模式使用不同的渲染路径,但共享大部分场景构建代码。
AI 应用场景
VR学习工坊
阶段:数据采集与交互反馈
解决的问题:传统在线教育只能提供平面视频,学员无法“上手”操作。WebXR让浏览器直接成为VR教学平台,无需安装VR应用即可体验沉浸式教学,同时配合AI自动评估学员操作轨迹。
具体案例:一家职业培训公司开发了一个WebXR汽车维修虚拟车间,学员用手机浏览器打开链接、插入手机盒子,就能看到一台虚拟发动机。学员用手柄(或手机屏幕触摸模拟)逐颗拧下螺丝,系统记录每次操作的三维坐标和旋转数据——这些轨迹数据被AI模型分析,自动判断操作顺序是否正确、旋拧力度是否到位,并给出实时反馈。所有环节都在浏览器里完成,学员不需要购置昂贵的PC VR设备。
家具AR预览
阶段:空间感知与场景融合
解决的问题:家具网站上买家看不到真实尺寸和摆放效果,下单后常有落差。WebXR的AR模式让虚拟家具直接“走”进真实房间,配合AI对房间的语义理解,让虚拟物体更贴合真实空间。
具体案例:一个家具电商平台在商品详情页加入“AR试摆”按钮,用户点击后用手机摄像头对准房间,系统自动识别墙壁和地面,虚拟沙发按真实尺寸叠加到画面中。用户可以绕着沙发走动,从不同角度观察颜色和材质是否适合客厅。由于是Web页面直接实现,用户不需要下载App,分享给家人也只需发送一个链接。
Python 演示
import math class Vector3: def __init__(self, x=0.0, y=0.0, z=0.0): self.x = x self.y = y self.z = z def __add__(self, other): return Vector3(self.x + other.x, self.y + other.y, self.z + other.z) def __mul__(self, scalar): return Vector3(self.x * scalar, self.y * scalar, self.z * scalar) def __repr__(self): return "Vector3({:.2f}, {:.2f}, {:.2f})".format(self.x, self.y, self.z) class Quaternion: def __init__(self, x=0.0, y=0.0, z=0.0, w=1.0): self.x = x self.y = y self.z = z self.w = w def rotate(self, v): # 简化的四元数旋转:只绕Y轴旋转示例 cos_a = self.w sin_a = self.z x = v.x * cos_a - v.z * sin_a z = v.x * sin_a + v.z * cos_a return Vector3(x, v.y, z) class XRView: def __init__(self, eye_offset, fov_y=math.radians(70)): self.eye_offset = eye_offset self.projection = self._make_projection(fov_y) def _make_projection(self, fov_y): # 简化的透视投影矩阵(列主序存储简化为系数) f = 1.0 / math.tan(fov_y / 2.0) return [f, 0.0, 0.0, 0.0] def view_matrix(self, head_pos, head_rot): eye_pos = head_rot.rotate(Vector3(self.eye_offset, 0.0, 0.0)) + head_pos return eye_pos class XRFrame: def __init__(self, time, head_pos, head_rot): self.time = time self.head_pos = head_pos self.head_rot = head_rot self.views = [ XRView(eye_offset=-0.032), XRView(eye_offset=0.032) ] def get_viewer_pose(self): return [v.view_matrix(self.head_pos, self.head_rot) for v in self.views] class XRSimulator: def __init__(self): self.time = 0.0 self.frame_count = 0 def request_animation_frame(self, callback): # 模拟每帧16ms的渲染循环 while self.frame_count < 60: t = self.frame_count * 0.016 head_pos = Vector3(0.0, 1.6, math.sin(t) * 0.2) head_rot = Quaternion(w=math.cos(t * 0.5), z=math.sin(t * 0.5)) frame = XRFrame(t, head_pos, head_rot) pose = frame.get_viewer_pose() callback(pose, t) self.frame_count += 1 def render_frame(pose, time): left_eye = pose[0] right_eye = pose[1] # 模拟左右眼视差计算深度估计 parallax = right_eye.x - left_eye.x depth_estimate = 0.5 / abs(parallax) if parallax != 0 else float("inf") print("frame={:.3f}s 左眼={} 右眼={} 视差={:.4f} 深度≈{:.2f}m".format( time, left_eye, right_eye, parallax, depth_estimate)) if __name__ == "__main__": sim = XRSimulator() sim.request_animation_frame(render_frame)
class XRFrame:模拟WebXR的XRFrame对象,承载每一帧的头部姿态和双眼视图。def get_viewer_pose(self):对应WebXR的frame.getViewerPose()方法,返回左右眼的位置。XRView(eye_offset=-0.032)左右眼的水平偏移量,模拟人眼瞳距约6.4厘米。parallax = right_eye.x - left_eye.x计算左右眼视图的水平视差,这是深度感知的核心数据。depth_estimate = 0.5 / abs(parallax)用视差的倒数估算深度,与文档中的视差—深度反比公式一一对应。head_rot = Quaternion(...)用四元数表示头部旋转,比欧拉角更稳定。while self.frame_count < 60:模拟渲染循环,用近似16ms帧间隔驱动一帧帧更新。
常见误区
误区我刚接触WebXR时以为,既然它是做虚拟现实的,那肯定只能在Oculus、HTC Vive这样昂贵的高端设备上跑,普通的手机浏览器肯定不支持。
正解WebXR的设计目标恰恰是跨设备的统一标准。它检测设备的能力,自动提供inline(网页内嵌3D视图)、immersive-vr(沉浸式VR)和immersive-ar(增强现实)三种模式。手机上的浏览器可以用Cardboard这种低成本手机盒子体验沉浸式VR,也可以用AR模式把虚拟物体叠加到摄像头画面中。开发者要做的是能力检测和降级设计,而不是放弃低端设备用户。
为何常见:大家对“VR“的认知普遍停留在”高端头显专属”的标签上,很容易忽略WebXR作为Web标准天然具备的跨平台特性。
误区很多人以为WebXR的双目渲染就是复制一份代码,把左右眼的画面画成完全一样就行,顶多位置稍微偏移一点。
正解立体渲染的关键不是简单复制,而是用两个不同的视图矩阵分别计算场景投影。左右眼的viewMatrix不仅位置有瞳距偏移,它们的投影矩阵虽然近似但可能因为头显光学的畸变而有所差异。更重要的是,场景中的每一个物体都要重新按双眼视角分别绘制一遍,包含遮挡关系、光影变化,这样才能让大脑产生真实的深度感。写代码时,如果只调用一次渲染再平移画面,看到的世界会像纸片一样毫无立体感。
为何常见:在普通2D开发中,“一帧一个画面”的心智模型根深蒂固,突然切换到“一帧两个画面”让很多前端开发者下意识地走了捷径。
误区有些初学者认为手柄姿态数据是绝对坐标,直接把设备返回的数值作为虚拟手的位置,结果虚拟手在空间中“飘”到奇怪的地方。
正解手柄姿态数据是相对于某个参考系(reference space)而言的。getPose()返回的姿态必须和场景的参考系保持一致,否则坐标系不匹配会导致位置错乱。通常做法是把手柄的姿态数据从XRInputSource中取出后,用getPose(inputSource.targetRaySpace, referenceSpace)来获取相对于当前参考系的射线数据,再映射到虚拟手的模型上。坐标系就像地图上的经纬度参照——同一个坐标在不同地图上位置完全不同。
为何常见:开发者习惯了对普通DOM元素的绝对定位,到了XR的3D空间中仍习惯“拿到数值就填进去”,缺乏对参考系和坐标变换的敏感度。
误区第一次听说“浏览器里跑VR”,我的第一反应是“那一定得装个插件吧,就像以前的网页3D需要装Unity插件那样”。
正解WebXR是浏览器原生支持的标准API,不需要安装任何插件。它由W3C制定标准,各大浏览器厂商直接集成在浏览器内核里。开发者只需调用navigator.xr上暴露的API,就能直接使用头显和手柄硬件。这正是WebXR与它的前身WebVR的重要区别之一——插件时代已经结束,标准时代已经到来。
为何常见:早期Web VR技术确实依赖插件或外部运行时,这个历史包袱让很多人对“网页跑VR”产生了固有的不信任感,从而在认知上形成了过时的刻板印象。
面试题
Q1. 要在一个不支持VR的浏览器上展示3D内容,WebXR最适合的降级方案是?
- A.推荐用户下载专用VR浏览器
- B.使用 inline 模式展示3D场景← 正确答案
- C.跳转到一个原生应用页面
- D.放弃展示并提示浏览器过旧
答案:B。WebXR的inline会话模式可以在普通浏览器页面中渲染3D内容,是最合理的降级路径。A需要用户额外安装软件,违背WebXR“免安装”的设计初衷;C跳转原生应用完全绕开了Web技术;D则直接放弃了用户体验。降级要做到人还在、场景还在,而不是把用户拒之门外。
Q2. WebXR会话可以使用的模式包括以下哪些?
- A.inline← 正确答案
- B.immersive-vr← 正确答案
- C.immersive-desktop
- D.immersive-ar← 正确答案
答案:ABD。WebXR规范定义了三种会话模式:inline(网页内嵌3D)、immersive-vr(沉浸式VR)和immersive-ar(增强现实)。不存在"immersive-desktop"这种模式。开发者需要根据设备能力检测这三种模式的可用性,渐进增强地提供不同的体验层次。
Q3. XRViewerPose中包含的views数组通常有几只眼睛的视图?
- A.1个
- B.2个← 正确答案
- C.4个
- D.取决于连接的显示器数量
答案:B。XRViewerPose中的views数组通常包含两个视图,分别对应左眼和右眼。每个view都有自己的projectionMatrix和viewMatrix,用于立体渲染。答案是2个。A是2D场景的视图数,C是四目相机才有的配置,D与头显设备无关。
Q4. 在渲染循环中,WebXR要求开发者如何处理左右眼的视图?
- A.只渲染左眼,右眼复用左眼画面
- B.分别使用每只眼睛的viewMatrix和projectionMatrix渲染场景← 正确答案
- C.渲染一次后水平镜像复制到另一只眼
- D.交替渲染,每帧只渲染一只眼睛
答案:B。WebXR的双目渲染要求遍历viewerPose.views数组,分别用每个view的投影矩阵和视图矩阵渲染完整场景。A和C无法产生正确的视差,所见画面没有立体感;D交替渲染会导致眼睛看到的时间错位,产生严重不适。正确做法是每帧用两套矩阵各渲染一次场景。
Q5. 关于四元数在姿态表示中的优势,正确的说法是?
- A.比欧拉角更节省存储空间
- B.可以避免万向锁问题← 正确答案
- C.更适合做旋转插值和平滑← 正确答案
- D.直接代表眼球注视方向
答案:BC。四元数用4个分量表达旋转,欧拉角用3个角度,A中“更节省存储”是错的。四元数最大的优势是避免了欧拉角的万向锁问题,同时因为数学性质适合插值,可以平滑过渡旋转(B、C对)。D错误,眼球注视方向通常另有专用API,与四元数本身无直接关系。
Q6. 手柄选择射线(Select Ray)命中检测的计算起点是?
- A.场景中心原点
- B.手柄尖端(targetRaySpace)← 正确答案
- C.头显中心位置
- D.地面参考系的零点
答案:B。选择射线通常从手柄的targetRaySpace发出的,这是一个专门用于表示“手柄指向方向”的参考坐标系。射线的原点是手柄尖端的虚拟位置,方向由手柄的朝向决定。A、C、D都不是手柄射线交互的正确起点。
Q7. WebXR中启动沉浸式VR会话的对象是?
- A.window.xr.requestVR()
- B.navigator.xr.requestSession()← 正确答案
- C.document.body.requestFullscreen()
- D.navigator.getVRDisplays()
答案:B。navigator.xr.requestSession('immersive-vr')是WebXR启动VR会话的标准方式。A和D是旧的WebVR API的写法,已废弃。C是普通网页全屏API,与VR会话无关。WebXR统一用requestSession()方法,配合session mode参数来区分不同模式。
Q8. 做WebXR设备兼容性处理时,下列哪些做法是正确的?
- A.调用isSessionSupported检测设备能力← 正确答案
- B.对不同设备写一套完全相同的渲染代码
- C.不支持VR时回退到inline模式← 正确答案
- D.对低端设备降低模型细节和绘制分辨率← 正确答案
答案:ACD。A是能力检测的标准方式;C是渐进增强的降级策略;D是性能适配的常见手段。B错误——“完全相同的渲染代码”忽略了高端头显和低端手机的渲染分辨率、帧率需求差异。良好的兼容性设计是在共享场景逻辑的前提下,为不同能力层配置不同的渲染参数。
Q9. XRFrame对象主要提供什么数据?
- A.设备当前帧的姿态和输入状态← 正确答案
- B.网页中DOM元素的样式
- C.网络请求的响应数据
- D.用户的浏览器历史记录
答案:A。XRFrame是每一帧开始时由WebXR系统生成的“瞬时快照”,它包含了设备在那一帧的姿态信息、输入源(手柄)状态以及所有视图数据。A正确。其他选项都与XRFrame无关。开发者通过在requestAnimationFrame回调中获取XRFrame,然后从中提取渲染所需的数据。
Q10. 为什么left和right两个view的viewMatrix会有细微差异?
- A.因为两只眼睛的投影矩阵完全不同
- B.因为要模拟人眼瞳距产生的视差← 正确答案
- C.因为浏览器渲染引擎有bug需要手动修正
- D.因为左右眼的分辨率不同
答案:B。左右眼viewMatrix的差异来自模拟人眼的瞳距(约6.4厘米)。左右眼的位置在x轴上有微小偏移,导致看到的同一个物体在视网膜上的投影位置有差异,形成双目视差。这个视差让大脑能够估算深度,从而产生立体感。A错误——投影矩阵几乎相同;C和D毫无根据。