news 2026/10/13 2:48:09

SpriteJS 3D 扩展之 Plane 平面元素:完整 API 解析与实战应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpriteJS 3D 扩展之 Plane 平面元素:完整 API 解析与实战应用
  • 图形学
  • 3D渲染

【免费下载链接】spritejs

A cross platform high-performance graphics system.

项目地址:https://gitcode.com/gh_mirrors/sp/spritejs
点击查看免费下载

Plane 是 SpriteJS Next 3D 扩展库(spritejs.ext3d)中用于创建三维平面网格元素的组件,继承自 Mesh3d。本文以 Plane API 文档 为骨架,结合仓库内 3D 示例、太阳系示例、文字标签示例 等真实源码,系统讲解 Plane 的构造函数、属性体系、只读属性、方法及典型应用场景,帮助你在 SpriteJS 的 WebGL 场景中熟练使用 Plane 搭建地面、背景、标签与动画元素。

一、Plane 是什么

Plane 是 SpriteJS 3D 扩展中的平面元素(Plane element),用于在 3D 图层中绘制一块由三角形构成的平面网格。它的继承关系为:

Plane extends Mesh3d extends Group3d extends Node3d extends Node

从源码结构看,3D 扩展文档 说明通过spritejs.ext3d访问全部 3D API,Plane 与 Cube、Sphere、Cylinder 等一起作为预置几何体元素提供。与 Mesh3d 直接接收自定义model(顶点数据)不同,Plane 会自动生成自身的平面几何体,你只需要通过width、height、widthSegments、heightSegments四个专属属性控制平面的大小与细分程度,即可直接用于渲染,无需手工书写顶点坐标。

在仓库的实际项目中,Plane 最常见的用法包括:

  • 作为地面 / 阴影接收面:见 阴影示例 与 骨骼动画示例;
  • 作为贴图平面 / 天体表面:见 太阳系示例;
  • 作为纹理文字标签:见 文字示例。

二、快速上手:创建第一个 Plane

2.1 引入 3D 扩展

Plane 属于 3D 扩展库。按 3D 扩展文档 说明,通过 CDN 加载时需保证扩展脚本位于 spritejs 脚本之后:

<script src="https://unpkg.com/spritejs@3/dist/spritejs.es.min.js"></script> <script src="https://unpkg.com/sprite-extend-3d/dist/sprite-extend-3d.js"></script>

若使用模块方式:

import {Scene} from 'spritejs'; import {Plane, shaders} from 'sprite-extend-3d';

2.2 最小可运行示例

参照 examples/test-3d.html 中的用法,创建一个带旋转动画的 Plane:

