WWDC Quick Look 💓 By SwiftGGTeam
What’s new for the spatial web

What’s new for the spatial web

观看原视频

Highlight

visionOS 26 的 Safari 默认开启 HTML <model> 元素,可在网页内联立体渲染 USDZ 文件,并新增 Website Environments 让网站指定沉浸式背景。


核心内容

过去十几年,Web 上展示 3D 模型只能靠 Three.js、Babylon.js 这样的 JS 库,在 2D canvas 里画几何体。用户看到的依然是平面图像,缺少深度,更没法走过去从另一个角度看。视觉上是 3D,体验上还是 2D。

visionOS 26 的 Safari 引入了一个酝酿多年的提案:HTML <model> 元素。语法和 <img> 一样简单,但渲染行为完全不同——它在页面表面”开了一扇窗”,把 USDZ 模型立体地放在虚拟空间里。用户可以转头从不同角度观察,也可以捏住模型把它拖到 Quick Look 中以真实尺寸查看,甚至直接拖进其他 app。这个元素和文档流、CSS、JavaScript 完全融合:可以用 background-color 让虚拟空间和页面底色一致,可以用 backdrop-filter 在模型上覆盖一层毛玻璃面板,也可以用 position: absolute 让其他 DOM 元素与模型分层叠放。

除了 <model>,这个 session 还讲了沉浸式媒体(180/360 度视频、Apple Immersive Video、空间照片)在 <video> / <img> 里的内联播放与全屏,以及 developer preview 的 <link rel="spatial-backdrop">,让网站为自己指定一个 USDZ 环境,访客可以选择沉浸进去再浏览。


详细内容

