worldlabs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

World Labs — 3D World/Environment Generation

World Labs — 3D世界/环境生成

Generate photorealistic 3D environments from text prompts or images using the World Labs Marble API. Outputs Gaussian Splat scenes (SPZ) rendered via SparkJS in Three.js, plus collider meshes (GLB) for physics.
借助World Labs Marble API,通过文本提示词或图像生成照片级真实感的3D环境。输出可通过Three.js中的SparkJS渲染的Gaussian Splat场景(SPZ),以及用于物理碰撞的碰撞器网格(GLB)。

When to Use

使用场景

  • Environment/level generation — create entire 3D worlds (rooms, landscapes, buildings) from reference images or text
  • Complementary to Meshy AI — Meshy generates individual models/characters; World Labs generates the environments they exist in
  • Photorealistic scenes — Gaussian Splats produce photorealistic quality vs mesh-based environments
  • 环境/关卡生成——通过参考图像或文本创建完整的3D世界(房间、景观、建筑)
  • 与Meshy AI互补——Meshy生成单个模型/角色;World Labs生成它们所处的环境
  • 照片级真实感场景——与基于网格的环境相比,Gaussian Splat可生成照片级真实感的画面

Input Priority

输入优先级

Image-first — always prefer image input over text:
  1. Image mode (default) — if the user has a reference image, concept art, screenshot, or photo, use
    --mode image
    . This produces the most faithful results because the AI can match the exact visual style, layout, lighting, and mood.
  2. Text mode (fallback) — only use
    --mode text
    when no reference image is available. The API auto-expands short prompts into rich scene descriptions, but results are less predictable than image-driven generation.
When the game-creator pipeline runs, ask the user for a reference image first:
I can generate a photorealistic 3D environment for your game using World Labs. Do you have a reference image (photo, concept art, screenshot) for the environment?
  • Yes → provide the file path or URL
  • No → I'll generate from a text description instead
图像优先——始终优先使用图像输入而非文本:
  1. 图像模式(默认)——如果用户有参考图像、概念图、截图或照片,使用
    --mode image
    。这种方式能生成最贴合需求的结果,因为AI可以匹配精确的视觉风格、布局、光线和氛围。
  2. 文本模式(备选)——仅在没有参考图像时使用
    --mode text
    。API会将简短提示词自动扩展为丰富的场景描述,但结果的可预测性低于图像驱动的生成。
当游戏创建流程启动时,首先询问用户是否有参考图像
我可以使用World Labs为你的游戏生成照片级真实感的3D环境。 你是否有该环境的参考图像(照片、概念图、截图)?
  • → 提供文件路径或URL
  • → 我将通过文本描述生成

Tech Stack

技术栈

