deck.gl SimpleMeshLayer 入门实战:在三维地图中批量渲染任意 3D 模型
2026/9/15 12:14:31 网站建设 项目流程

deck.gl SimpleMeshLayer 入门实战:在三维地图中批量渲染任意 3D 模型

【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl

本教程以仓库中的 mesh 最小示例 为骨架,系统讲解 deck.glSimpleMeshLayer的安装、数据格式、核心配置与源码级渲染原理。读完你将从零搭建一个在三维空间中批量摆放、旋转、缩放并着色任意 OBJ/PLY 等网格模型的 deck.gl 应用,并理解其底层如何把每个实例的位置、朝向与缩放矩阵写入 GPU 顶点属性。

示例概览:一个最小可运行的 SimpleMeshLayer 应用

仓库中的examples/website/mesh是一个刻意保持精简的 deck.gl 示例,仅由 4 个文件组成:

  • README.md:示例说明与运行方式
  • app.jsx:核心应用代码(React +@deck.gl/react
  • index.html:页面入口,挂载#app容器
  • package.json:依赖与脚本声明

其核心思想一句话概括:SimpleMeshLayer把同一个 3D 几何体(mesh)在数据流中渲染出大量实例,每个实例拥有独立的位置、颜色、朝向与缩放。官方 API 文档给出的典型场景是"在地图上可视化一支各有位置和朝向的 3D 车队"(见 SimpleMeshLayer 文档)。

快速上手:安装、运行与依赖

examples/website/mesh目录内容复制到你的项目后,按 README 中的方式安装依赖并启动:

# 安装依赖 npm install # 或使用 yarn yarn # 使用 vite 打包并启动开发服务器 npm start

查看 package.json 可以看到示例的依赖构成:

  • deck.gl(^9.0.0):聚合了 core 与各图层模块的入口包
  • @loaders.gl/obj(^4.4.3):用于解析 OBJ 格式模型的 loader
  • @math.gl/core(^4.1.0):数学工具
  • react/react-dom(^18.0.0):渲染层
  • vite(^7.3.3):构建与开发服务器

脚本定义与运行方式一一对应:start对应vite --openstart-local则复用仓库根目录的本地开发配置(vite --config ../../vite.config.local.mjs),build执行vite build产出静态文件。

如果不想使用 React,文档也提供了 NPM 安装与预打包脚本两种接入方式(见 simple-mesh-layer.md):

npm install deck.gl # 或按需安装 npm install @deck.gl/core @deck.gl/mesh-layers

使用预打包脚本(pre-bundled scripts)时:

<script src="https://unpkg.com/deck.gl@^9.0.0/dist.min.js"></script>

然后通过全局命名空间使用:new deck.SimpleMeshLayer({})

数据格式:mesh 与 loaders.gl

README 明确指出,示例所用的 OBJ 模型来自 John Burkardt 的数据样例集;而真正让 SimpleMeshLayer 可以加载各类 3D 格式的关键是 loaders.gl——它提供了与 deck.gl 无缝协作的一系列通用 3D 格式 loader。

在示例中,app.jsx 先注册 loader,再通过 URL 加载模型:

import {OBJLoader} from '@loaders.gl/obj'; import {registerLoaders} from '@loaders.gl/core'; // 把处理 mesh 格式的 loader 注册进全局 registerLoaders([OBJLoader]); const MESH_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/mesh/minicooper.obj';

随后把这个 URL 直接传给图层的mesh属性即可。换成 PLY 等其他格式同样简单(见官方文档的 PLY 示例):

import {SimpleMeshLayer} from '@deck.gl/mesh-layers'; import {PLYLoader} from '@loaders.gl/ply'; new SimpleMeshLayer({ mesh: 'path/to/model.ply', loaders: [PLYLoader] });

从 simple-mesh-layer.ts 源码 可以看出,meshtexture都被声明为{type: 'object', value: null, async: true},即异步属性:传入 URL 字符串后,deck.gl 会借助已注册的 loader 异步解析。实际上mesh属性接受三种形态:

  1. 指向网格描述文件的 URL(格式需被 loaders.gl 支持,并注册对应 loader);
  2. 一个 luma.gl 的Geometry实例;
  3. 一个包含positions(Float32Array,相对物体中心的三维顶点偏移,单位为米)、normals(三维法线)、texCoords(二维纹理坐标)的普通对象。

源码中的normalizeGeometryAttributes(simple-mesh-layer.ts)展示了网格数据的归一化逻辑:它会同时兼容小写(positions/normals/texCoords)与 loaders.gl 惯例的大写(POSITION/NORMAL/TEXCOORD_0/COLOR_0)属性名,缺失的colorsnormalstexCoords会被自动填充为占位数组(颜色填 1、法线填 0、纹理坐标填 0),保证着色器总能拿到完整的顶点属性。

核心配置参数详解

以下参数均出自 SimpleMeshLayer 官方 API 文档,并可由 defaultProps 逐一印证默认值与类型。

mesh

  • 类型:string | object
  • 作用:每个数据对象要渲染的几何体,取值方式见上文三种形态。

texture 与 textureParameters

  • texture:默认null。网格的纹理。接受字符串(URL 或 Data URL)、WebGL2 合法像素源、luma.glTexture实例,或可传给Texture构造器的普通对象(如{width, height, data}),也接受解析到以上类型的 Promise。当texture存在时用它渲染几何体;否则使用getColor获取的对象颜色。
  • textureParameters:自定义纹理采样参数。不指定时使用线性平滑插值的默认值:
{ minFilter: 'linear', magFilter: 'linear', mipmapFilter: 'linear', addressModeU: 'clamp-to-edge', addressModeV: 'clamp-to-edge' }

在源码中,texture同样被标记为async: true,且未提供纹理时会创建一个 1x1 的emptyTexture占位,以避免 luma.gl 的缺省 uniform 警告(simple-mesh-layer.ts)。

渲染选项

参数默认值说明
sizeScale1每个几何体的整体缩放倍数(min: 0),支持过渡动画
wireframefalse是否以线框模式渲染 mesh
materialtrue应用于挤出多边形上的光照材质属性,配合光照效果(LightingEffect)使用,可配置项参见 使用光照指南

其中sizeScale在顶点着色器中直接对局部坐标做乘法:vec3 pos = (instanceModelMatrix * positions) * simpleMesh.sizeScale + instanceTranslation;(见 simple-mesh-layer-vertex.glsl.ts)。而wireframe的实现也相当直白——源码在每次状态更新时按需切换图元拓扑:

this.state.model.setTopology(this.props.wireframe ? 'line-strip' : 'triangle-list');

不过需要留意源码注释的提醒:这是一种"快速而粗糙"的线框实现(用 LINE_STRIP 重画同一份 mesh),并不会严格沿原始网格的边绘制。

数据访问器(Accessors)

访问器默认值说明
getPositionobject => object.position获取每个对象在数据流中的中心锚点位置,支持过渡动画
getColor[0, 0, 0, 255]若 mesh 不含顶点色,用它渲染每个对象;若含顶点色则二者混合;设为[255,255,255]保留原始颜色;指定texture后二者都被忽略。颜色格式[r,g,b,[a]],通道取值 0-255
getOrientation[0, 0, 0]物体朝向,欧拉角[pitch, yaw, roll],单位为度,会与图层的modelMatrix复合
getScale[1, 1, 1]沿各轴的缩放因子
getTranslation[0, 0, 0]相对getPosition锚点的平移,[x, y, z]单位为米
getTransformMatrixnull显式提供 4x4 列主序模型矩阵,一旦提供将覆盖getOrientationgetScalegetTranslation

每个访问器都遵循 deck.gl 的统一规则:传入数组则对所有对象生效(常量),传入函数则对每个对象分别求值。示例 app.jsx 用程序化生成的 10×10 网格数据演示了这种用法,每个数据点携带positioncolororientation三个字段:

const SAMPLE_DATA = (([xCount, yCount], spacing) => { const data = []; for (let x = 0; x < xCount; x++) { for (let y = 0; y < yCount; y++) { data.push({ position: [(x - (xCount - 1) / 2) * spacing, (y - (yCount - 1) / 2) * spacing], color: [(x / (xCount - 1)) * 255, 128, (y / (yCount - 1)) * 255], orientation: [(x / (xCount - 1)) * 60 - 30, 0, -90] }); } } return data; })([10, 10], 120);

这里 120 是实例间距,10×10 共 100 辆"迷你库珀"(minicooper.obj),每个实例的朝向随 x 坐标在 -30° 到 30° 之间渐变,视觉上形成整齐又富有变化的阵列。

源码级原理:实例化矩阵的生成与坐标系统

这是 SimpleMeshLayer 最值得深挖的实现细节。从 initializeState 可以看到,图层在 AttributeManager 中注册了三类实例化(instanced)属性

  • instancePositions:float64 类型(支持高精度 64 位坐标),由getPosition驱动;
  • instanceColors:unorm8 类型,由getColor驱动;
  • instanceModelMatrix:一个 12 元素复合属性,由getOrientationgetScalegetTranslationgetTransformMatrix四个访问器共同驱动。

顶点着色器(simple-mesh-layer-vertex.glsl.ts)中这 12 个元素被拆解为三个 3 分量矩阵列向量instanceModelMatrixCol0/1/2加上instanceTranslation,完成"旋转 × 缩放 → 平移"的实例变换。

四者如何协同由 matrix.ts 中的MATRIX_ATTRIBUTES.update决定:

  • 若提供了getTransformMatrix(16 元素矩阵),直接取其 3×3 旋转缩放部分与 3 分量平移(getExtendedMat3FromMat4),完全忽略另外三个访问器;
  • 否则用calculateTransformMatrix把欧拉角(角度制,内部乘以Math.PI / 180转弧度)与缩放因子合成旋转缩放矩阵,再把getTranslation写入矩阵第 4 列。

更关键的细节在shouldComposeModelMatrix(matrix.ts):只有当坐标系为cartesianmeter-offsets,或默认坐标系且视口非地理空间时,实例变换才会与图层modelMatrix的旋转部分复合。在 LNGLAT / LNGLAT_OFFSET(经纬度)坐标系下该复合会被禁用,因为"旋转经纬度坐标"没有物理意义。这也是示例特意使用OrbitView+coordinateSystem: 'cartesian'的原因——在一个纯三维欧氏空间中展示模型阵列,避免地理坐标带来的投影复杂性(app.jsx)。

此外,着色器中的flatShading开关(由网格是否含法线决定)与hasTexture标志(由是否传入纹理决定)共同控制片元着色阶段的明暗与纹理采样,仓库中的 WebGPU 测试(test/modules/mesh-layers/simple-mesh-layer.spec.ts)也验证了无纹理与带纹理两种路径在 WebGPU 设备上均能正常初始化图层与模型。

组合示例:光照、阴影与地面

示例没有停留在"只画一层 mesh",还展示了两个重要的组合技巧(app.jsx):

  1. 光照效果:创建AmbientLight(环境光)与DirectionalLight(平行光,开启_shadow: true投影)组成LightingEffect,挂到DeckGLeffects上——注意 WebGPU 模式下跳过光照(isWebGPU ? [] : [lightingEffect]),因为该路径暂未启用相关效果;
  2. 阴影接收地面:用一个SolidPolygonLayer绘制透明大平面(范围 -1000 到 1000,z=-40),作为阴影投射的承接面,与 SimpleMeshLayer 一起使用coordinateSystem: 'cartesian'

OrbitView配合initialViewStaterotationXrotationOrbitfov: 30zoom: 0)提供围绕目标点旋转观察的 3D 视口,near: 0.1, far: 2设置了相机裁剪平面。

扩展阅读

  • 官方 API 参考:SimpleMeshLayer 文档
  • 完整源码:modules/mesh-layers/src/simple-mesh-layer
  • 模块导出入口:modules/mesh-layers/src/index.ts(同时导出SimpleMeshLayerScenegraphLayer,后者用于加载 glTF 场景图)
  • 测试用例:test/modules/mesh-layers/simple-mesh-layer.spec.ts
  • 教程配套示例目录:examples/website/mesh

【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询