<model> 的最简形式(01:00

<model src="teapot.usdz"></model>

关键点:

  • src 指向一个 USDZ 文件,<model> 元素必须显式闭合(不像 <img> 是 void 元素)。
  • 浏览器会把 bounding box 的 x-y 面居中缩放到元素内;模型始终渲染在页面表面之后,滚动出视口时不会”戳出来”。

服务器需要正确的 MIME 类型(05:30

# NGINX mime.types
types {
  ...
  model/vnd.usdz+zip usdz;
}

关键点:

  • 旧 CDN / 服务器可能把 .usdz 当成 application/octet-stream,浏览器会拒绝渲染。
  • Apache 用 AddType model/vnd.usdz+zip .usdz,Python 自带的 http.server 需要修改 Handler.extensions_map

提供降级方案(05:51 ~ 06:17

<!-- <model-viewer> library from https://modelviewer.dev/ -->
<script type="module"
  src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js">
</script>

<model src="camera.usdz">
  <!-- Fallback experience for backward compatibility -->
  <model-viewer src="camera.glb"></model-viewer>
</model>

关键点:

  • 把 fallback 内容直接写在 <model> 内部,支持 <model> 的浏览器忽略它,不支持的浏览器会渲染内部内容。
  • 最简单的 fallback 是一张 <img>;想保留旋转体验就嵌一个 <model-viewer> 这类 2D 库。

检测特性而非 user agent(06:52

if (window.HTMLModelElement) {
  // Supported by this browser
} else {
  // Not supported by this browser
}

关键点:

  • 检测 window.HTMLModelElement 这个全局对象是否存在。
  • 不要用 user agent 字符串判断,将来其他浏览器实现这个标准时会判断错。

加载指示器(07:32

<model src="camera.usdz" id="mymodel"></model>

<script>
const mymodel = document.getElementById("mymodel");

if (window.HTMLModelElement) {
  mymodel.ready.then(result => {
    // Hide the loading indicator
    // Show the model
  }).catch(error => {
    // Loading error occurred, show a retry button
  });
}
</script>

关键点:

  • model.ready 返回一个 Promise,资源完全加载后 resolve。
  • USDZ 文件超过 10MB 是常态,慢网络要好几秒;显示 loading 指示器能防止用户直接关页面。
  • .catch 里准备一个重试按钮,处理网络失败。

用 IBL 控制光照(10:56

<model src="camera.usdz" environmentmap="sunset.exr"></model>

关键点:

  • environmentmap 属性指定一张 image-based lighting 图(等距柱状投影)。
  • 推荐 OpenEXR 或 Radiance HDR 格式,可以表达多个数量级的亮度,反射效果真实;JPEG 会让模型看起来很平。
  • IBL 文件不打包进 USDZ,便于不同场景应用不同光照。

JavaScript 控制位姿(13:49

<model src="teapot.usdz" id="mymodel"></model>
<a onclick="turnRight()">Right</a>

<script>
const mymodel = document.getElementById("mymodel");
function turnRight() {
  const matrix = mymodel.entityTransform; // DOMMatrixReadOnly
  const newMatrix = matrix.rotateAxisAngle(0, 1, 0, 90);
  mymodel.entityTransform = newMatrix;
}
</script>

关键点:

  • entityTransform 是一个 DOMMatrixReadOnly,读取当前模型的位姿。
  • 通过 rotateAxisAngle(0, 1, 0, 90) 绕 y 轴旋转 90°,再赋值回 entityTransform 触发渲染更新。
  • 想恢复初始状态:model.entityTransform = new DOMMatrix();

按时间轴跳转动画(17:35

<model src="camera.usdz" id="mymodel"></model>

<script>
const mymodel = document.getElementById("mymodel");

function openFlash() {
  mymodel.currentTime = 1; // Unit is seconds
}

function openScreen() {
  mymodel.currentTime = 3; // Unit is seconds
}
</script>

关键点:

  • currentTime 单位是秒,对应 USDZ 文件内的动画时间轴。
  • 配合 <input type="range"> 可以做一个手动拖动的动画进度条。
  • 自动播放用 <model loop autoplay>;JS 控制用 model.play() / model.pause(),状态读 model.paused

用 Three.js 动态生成 USDZ(19:35

import * as THREE from "three";
import { USDZExporter } from "three/examples/exporters/USDZExporter.js";

async function generateModel() {
  const scene = new THREE.Scene();
  // ... create a really nice scene procedurally ...

  const bytes = await new USDZExporter().parseAsync(scene);
  const objURL = URL.createObjectURL(new Blob([bytes]));

  const mymodel = document.getElementById("mymodel");
  mymodel.setAttribute("src", objURL);
}

关键点:

  • Three.js 自带 USDZExporter,把过程化生成的场景导出成 USDZ 字节流。
  • URL.createObjectURL 包成 blob URL,再赋给 <model>src,浏览器即可立体渲染。
  • 适合做参数化产品配置器:用户改颜色 / 改尺寸 → 重新生成 USDZ → 直接预览。

沉浸式媒体内联(23:10

<video src="spatial_video.mov"></video>  <!-- Single file -->
<video src="360_video.m3u8"></video>  <!-- HTTP Live Streaming -->

关键点:

  • 现有 <video> 标签直接支持 180/360 度、广角和 Apple Immersive Video。
  • 默认在页面里 2D 内联播放,调用 player.requestFullScreen() 全屏后自动以正确投影方式包裹用户。
  • 空间照片用 <img src="spatial.heic" controls> 即可,加 controls 属性会显示打开沉浸视图的按钮。

Website Environments(developer preview,26:49

<link rel="spatial-backdrop" href="office.usdz" environmentmap="lighting.hdr">

关键点:

  • <link> 给整个页面声明一个 USDZ 沉浸式背景。
  • 用户主动选择”沉浸”进去后,整个房间会替换成 office.usdz 的环境。
  • 目前是 developer preview 状态,需要在 Safari 设置里手动开启。

核心启发

  • 做什么:把产品 3D 展示从 2D canvas 升级到 <model>

    • 为什么值得做:电商、家具、相机、玩具类商品在 visionOS Safari 上能直接立体展示,转头看不同角度比”360 度图集”沉浸感高一个量级,且无需 native app。
    • 怎么开始:用 iPhone + Reality Composer 把样品拍成 USDZ → Mac Preview 导出压缩纹理版本控制在 5MB 以内 → 在原产品页加 <model src>,用 <img> 做 fallback。
  • 做什么:用 <model> + DOM 做产品配置器

    • 为什么值得做:不需要再造一套 WebGL 渲染管线,CSS 就能做毛玻璃面板、文字层、按钮;切换配置直接改 src 或者用 Three.js 现场生成 USDZ。
    • 怎么开始:先在原页用 Three.js 跑通过程化生成场景,然后接入 USDZExporter 输出 blob URL;用 model.ready 控制 loading 态。
  • 做什么:把品牌沉浸式短片放到官网

    • 为什么值得做:Apple Immersive Video 在 visionOS Safari 上可以直接 <video> 内联播放和全屏沉浸,省掉 App Store 上架;普通访客看到的是 2D 内联,Vision Pro 用户全屏后是 180 度沉浸。
    • 怎么开始:把素材切成 HLS(.m3u8)→ 同一份 <video> 标签同时服务两端 → 在桌面端做好降级海报。
  • 做什么:尝试 spatial-backdrop 给品牌站做专属环境

    • 为什么值得做:作为 developer preview 早期试水,能让访客”沉浸进店”;先发先得,可以收获媒体关注。
    • 怎么开始:用 Reality Composer Pro 做一个简单的店面 / 展厅 USDZ → 配套一张 HDR 光照 → 加 <link rel="spatial-backdrop">,在 Safari 实验性功能里开启验证。

关联 Session

评论

GitHub Issues · utterances