const {Scene} = spritejs; const container = document.getElementById('container'); const scene = new Scene({container, displayRatio: 2}); const layer = await scene.layer3d('fglayer', { camera: {fov: 35}, // 视野 35° }); const {Plane, Sphere, Cube} = spritejs.ext3d; layer.camera.attributes.pos = [5, 3, 6]; layer.camera.lookAt([0, 0, 0]); // 自定义着色器:仅输出法线光照效果 const program = layer.createProgram({ vertex, // 顶点着色器 fragment, // 片元着色器 cullFace: null, // 关闭背面剔除,保证平面双面可见 }); const plane = new Plane(program); plane.attributes.pos = [0, 1.3, 0]; layer.append(plane); plane.animate([ {rotateY: 0}, {rotateY: -360}, ], { duration: 10000, iterations: Infinity, });

要点说明:

  • layer.createProgram()基于当前 WebGL 上下文创建 GLProgram,Plane 渲染依赖它;
  • new Plane(program)即可得到一个宽高为 1×1 的默认平面;
  • 使用layer.append(plane)将元素加入 3D 图层,元素才会被渲染。

三、构造函数与专属属性

3.1 构造函数

new Plane(program, attrs)
  • program:由layer.createProgram()创建的 GLProgram 对象;
  • attrs:可选,属性配置对象,可直接传入下文表格中的任意属性。

3.2 专属属性:width / height / segments

Plane 定义了四个专属属性(不继承,默认值均为 1):

属性名类型默认值说明
widthnumber1元素的宽度
heightnumber1元素的高度
widthSegmentsnumber1宽度方向的细分段数
heightSegmentsnumber1高度方向的细分段数

用法说明:

  • width、height决定平面的几何尺寸。例如 太阳系示例 中创建“太阳”时,将宽高放大到 100:
const sun = new Plane(sunProgram); sun.attr({width: 100, height: 100}); layer.append(sun); layer.bindTime(sunProgram); // 让 uTime 随时间自动更新
  • widthSegments、heightSegments控制平面的顶点细分数量。文档未对该二参数给出详细说明;从同属 Mesh3d 子类的 Sphere(widthSegments/heightSegments控制经纬线细分,见 Sphere 文档)以及 Cylinder 文档 的同类参数可以推断,它们决定平面在宽、高方向上被分割为多少段,段数越高顶点越密,可承载更强的顶点级形变与法线细节,同时消耗更多显存与计算资源。需要平滑形变(如水面波动)时可将段数调高,普通静态地面保持默认值 1 即可。

3.3 一个注意点:动态更新几何

修改 Plane 的width/height/widthSegments/heightSegments会触发几何数据重建。这与 Plane 重写的两个方法密切相关:

  • onPropertyChange(key, newValue, oldValue):当元素属性值改变时执行的动作,是属性变化进入内部逻辑的入口;
  • remesh():重新创建元素的 Mesh 对象,即基于新属性重建平面网格。

这也是 Plane 从 Mesh3d 继承的setGeometry(model)、updateMesh()等方法的底层调用基础——属性变化最终会反映到 GPU 侧的顶点缓冲上。

四、完整属性表:继承属性体系

除上述四个专属属性外,Plane 还继承了来自 Mesh3d、Node3d、Node 的全部属性,完整如下:

属性名继承自类型默认值说明
modeMesh3dstringTRIANGLES设置绘制模式
colorsMesh3dColor#80808080每个顶点的颜色
colorDivisorMesh3dnumber3每个 color 值赋给几个顶点
xNode3dnumber0元素 X 轴坐标
yNode3dnumber0元素 Y 轴坐标
zNode3dnumber0元素 Z 轴坐标
posNode3dArray[0, 0, 0][x, y, z] 简写
rotateXNode3dnumber0绕 X 轴旋转
rotateYNode3dnumber0绕 Y 轴旋转
rotateZNode3dnumber0绕 Z 轴旋转
rotateNode3dArray[0, 0, 0][rotateX, rotateY, rotateZ] 简写
scaleXNode3dnumber1X 轴拉伸
scaleYNode3dnumber1Y 轴拉伸
scaleZNode3dnumber1Z 轴拉伸
scaleNode3dArray[1, 1, 1][scaleX, scaleY, scaleZ] 简写
raycastNode3dstringundefined碰撞检测模式:默认检测 bounds,设为sphere则进行球形检测
visibilityNode3denumenum{visible, hidden}元素是否可见
idNodestring''设定元素的 ID
nameNodestring''设定元素的 name
classNameNodestring''设定元素的 className
displayNodestring''设定元素的可见性
pointerEventsNodestringvisible同 CSS 的 pointerEvents

4.1 渲染相关:mode / colors / colorDivisor

  • mode:GL 绘制模式,默认TRIANGLES。渲染层面,Plane 的几何体以三角形为单位组织顶点,可直接改用TRIANGLE_STRIP、LINES等其他模式输出特殊效果;
  • colors:每个顶点的颜色,默认#80808080(半透明灰)。支持单个颜色字符串,也支持按顶点数量提供颜色数组(如'red blue green orange',参照 3D 扩展文档 中 Mesh3d 的用法);
  • colorDivisor:每条颜色值覆盖的顶点数,默认 3(即一个三角形 3 个顶点共享一个颜色值)。

4.2 变换相关:位置 / 旋转 / 缩放

来自 Node3d 的变换属性是最常用的操作入口,推荐使用简写形式批量设置。例如把平面平铺成“地面”并放大:

// 阴影示例:作为地面接收阴影 const ground = new Plane(program2, { rotateX: 90, // 从竖直面旋转为水平地面 scale: 6, // 放大 6 倍 y: -3, // 下沉到 y = -3 }); layer.append(ground);

参见 demos/3d/shadow/index.js。其中rotateX: 90是最经典的手法——Plane 默认位于 XOY 平面(法线朝 Z 轴),绕 X 轴旋转 90° 后变为水平面,从而作为地面承接阴影。

4.3 拾取与可见性

  • raycast:碰撞检测模式。文档说明默认检测包围盒(bounds),设为sphere则进行球形检测。需配合图层的layer.setRaycast()启用拾取,才能让 Plane 响应鼠标/触屏事件(详见本文第七节);
  • visibility:visible/hidden,控制元素是否参与渲染;
  • pointerEvents:同 CSS 语义,默认visible,控制元素是否接收指针事件。

五、只读属性(Properties)

Plane 的只读属性全部来自继承,按继承层级组织如下。

5.1 来自 Mesh3d

只读属性说明
geometry获取元素的几何体对象
meshes当前元素及其子孙元素的 Mesh 对象列表
model获取几何数据
program获取 GLProgram

5.2 来自 Group3d

只读属性说明
childNodeschildren 的别名
children子元素
meshes当前元素及其子孙元素的 Mesh 对象列表

Plane 继承自 Group3d,因此它本身也是一个三维分组容器,可以append其他 3D 元素作为子节点(参见 Group3d 文档)。

5.3 来自 Node3d

只读属性说明
body当前元素的 Mesh 或 Transform 对象
isVisible元素是否可见
localMatrix当前元素的变换矩阵
matrixlocalMatrix 的别名
mesh当前元素的 Mesh 对象,如果没有则返回 null
modelViewMatrix经过相机换算后的变换矩阵
normalMatrix法向变换矩阵(3×3),用于计算光照
renderMatrix相对于 layer 的变换矩阵
worldMatrixrenderMatrix 的别名
zDepth元素相对 z 轴的深度

这些矩阵在自定义着色器中常被作为 uniform 使用:例如 PBR 示例 的顶点着色器通过uniform mat4 modelMatrix / modelViewMatrix / projectionMatrix与uniform mat3 normalMatrix完成顶点变换与法线换算,与这里的矩阵体系一一对应。

5.4 来自 Node

只读属性说明
ancestors返回当前元素的祖先元素列表
animations返回当前元素执行中的所有动画
layer返回当前绘制上下文中的 Layer 对象
parent返回当前对象的父对象
renderer返回当前绘制上下文中的渲染对象
zOrder返回当前对象被添加到对象树上的次序
attributes返回当前元素的属性对象
className相当于 element.attributes.className
id相当于 element.attributes.id
name相当于 element.attributes.name
zIndex相当于 element.attributes.zIndex

六、方法体系

6.1 Plane 自身重写的方法

方法说明
onPropertyChange(key, newValue, oldValue)当元素属性值被改变时,执行的动作
remesh()重新创建 Mesh 对象

这两个方法分别负责属性变化的响应与网格重建,是 Plane 在尺寸/细分属性变化时保持几何正确性的核心机制。

6.2 继承自 Mesh3d

方法说明
addEventListener(type, listener, options = {})注册事件监听器
cloneNode(deep = false)复制一个元素,deep 为 true 时同时复制子元素
removeAllListeners(type)移除某类型的所有事件监听器
removeEventListener(type, listener, options)移除事件监听器
setGeometry(model = this[_model])设置或更新几何信息
setProgram(program)设置或更新 GLProgram
updateMesh()设置或更新 Mesh 信息

6.3 继承自 Group3d(分组与查询)

方法说明
append(...els)批量添加元素到 group 中
appendChild(el)将一个元素添加到 group 中
getElementById(id)返回指定 id 的子元素
getElementsByClassName(className)返回指定 className 的子元素列表
getElementsByName(name)返回指定 name 的子元素列表
getElementsByTagName(name)返回指定类型的子元素列表
insertBefore(el, ref)将指定元素插入到 ref 元素之前;ref 为 null 时追加到末尾;ref 非 null 且不是 group 子元素时抛出异常
querySelctor(selector)根据选择器返回指定的子元素
querySelectorAll(selector)根据选择器返回所有匹配的子元素列表
replaceChild(el, ref)用 el 替换 ref;ref 不在当前 group 中时抛出异常
removeAllChildren()移除 group 的所有子元素
removeChild(el)将指定元素移出 group
setResolution({width, height})设置元素的上下文分辨率

6.4 继承自 Node3d

方法说明
connect(parent, zOrder)元素被添加到对象树上时调用,parent 与 zOrder 被赋给元素
disconnect()元素从对象树上移除时调用,移除 parent 与 zOrder 属性
lookAt([x, y, z])让元素转向到对应坐标所在的方向
setBody(body, update = true)设置元素的 Mesh 对象
traverse(callback)遍历当前元素和所有的子孙元素
updateMatrix()更新 matrix
updateMatrixWorld(force = false)更新 worldMatrix

6.5 继承自 Node

方法说明
activateAnimations()激活元素上正在执行的所有动画
animate(frames, timing)执行动画
attr(...args)读取或批量设置属性
cloneNode()复制整个元素
deactivateAnimations()停止元素上正在执行的所有动画
dispatchEvent(event)转发一个自定义事件
dispatchPointerEvent(event)转发一个鼠标或触屏事件
forceUpdate()强制重绘画布
getAttribute(key)读取元素属性值
getListeners(type, {capture = false} = {})获取事件监听器
getOffsetPosition(x, y)将相对于 Layer 的指定 [x, y] 坐标变换为相对于当前元素的坐标,以锚点为原点
getResolution()获取元素的上下文分辨率
isPointCollision(x, y)判断事件坐标是否与元素相交
setAttribute(key, value)设置元素属性值
setMouseCapture()捕获鼠标
setResolution({width, height})设置元素的上下文分辨率
releaseMouseCapture()释放鼠标
remove()将元素从 parent 上移除
removeAttribute(key)移除元素属性值,恢复为默认值
transition(sec, easing = 'linear')创建过渡动画

常用组合技巧:attr({...})一次设置多个属性;animate()与transition()驱动动画;cloneNode()复制平面做实例化复用;remove()从场景中摘除元素。

七、实战场景详解

7.1 地面与阴影接收面

Plane 最常见的用途是作为地面。在 阴影示例 中,地面 Plane 配合阴影系统工作:

const ground = new Plane(program2, { rotateX: 90, // 平铺为地面 scale: 6, y: -3, }); layer.append(ground); shadow.add(plane); // 将动态模型加入阴影 shadow.add(ground); // 地面参与阴影 layer.setShadow(shadow);

骨骼动画示例 中同样的手法用于承接角色阴影:

const plan = new Plane(shadowProgram, {width: 7, height: 7}); plan.attr({rotateX: -90, y: -1});

这里rotateX取-90与90效果一致(平面翻转 180° 无差异),关键都是把竖直的默认平面旋转为水平面。

7.2 贴图平面:太阳与天体表面

太阳系示例 中,Plane 配合纹理与自定义噪声着色器绘制一个 100×100 的“太阳”:

const texture = layer.createTexture({ image: '...', wrapS: layer.gl.REPEAT, }); const sunProgram = layer.createProgram({ transparent: true, vertex: shaders.TEXTURE.vertex, fragment, // 程序化噪声片元着色器 cullFace: null, texture, uniforms: { specularIntensity: {value: 0.5}, uTime: {value: 0}, }, }); const sun = new Plane(sunProgram); sun.attr({width: 100, height: 100}); layer.append(sun); layer.bindTime(sunProgram); // 每帧自动更新 uTime,驱动噪声动画

由此可知,Plane 是纯几何容器,外观完全由 program 决定:换一张纹理、换一组着色器,同一个 Plane 就能渲染成太阳、水面或任意贴图平面。

7.3 纹理文字标签(Billboard)

文字示例 中,Plane 按纹理图片的宽高比自适应尺寸,并隐藏网格颜色,作为文字标签贴片:

const label = new Plane(program, { width: 1, height: texture.image.height / texture.image.width, // 保持宽高比 colors: 'transparent', // 顶点颜色透明,仅显示纹理 });

这是 3D 场景中常见的“公告板(Billboard)”做法:用一张带透明通道的文字纹理贴在平面上,再把colors设为transparent,即可在场景中显示不依赖字体渲染的文字或图标。

7.4 动画与旋转

Plane 继承的动画 API 可直接驱动平面旋转。除 examples/test-3d.html 中的animate()外,阴影示例 还在layer.tick回调中逐帧修改属性:

layer.tick((t) => { if(plane) { plane.attributes.z = Math.sin(t * 0.001); plane.attributes.rotateX = Math.sin(t * 0.001 + 2) * 18; plane.attributes.rotateY = Math.sin(t * 0.001 - 4) * -18; } });

两种方式分别适合“关键帧动画”与“程序化动画”两类需求。

八、与 Plane 相关的体系位置

  • 基类 Mesh3d:Plane 的渲染基座。mode、colors、colorDivisor及几何/程序管理方法均定义于此,详见 Mesh3d 文档;
  • 父容器 Group3d:Plane 可作为分组容器嵌套其他 3D 元素,详见 Group3d 文档;
  • 变换基座 Node3d:坐标、旋转、缩放及矩阵体系,详见 Node3d 文档;
  • 创建环境 Layer3d:scene.layer3d()、layer.createProgram()、layer.createTexture()等均在 Layer3d 文档 中有完整参数说明;
  • 同族几何体:Sphere(文档)、Cylinder(文档)与 Plane 使用相同属性体系,可相互对照学习;
  • 整体入门:坐标系统、相机、光照、交互等前置知识见 3D 扩展文档。

九、小结

Plane 是 SpriteJS 3D 扩展中最常用的基础几何元素:通过width/height控制尺寸、widthSegments/heightSegments控制细分、rotateX: 90平铺成地面,再结合 Mesh3d 的颜色体系、Node3d 的变换体系与 Node 的动画体系,即可快速搭建地面、贴图平面、文字标签和程序化动画等各类 3D 效果。实际使用中可对照本仓库的 demos/3d 目录 下的各示例代码,理解 Plane 与着色器、纹理、阴影和动画的组合方式。

  • 图形学
  • 3D渲染

【免费下载链接】spritejs

A cross platform high-performance graphics system.

项目地址:https://gitcode.com/gh_mirrors/sp/spritejs
点击查看免费下载
上一篇:WechatDecrypt终极指南:5步轻松解密微信加密数据库,重新掌控聊天记录
下一篇:WechatDecrypt微信解密工具:3步专业解密微信加密数据库,重获聊天记录控制权

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 2:48:01

OAuth2.0认证体系搭建:Swagger与WireMock的RESTful API调试准备

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 2:47:12

语义分割各类别mIoU计算详解:从混淆矩阵到PyTorch实战与避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 2:46:59

AIS数据解析全流程:从串口驱动到船舶轨迹可视化

简介&#xff1a;本资源是一套面向嵌入式开发与海洋信息化方向初学者的AIS系统实践入门材料&#xff0c;聚焦自动识别系统的核心技术链&#xff1a;协议理解、信号解码、设备驱动开发与数据解析应用。资源共3个文件&#xff0c;含2个文本说明文档&#xff08;涵盖ITU-R M.1371报…

作者头像 李华