ComponentTechnology
APIWorld Labs Marble API (
https://api.worldlabs.ai/marble/v1
)
Auth
WLT-Api-Key
header
Output: VisualGaussian Splat (
.spz
) — 100k, 500k, full resolution tiers
Output: PhysicsCollider mesh (
.glb
) — for collision detection
Output: SkyboxPanorama image (
.jpg
/
.png
)
Browser RendererSparkJS (
@worldlabs/spark
) — Three.js compatible
CLI Script
scripts/worldlabs-generate.mjs
(zero dependencies)
组件技术
APIWorld Labs Marble API (
https://api.worldlabs.ai/marble/v1
)
认证
WLT-Api-Key
请求头
输出:视觉内容Gaussian Splat(
.spz
)——100k、500k、全分辨率三个层级
输出:物理碰撞碰撞器网格(
.glb
)——用于碰撞检测
输出:天空盒全景图像(
.jpg
/
.png
浏览器渲染器SparkJS (
@worldlabs/spark
)——兼容Three.js
CLI脚本
scripts/worldlabs-generate.mjs
(无依赖)

Environment Variable

环境变量

bash
export WORLDLABS_API_KEY=<your-key>
bash
export WORLDLABS_API_KEY=<your-key>

CLI Script Usage

CLI脚本使用方法

bash
undefined
bash
undefined

Text to 3D world

文本转3D世界

WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode text --prompt "a medieval tavern with wooden beams and a roaring fireplace"
--output public/assets/worlds/ --slug tavern
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode text --prompt "a medieval tavern with wooden beams and a roaring fireplace"
--output public/assets/worlds/ --slug tavern

Image to 3D world (local file or URL)

图像转3D世界(本地文件或URL)

WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode image --image ./reference-photo.jpg
--output public/assets/worlds/ --slug my-world
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode image --image ./reference-photo.jpg
--output public/assets/worlds/ --slug my-world

Check generation status

检查生成状态

WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode status --operation-id <op-id>
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode status --operation-id <op-id>

Download assets from existing world

下载现有世界的资源

WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode get --world-id <id> --output public/assets/worlds/ --slug my-world
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode get --world-id <id> --output public/assets/worlds/ --slug my-world

List your worlds

列出你的所有世界

WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs --mode list
undefined
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs --mode list
undefined

Output Files

输出文件

public/assets/worlds/
  tavern.spz              # Gaussian Splat (full resolution)
  tavern-500k.spz         # Gaussian Splat (500k, medium quality)
  tavern-100k.spz         # Gaussian Splat (100k, lightweight/mobile)
  tavern-collider.glb     # Collider mesh for physics (GLB)
  tavern-pano.jpg         # Panorama image (skybox)
  tavern.meta.json        # Metadata: world ID, prompt, timestamps, asset URLs
public/assets/worlds/
  tavern.spz              # Gaussian Splat(全分辨率)
  tavern-500k.spz         # Gaussian Splat(500k,中等质量)
  tavern-100k.spz         # Gaussian Splat(100k,轻量/移动端适配)
  tavern-collider.glb     # 物理碰撞器网格(GLB)
  tavern-pano.jpg         # 全景图像(天空盒)
  tavern.meta.json        # 元数据:世界ID、提示词、时间戳、资源URL

Integration with Three.js Games

与Three.js游戏集成

Tested & working — see
examples/worldlabs-arcade/
for a complete runnable demo.
已测试并可正常使用——查看
examples/worldlabs-arcade/
获取完整可运行的示例。

Install SparkJS

安装SparkJS

bash
npm install @sparkjsdev/spark
Package:
@sparkjsdev/spark
— high-performance Gaussian Splat renderer for Three.js. Supports SPZ, PLY, SOGS, KSPLAT, SPLAT formats.
bash
npm install @sparkjsdev/spark
包名
@sparkjsdev/spark
——适用于Three.js的高性能Gaussian Splat渲染器。支持SPZ、PLY、SOGS、KSPLAT、SPLAT格式。

Constants.js — World Configuration

Constants.js — 世界配置

js
export const WORLD = {
  splatPath: 'assets/worlds/tavern-500k.spz',   // 500k is a good desktop default
  colliderPath: 'assets/worlds/tavern-collider.glb',
  panoPath: 'assets/worlds/tavern-pano.png',
  scale: 1,
  position: { x: 0, y: 0, z: 0 },
};
js
export const WORLD = {
  splatPath: 'assets/worlds/tavern-500k.spz',   // 500k是桌面端的理想默认值
  colliderPath: 'assets/worlds/tavern-collider.glb',
  panoPath: 'assets/worlds/tavern-pano.png',
  scale: 1,
  position: { x: 0, y: 0, z: 0 },
};

WorldLoader.js — Load Gaussian Splat + Collider

WorldLoader.js — 加载Gaussian Splat + 碰撞器

js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { SplatMesh } from '@sparkjsdev/spark';
import { WORLD } from '../core/Constants.js';

let _colliderMesh = null;
let _splatMesh = null;

export async function loadWorld(scene, renderer, camera) {
  const promises = [];

  // 1. Gaussian Splat via SparkJS — SplatMesh works like any Three.js object
  if (WORLD.splatPath) {
    promises.push((async () => {
      const splat = new SplatMesh({ url: WORLD.splatPath });
      splat.scale.setScalar(WORLD.scale);
      splat.position.set(WORLD.position.x, WORLD.position.y, WORLD.position.z);
      scene.add(splat);
      _splatMesh = splat;
    })());
  }

  // 2. Collider mesh (GLB) — invisible, for physics raycasting only
  if (WORLD.colliderPath) {
    promises.push((async () => {
      const loader = new GLTFLoader();
      const gltf = await loader.loadAsync(WORLD.colliderPath);
      _colliderMesh = gltf.scene;
      _colliderMesh.visible = false;
      _colliderMesh.scale.setScalar(WORLD.scale);
      _colliderMesh.position.set(WORLD.position.x, WORLD.position.y, WORLD.position.z);
      _colliderMesh.traverse(c => { if (c.isMesh) c.material.side = THREE.DoubleSide; });
      scene.add(_colliderMesh);
    })());
  }

  // 3. Panorama as equirectangular skybox + environment lighting
  if (WORLD.panoPath) {
    promises.push((async () => {
      const texLoader = new THREE.TextureLoader();
      const panoTex = await texLoader.loadAsync(WORLD.panoPath);
      panoTex.mapping = THREE.EquirectangularReflectionMapping;
      panoTex.colorSpace = THREE.SRGBColorSpace;
      scene.background = panoTex;
      scene.environment = panoTex;
    })());
  }

  await Promise.all(promises);
  return { splat: _splatMesh, collider: _colliderMesh };
}

// Raycast down to find ground height on collider mesh
const _raycaster = new THREE.Raycaster();
const _downDir = new THREE.Vector3(0, -1, 0);
const _rayOrigin = new THREE.Vector3();

export function getGroundHeight(x, z, fallback = 0) {
  if (!_colliderMesh) return fallback;
  _rayOrigin.set(x, 50, z);
  _raycaster.set(_rayOrigin, _downDir);
  const hits = _raycaster.intersectObject(_colliderMesh, true);
  return hits.length > 0 ? hits[0].point.y : fallback;
}

export function getCollider() { return _colliderMesh; }
js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { SplatMesh } from '@sparkjsdev/spark';
import { WORLD } from '../core/Constants.js';

let _colliderMesh = null;
let _splatMesh = null;

export async function loadWorld(scene, renderer, camera) {
  const promises = [];

  // 1. 借助SparkJS加载Gaussian Splat — SplatMesh的使用方式与普通Three.js对象一致
  if (WORLD.splatPath) {
    promises.push((async () => {
      const splat = new SplatMesh({ url: WORLD.splatPath });
      splat.scale.setScalar(WORLD.scale);
      splat.position.set(WORLD.position.x, WORLD.position.y, WORLD.position.z);
      scene.add(splat);
      _splatMesh = splat;
    })());
  }

  // 2. 碰撞器网格(GLB)——不可见,仅用于物理射线检测
  if (WORLD.colliderPath) {
    promises.push((async () => {
      const loader = new GLTFLoader();
      const gltf = await loader.loadAsync(WORLD.colliderPath);
      _colliderMesh = gltf.scene;
      _colliderMesh.visible = false;
      _colliderMesh.scale.setScalar(WORLD.scale);
      _colliderMesh.position.set(WORLD.position.x, WORLD.position.y, WORLD.position.z);
      _colliderMesh.traverse(c => { if (c.isMesh) c.material.side = THREE.DoubleSide; });
      scene.add(_colliderMesh);
    })());
  }

  // 3. 将全景图作为等距天空盒 + 环境光
  if (WORLD.panoPath) {
    promises.push((async () => {
      const texLoader = new THREE.TextureLoader();
      const panoTex = await texLoader.loadAsync(WORLD.panoPath);
      panoTex.mapping = THREE.EquirectangularReflectionMapping;
      panoTex.colorSpace = THREE.SRGBColorSpace;
      scene.background = panoTex;
      scene.environment = panoTex;
    })());
  }

  await Promise.all(promises);
  return { splat: _splatMesh, collider: _colliderMesh };
}

// 向下射线检测以获取碰撞器网格上的地面高度
const _raycaster = new THREE.Raycaster();
const _downDir = new THREE.Vector3(0, -1, 0);
const _rayOrigin = new THREE.Vector3();

export function getGroundHeight(x, z, fallback = 0) {
  if (!_colliderMesh) return fallback;
  _rayOrigin.set(x, 50, z);
  _raycaster.set(_rayOrigin, _downDir);
  const hits = _raycaster.intersectObject(_colliderMesh, true);
  return hits.length > 0 ? hits[0].point.y : fallback;
}

export function getCollider() { return _colliderMesh; }

Game.js — Render Loop Integration

Game.js — 渲染循环集成

SplatMesh renders as part of the normal Three.js scene — no separate render pass needed. Just
scene.add(splat)
and call
renderer.render(scene, camera)
as usual.
js
import { loadWorld, getGroundHeight } from '../level/WorldLoader.js';

// In init():
await loadWorld(scene, renderer, camera);

// In the render loop — standard Three.js, no extra splat pass:
function animate() {
  requestAnimationFrame(animate);

  player.update(delta, input, azimuth);

  // Snap player Y to collider ground
  const groundY = getGroundHeight(player.mesh.position.x, player.mesh.position.z, 0);
  player.mesh.position.y = groundY;

  // Single render call handles both meshes AND splats
  renderer.render(scene, camera);
}
SplatMesh作为普通Three.js场景的一部分进行渲染——无需单独的渲染通道。只需执行
scene.add(splat)
,然后像往常一样调用
renderer.render(scene, camera)
即可。
js
import { loadWorld, getGroundHeight } from '../level/WorldLoader.js';

// 在init()中:
await loadWorld(scene, renderer, camera);

// 在渲染循环中——标准Three.js流程,无需额外的splat渲染步骤:
function animate() {
  requestAnimationFrame(animate);

  player.update(delta, input, azimuth);

  // 将玩家Y轴位置对齐到碰撞器地面
  const groundY = getGroundHeight(player.mesh.position.x, player.mesh.position.z, 0);
  player.mesh.position.y = groundY;

  // 单次渲染调用即可处理网格和splat
  renderer.render(scene, camera);
}

Resolution Tiers

分辨率层级

TierFileQualityUse Case
100k
{slug}-100k.spz
LowMobile, fast loading, previews
500k
{slug}-500k.spz
MediumDesktop games, good balance
full_res
{slug}.spz
HighHigh-end, hero environments
Choose based on target platform. The collider mesh (GLB) is the same regardless of splat resolution.
层级文件质量使用场景
100k
{slug}-100k.spz
移动端、快速加载、预览
500k
{slug}-500k.spz
桌面端游戏,平衡质量与性能
full_res
{slug}.spz
高端设备、核心展示环境
根据目标平台选择合适的层级。无论splat分辨率如何,碰撞器网格(GLB)都是相同的。

Pipeline: World Labs + Meshy AI

工作流:World Labs + Meshy AI

For a complete 3D game, combine both:
  1. World Labs → Generate the environment (room, landscape, arena)
  2. Meshy AI → Generate characters and props (player, enemies, items)
  3. Integrate → Characters walk on World Labs collider mesh, rendered inside the Gaussian Splat scene
┌─────────────────────────────────────────────────┐
│                Complete 3D Scene                 │
├─────────────────────────────────────────────────┤
│  World Labs (environment)                        │
│    └─ Gaussian Splat (visual)                    │
│    └─ Collider mesh (physics)                    │
│    └─ Panorama (skybox)                          │
│                                                   │
│  Meshy AI (entities)                              │
│    └─ Player character (rigged, animated GLB)     │
│    └─ Enemies (rigged, animated GLB)              │
│    └─ Props/items (static GLB)                    │
│                                                   │
│  Three.js (engine)                                │
│    └─ SparkJS renders splats                      │
│    └─ GLTFLoader renders characters/props         │
│    └─ Raycaster uses collider for ground/walls    │
└─────────────────────────────────────────────────┘
构建完整3D游戏时,可结合两者的优势:
  1. World Labs → 生成环境(房间、景观、竞技场)
  2. Meshy AI → 生成角色和道具(玩家、敌人、物品)
  3. 集成 → 角色在World Labs的碰撞器网格上行走,在Gaussian Splat场景中渲染
┌─────────────────────────────────────────────────┐
│                完整3D场景                        │
├─────────────────────────────────────────────────┤
│  World Labs(环境)                              │
│    └─ Gaussian Splat(视觉内容)                  │
│    └─ 碰撞器网格(物理碰撞)                      │
│    └─ 全景图(天空盒)                            │
│                                                   │
│  Meshy AI(实体)                                │
│    └─ 玩家角色(带骨骼绑定、动画的GLB)            │
│    └─ 敌人(带骨骼绑定、动画的GLB)                │
│    └─ 道具/物品(静态GLB)                        │
│                                                   │
│  Three.js(引擎)                                │
│    └─ SparkJS渲染splat内容                        │
│    └─ GLTFLoader渲染角色/道具                     │
│    └─ Raycaster使用碰撞器检测地面/墙壁              │
└─────────────────────────────────────────────────┘

Reference Implementation

参考实现

See
examples/worldlabs-arcade/
— a complete, tested demo with:
  • World Labs Gaussian Splat environment (retro arcade)
  • Animated Soldier character (walk/run/idle)
  • OrbitControls third-person camera
  • Collider-mesh ground raycasting
  • Panorama skybox
查看
examples/worldlabs-arcade/
——完整的已测试示例,包含:
  • World Labs Gaussian Splat环境(复古街机风格)
  • 带动画的士兵角色(行走/奔跑/ idle)
  • OrbitControls第三人称相机
  • 碰撞器网格地面射线检测
  • 全景天空盒

Troubleshooting

故障排除

Scene appears upside down (Y-flip)

场景显示颠倒(Y轴翻转)

Cause: World Labs SPZ files use Y-inverted coordinates compared to Three.js convention. Fix: Apply
rotation.x = Math.PI
to both the splat mesh and collider mesh. Then adjust position.z to compensate:
position.z += (minZ + maxZ)
. Do NOT use
scale.y = -1
on a parent group — SparkJS breaks with negative parent scale.
原因:World Labs的SPZ文件使用的Y轴坐标与Three.js的惯例相反。 解决方法:对splat网格和碰撞器网格应用
rotation.x = Math.PI
。然后调整position.z进行补偿:
position.z += (minZ + maxZ)
。不要对父级组使用
scale.y = -1
——SparkJS在父级缩放为负数时会出现异常。

Raycast hits ceiling instead of floor

射线检测击中天花板而非地面

Cause: After Y-flip, the coordinate system is inverted. Downward raycasts hit what was originally the floor (now the ceiling in flipped space). Fix: Raycast UPWARD from Y=-50 with direction
(0, 1, 0)
to hit the visual floor first. The floor is the lowest surface after the flip.
原因:Y轴翻转后,坐标系被反转。向下的射线检测会击中原本的地面(在翻转后的空间中变为天花板)。 解决方法:从Y=-50的位置向上射线检测,方向为
(0, 1, 0)
,这样会首先击中视觉上的地面。翻转后的地面是空间中最低的表面。

Collider mesh raycasts return no hits

碰撞器网格射线检测无结果

Cause: The collider mesh's world matrix hasn't been updated after setting rotation/position, especially before the first render frame. Fix: Call
_colliderMesh.updateMatrixWorld(true)
immediately after setting rotation and position, before any raycast operations.
原因:设置旋转/位置后,碰撞器网格的世界矩阵未更新,尤其是在第一帧渲染前。 解决方法:在设置旋转和位置后,立即调用
_colliderMesh.updateMatrixWorld(true)
,再执行任何射线检测操作。

Scene appears doubled / world inside a world

场景显示重复 / 世界套娃

Cause: Using the World Labs panorama as
scene.background
shows the same environment as a giant sphere surrounding the 3D scene. Fix: Don't use the panorama as scene background. Use a solid color (
scene.background = new THREE.Color(0x87CEEB)
) or a custom skybox instead.
原因:将World Labs的全景图设置为
scene.background
后,会在3D场景周围显示一个巨大的球体,内容与当前环境相同。 解决方法:不要将全景图作为场景背景。使用纯色(
scene.background = new THREE.Color(0x87CEEB)
)或自定义天空盒代替。

