three.js SkyGeometry 过程化天空几何体:源码原理与 VR 过山车示例实战
【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js
SkyGeometry 是 three.js 中一个随附在RollerCoaster.js附加模块(addon)里的过程化(procedural)天空几何体:它不依赖任何外部贴图或资源,仅在构造时用随机数在天空高度上生成一批"云朵"形状的三角形面片。本文以 docs/pages/SkyGeometry.html.md 为骨架,结合其真实实现 examples/jsm/misc/RollerCoaster.js 与官方示例 webxr_vr_rollercoaster.html、webgpu_xr_rollercoaster.html,讲清它的导入方式、构造行为、几何数据生成算法,以及如何在项目中把它渲染成天空中的云层。读完你将能独立理解并复用这个轻量的"云朵"几何体。
类概览与继承关系
SkyGeometry是一个程序化生成的天空几何体(原文描述为 "A procedural sky geometry")。它的继承链与 three.js 核心几何体系完全一致:
EventDispatcher → BufferGeometry → SkyGeometry即它继承自 src/core/BufferGeometry.js,因此在构造完成后具备attributes、boundingSphere、computeBoundingBox()等所有标准BufferGeometry能力,可以直接交给Mesh使用。
在官方文档页中,它是作为 RollerCoaster(过山车)模块的组成类被收录的。从 examples/jsm/misc/RollerCoaster.js 的导出语句可以看到,同一个模块中还导出了:
export { RollerCoasterGeometry, RollerCoasterLiftersGeometry, RollerCoasterShadowGeometry, SkyGeometry, TreesGeometry };其中RollerCoasterGeometry生成轨道、RollerCoasterLiftersGeometry生成提升坡道、RollerCoasterShadowGeometry生成地面投影、TreesGeometry生成散布在地表上的树木、而SkyGeometry负责生成漂浮在空中的云朵——它们共同组成一个完整的过山车户外场景。
导入方式
SkyGeometry属于官方 addon,不会打包在核心three模块中,必须显式导入。官方文档给出的导入代码为:
import { SkyGeometry } from 'three/addons/misc/RollerCoaster.js';值得留意的是:虽然类名叫SkyGeometry,它实际定义在文件名与过山车相关的RollerCoaster.js中,这一点以源码注释中的@three_import标记(examples/jsm/misc/RollerCoaster.js)为准,不要误以为它位于Sky.js或Sky目录下。
在官方示例中通常还会一并导入同模块的其他几何体与 XR 按钮(webxr_vr_rollercoaster.html):
import { RollerCoasterGeometry, RollerCoasterLiftersGeometry, RollerCoasterShadowGeometry, TreesGeometry, SkyGeometry } from 'three/addons/misc/RollerCoaster.js'; import { VRButton } from 'three/addons/webxr/VRButton.js';构造函数与使用方式
new SkyGeometry()
构造函数不接受任何参数,这与RollerCoasterGeometry( curve, divisions )或TreesGeometry( landscape )需要外部输入截然不同:它完全自包含,随机性由内部生成。其实现位于 examples/jsm/misc/RollerCoaster.js,核心逻辑只有两步:
- 循环 100 次,随机生成云朵位置与大小;
- 为每个云朵生成两个三角形(共 6 个顶点),拼接进
vertices数组,最后一次性写入 position 属性。
调用示例(取自 webgpu_xr_rollercoaster.html):
geometry = new SkyGeometry(); material = new THREE.MeshBasicMaterial( { color: 0xffffff } ); mesh = new THREE.Mesh( geometry, material ); scene.add( mesh );由于几何体自带全部顶点数据,渲染只需配合最简单的MeshBasicMaterial(纯色、不受光照影响),非常适合作为远景天空装饰。
几何数据生成算法解析
SkyGeometry最有价值的地方在于它完全用代码描述几何生成。逐段拆解源码可以还原出以下精确规则(examples/jsm/misc/RollerCoaster.js):
for ( let i = 0; i < 100; i ++ ) { const x = Math.random() * 800 - 400; const y = Math.random() * 50 + 50; const z = Math.random() * 800 - 400; const size = Math.random() * 40 + 20; vertices.push( x - size, y, z - size ); vertices.push( x + size, y, z - size ); vertices.push( x - size, y, z + size ); vertices.push( x + size, y, z - size ); vertices.push( x + size, y, z + size ); vertices.push( x - size, y, z + size ); }各随机参数的实际含义与取值范围如下:
| 参数 | 生成表达式 | 取值范围 | 含义 |
|---|---|---|---|
x | Math.random() * 800 - 400 | [-400, 400) | 云朵在水平 X 轴上的中心位置,铺满 800 单位宽的区域 |
z | Math.random() * 800 - 400 | [-400, 400) | 云朵在水平 Z 轴上的中心位置,与 X 轴共同构成 800×800 的分布范围 |
y | Math.random() * 50 + 50 | [50, 100) | 云朵高度,全部位于地平线上方 50~100 单位,形成"悬浮天空层" |
size | Math.random() * 40 + 20 | [20, 60) | 云朵面片半边长,即单块云宽高约为 40~120 单位 |
几何拓扑上,每个云朵由两个三角形拼成一个轴对齐的矩形(quad),矩形所在平面平行于 XZ 世界平面(所有顶点y相同):
- 三角形 1:
(x-size, z-size)、(x+size, z-size)、(x-size, z+size) - 三角形 2:
(x+size, z-size)、(x+size, z+size)、(x-size, z+size)
三角形顶点不做索引化(non-indexed),而是逐块写入 6 个独立顶点,因此云朵块之间可以拥有不同的朝向(flat shading)而不互相干扰;最终数据通过setAttribute( 'position', new BufferAttribute( new Float32Array( vertices ), 3 ) )(examples/jsm/misc/RollerCoaster.js)一次性提交给 GPU。
整个几何体只包含一个position属性(300 个顶点、共 900 个浮点数),不包含法线、UV 与顶点色。因此源码注释中仅有一句 "A procedural sky geometry",指的就是这套"纯位置数据 + 随机分布"的自包含生成策略。也正因顶点数是固定的(100 × 6 = 600个顶点),每次实例化都会产生不同的云朵排布,用于营造每次进入场景都不一样的天空。
渲染要点:为什么用 MeshBasicMaterial
官方示例中选择new THREE.MeshBasicMaterial( { color: 0xffffff } )渲染云朵,原因与几何体自身特性强相关:
- 无光照需求:云朵面片没有计算法线,用
MeshLambertMaterial/MeshPhongMaterial这类光照材质不会有真实漫反射效果,反而可能显得斑驳; - 无纹理需求:几何体没有 UV 坐标,无法(也无需)采样贴图;
- 场景配合:示例将场景背景设为浅蓝天空色
scene.background = new THREE.Color( 0xf0f0ff )(见 webxr_vr_rollercoaster.html),白色的云与浅蓝背景形成天然的"晴空白云"对比。
如果需要更立体的云,可以在构造后自行computeVertexNormals()并改用MeshStandardMaterial,或在渲染层面将云层Mesh放到轨道、树木之后,避免遮挡前景。
在实际项目中的定位:XR 过山车天空层
SkyGeometry的典型使用场景来自同仓库的两个 VR/WebGPU 示例。以 WebXR 版 webxr_vr_rollercoaster.html 为例,环境的搭建顺序依次为:
- 生成起伏地形平面(
PlaneGeometry+ 随机扰动高度,配绿色MeshLambertMaterial); new TreesGeometry( mesh )依据地形网格用 Raycaster 向下投射生成树木;new SkyGeometry()生成云层(白色MeshBasicMaterial);- 用参数化曲线构造
RollerCoasterGeometry( curve, 1500 )、RollerCoasterLiftersGeometry、RollerCoasterShadowGeometry等轨道组件。
WebGPU 版本 webgpu_xr_rollercoaster.html 采用完全相同的 SkyGeometry 使用方式,说明该几何体与渲染后端无关,WebGL 与 WebGPU 渲染器均可直接消费其 BufferGeometry 数据。
在这些场景中,云层高度(y ∈ [50, 100))远高于过山车轨道与相机,配合renderer.xr.enabled = true与VRButton后,玩家在俯冲、翻转过程中能从不同角度看到远处漂浮的云朵,极大增强沉浸感——而这一切没有加载任何贴图或外部模型,全部由Math.random()实时生成。
总结
SkyGeometry是 three.js 官方附加模块RollerCoaster.js中一个无参构造、自包含的过程化天空(云朵)几何体,继承自BufferGeometry;- 其核心算法为 100 块随机面片:位置在 800×800 的水平范围内、高度 50~100、边长 20~60,每块为两个非索引三角形组成的轴对齐矩形;
- 它只提供
position属性,最佳搭配是无光照、无纹理的白色MeshBasicMaterial; - 官方用法集中于 webxr_vr_rollercoaster.html 与 webgpu_xr_rollercoaster.html 两个 XR 过山车示例,作为叠加在地形与轨道之上的远景天空装饰层。
若想查看该类的完整声明与同模块其他几何体,可直接阅读源码 examples/jsm/misc/RollerCoaster.js,文档页 docs/pages/SkyGeometry.html 也提供了与之对应的渲染版 API 参考。
【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考