Generation times out or takes too long

生成超时或耗时过长

Cause: World Labs generation typically takes 3-8 minutes. Complex scenes or high server load can extend this. Fix: Poll the operation status endpoint every 10-15 seconds. Check
progress.status
for
"IN_PROGRESS"
vs
"COMPLETE"
. If stuck beyond 15 minutes, create a new generation request — don't retry the same operation.
原因:World Labs的生成流程通常需要3-8分钟。复杂场景或服务器负载过高可能会延长耗时。 解决方法:每10-15秒轮询一次操作状态接口。检查
progress.status
"IN_PROGRESS"
还是
"COMPLETE"
。如果超过15分钟仍未完成,创建新的生成请求——不要重试同一个操作。

Cannot find SparkJS package

找不到SparkJS包

Cause: The package name is
@sparkjsdev/spark
, not
@worldlabs/spark
or other variations. Fix: Install with
npm install @sparkjsdev/spark
. Import
SplatMesh
from this package. It integrates directly into the Three.js scene — no separate render pass needed.
原因:包名称是
@sparkjsdev/spark
,而非
@worldlabs/spark
或其他变体。 解决方法:使用
npm install @sparkjsdev/spark
安装。从该包导入
SplatMesh
。它可直接集成到Three.js场景中——无需单独的渲染通道。

Media upload fails with 404

媒体上传失败,返回404

Cause: Using the wrong endpoint for media upload preparation. Fix: Use
POST /media-assets:prepare_upload
(NOT
/media-assets
). This returns a signed URL for PUT upload. The colon syntax is intentional — it's a custom action on the resource.
原因:使用了错误的媒体上传准备接口。 解决方法:使用
POST /media-assets:prepare_upload
(而非
/media-assets
)。该接口会返回一个用于PUT上传的签名URL。冒号语法是有意设计的——它是资源的自定义操作。

API returns 401 Unauthorized

API返回401未授权

Cause: Using the wrong authentication header format. Fix: Use
WLT-Api-Key: <your-key>
header, NOT
Authorization: Bearer <your-key>
. The World Labs API uses a custom header format.
原因:使用了错误的认证请求头格式。 解决方法:使用
WLT-Api-Key: <your-key>
请求头,而非
Authorization: Bearer <your-key>
。World Labs API使用自定义的请求头格式。

Checklist

检查清单

  • WORLDLABS_API_KEY
    environment variable is set
  • Ask user for reference image first (image-to-world is preferred)
  • Run
    scripts/worldlabs-generate.mjs
    to generate world (~3-8 min)
  • SPZ + collider GLB + panorama downloaded to
    public/assets/worlds/
  • @sparkjsdev/spark
    installed (
    npm install @sparkjsdev/spark
    )
  • WorldLoader.js
    created in
    src/level/
    (SplatMesh + GLTFLoader + TextureLoader)
  • Constants.js
    updated with
    WORLD
    config (splatPath, colliderPath, panoPath)
  • Game.js
    calls
    loadWorld()
    in init, uses
    getGroundHeight()
    for player Y
  • Single
    renderer.render(scene, camera)
    handles both splats and meshes — no extra pass
  • Physics uses invisible collider mesh for ground/wall raycasting
  • Test: character walks on collider surface, splat renders around them
  • Performance: use 500k SPZ for desktop, 100k for mobile
  • 已设置
    WORLDLABS_API_KEY
    环境变量
  • 首先询问用户是否有参考图像(优先使用图像转世界功能)
  • 运行
    scripts/worldlabs-generate.mjs
    生成世界(耗时约3-8分钟)
  • SPZ + 碰撞器GLB + 全景图已下载到
    public/assets/worlds/
  • 已安装
    @sparkjsdev/spark
    npm install @sparkjsdev/spark
  • src/level/
    中创建了
    WorldLoader.js
    (包含SplatMesh + GLTFLoader + TextureLoader)
  • 已更新
    Constants.js
    中的
    WORLD
    配置(splatPath、colliderPath、panoPath)
  • Game.js
    在初始化时调用
    loadWorld()
    ,使用
    getGroundHeight()
    设置玩家Y轴位置
  • 单次
    renderer.render(scene, camera)
    调用即可处理splat和网格——无需额外渲染通道
  • 物理系统使用不可见的碰撞器网格进行地面/墙壁射线检测
  • 测试:角色可在碰撞器表面行走,splat内容在周围正常渲染
  • 性能优化:桌面端使用500k SPZ,移动端使用100k SPZ