资讯详情

PixiJS v8 Mesh 完全指南:从 MeshGeometry 到自定义 Shader 的顶点级渲染

📅 2026/9/18 15:17:18 | 华诺云谱 👁 阅读
PixiJS v8 Mesh 完全指南:从 MeshGeometry 到自定义 Shader 的顶点级渲染
PixiJS v8 Mesh 完全指南从 MeshGeometry 到自定义 Shader 的顶点级渲染【免费下载链接】pixijsThe HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.项目地址: https://gitcode.com/gh_mirrors/pi/pixijs导读Mesh是 PixiJS 中能力最底层的可渲染对象它把一份Geometry顶点位置、UV、索引与拓扑和一张Texture或自定义Shader组合起来渲染任意 2D 甚至透视投影图形。当你的几何形状无法用 Sprite / Graphics / ParticleContainer 表达、需要逐顶点控制或自定义着色器时Mesh就是首选方案。读完本文你将掌握 v8 中Mesh/MeshGeometry的完整用法构造选项、五种拓扑、批处理规则、几何共享、运行时顶点更新以及常见的 v8 迁移陷阱。Quick Start一个带纹理的四边形const texture await Assets.load(pattern.png); const geometry new MeshGeometry({ positions: new Float32Array([0, 0, 100, 0, 100, 100, 0, 100]), uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), indices: new Uint32Array([0, 1, 2, 0, 2, 3]), topology: triangle-list, }); const mesh new Mesh({ geometry, texture, roundPixels: false, }); app.stage.addChild(mesh);Mesh是一个叶子节点leafallowChildren为false见 Mesh.ts因此不能像Container那样往里添加子对象。它持有对一份Geometry的引用该 Geometry 可以被多个 Mesh 共享并且要么使用一张纹理、要么使用一个自定义 Shader。在渲染管线中Mesh通过renderPipeId mesh注册到MeshPipe见 MeshPipe.tsWebGL 与 WebGPU 后端分别由GlMeshAdaptor与GpuMeshAdapter实现。构造选项new MeshGEOMETRY, SHADER(options: MeshOptionsGEOMETRY, SHADER)—— v8 只接受选项对象形式。Option类型默认值说明geometryGEOMETRY extends Geometry—顶点缓冲、索引、UV 与拓扑。必填且可被多个 Mesh 共享。shaderSHADER extends Shader \| nullnull自定义顶点/片元着色器。一旦设置网格将跳过批处理由着色器自行决定采样什么。stateStateState.for2d()GPU 状态混合、深度、裁剪。开启深度测试或裁剪会禁用批处理。textureTextureTexture.WHITE默认着色器采样的纹理。若自定义shader自带纹理则该选项被忽略。roundPixelsbooleanfalse渲染时将x/y吸附到整数像素。从源码可以看到完整的赋值逻辑Mesh.tsconst { geometry, shader, texture, roundPixels, state, ...rest } options; super({ label: Mesh, ...rest }); this.allowChildren false; this.shader shader ?? null; this.texture texture ?? (shader as unknown as TextureShader)?.texture ?? Texture.WHITE; this.state state ?? State.for2d(); this._geometry geometry; this._geometry.on(update, this.onViewUpdate, this); this.roundPixels roundPixels ?? false;值得注意的两个细节当传入自定义 shader 而未显式传texture时构造函数会从shader.texture继承纹理三者都缺失时回退到Texture.WHITE。运行时把null/undefined赋给texturesetter 会被强制转换为Texture.EMPTYMesh.ts同时纹理的update事件与onViewUpdate挂钩动态纹理currentTexture.dynamic更新时会自动触发视图重绘。此外所有Container选项position、scale、tint、label、filters、zIndex等在这里同样有效详见 constructor-options.md。MeshOptions在类型上直接extends ContainerOptions这一点在 Mesh.ts 的接口定义中可以验证。核心模式MeshGeometry 的结构剖析MeshGeometry是Mesh的标准几何类本质上是三个 GPU Buffer 与两个顶点属性的封装。构造时传入的每个字段都可以省略const geometry new MeshGeometry({ positions: new Float32Array([ 0, 0, // vertex 0: x, y 100, 0, // vertex 1 100, 100, // vertex 2 0, 100, // vertex 3 ]), uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), indices: new Uint32Array([0, 1, 2, 0, 2, 3]), topology: triangle-list, });positions局部空间local space中扁平的 x,y 成对数组。uvs[0, 1]纹理空间中的扁平 u,v 成对数组。省略时默认用零填充长度与 positions 相同此时纹理只会采样(0, 0)这一个像素。indices指向顶点数组的三角形索引。省略时默认退化为四边形索引[0, 1, 2, 0, 2, 3]。topology索引的解析方式见下文。在 MeshGeometry.ts 的源码中默认行为被明确实现positions缺省时使用[0, 0, 1, 0, 1, 1, 0, 1]的单位四边形uvs缺省时用new Float32Array(positions.length)填零indices缺省时使用[0, 1, 2, 0, 2, 3]。构造时创建的三个 Buffer 分别标记为attribute-mesh-positions、attribute-mesh-uvs和index-mesh-bufferaPosition与aUV的顶点格式均为float32x2。构造完成后可以通过 getter 读取或替换类型化数组geometry.positions、geometry.uvs、geometry.indices。它们直接返回底层 Buffer 的data见 MeshGeometry.ts原地修改等价于修改getBuffer(aPosition).data。也就是说geometry.positions[0] 5与geometry.getBuffer(aPosition).data[0] 5操作的是同一块内存。另外MeshGeometryOptions还额外支持一个shrinkBuffersToFit选项默认false设为true时创建 Buffer 会收缩到恰好容纳数据适合静态几何节省显存。拓扑Topologynew MeshGeometry({ positions, uvs, indices, topology: triangle-list });渲染器共支持五种拓扑类型定义位于 const.tsTopology含义triangle-list默认每 3 个索引构成一个独立三角形triangle-strip每个新索引与前两个构成一个三角形形成带状延伸line-list每对索引构成一条独立线段line-strip连续相连的线段point-list每个索引渲染一个点拓扑设置在geometry 上而不是 mesh 上。默认是triangle-list如果你的数据是 strip、line 或 point 布局却没有显式声明拓扑mesh 会渲染出完全错误的结果garbage。另外v7 时代的DRAW_MODES.POINTS等常量在 v8 已被弃用并通过 Proxy 自动映射到新的字符串拓扑const.ts迁移时直接写字符串即可。Mesh的命中检测containsPoint也会依据拓扑工作对triangle-strip步长为 3其余拓扑步长为 1逐三角形调用pointInTriangle判定Mesh.ts。批处理Batchinggeometry.batchMode auto; // 默认 geometry.batchMode batch; // 始终批处理 geometry.batchMode no-batch; // 永不批处理一个 mesh 只有同时满足以下条件才会被合批与其他绘制调用合并成一次 draw call使用的是MeshGeometry而非自定义 Geometry 子类没有自定义shader其state未开启深度测试或裁剪源码中用(this.state.data 0b001100) ! 0判定见 Mesh.ts在auto规则下顶点数不超过 100判定条件是this._geometry.positions.length / 2 100。自定义 shader 总是独立渲染。对于顶点数多、形状基本不变的静态 mesh可以显式设batchMode no-batch跳过每帧的合批资格检查开销。批处理的执行路径也值得了解MeshPipe.addRenderable在 mesh 可批处理时创建BatchableMesh并调用batcher.addToBatch(...)否则调用batcher.break(...)后把 mesh 作为独立指令加入指令集MeshPipe.ts。BatchableMesh通过packAsQuad false声明自己是非四边形元素BatchableMesh.ts其attributeSize由顶点数推导indexSize取自索引数组长度。MeshPipe的本地 uniform 包含uTransformMatrix、uColor与uRoundMeshPipe.ts逐帧将变换矩阵与groupColorAlpha写入 uniform 再交给适配器执行。自定义 Shaderimport { Shader } from pixi.js; const shader Shader.from({ gl: { vertex: vertSrc, fragment: fragSrc }, resources: { uTexture: texture.source, uSampler: texture.source.style }, }); const mesh new Mesh({ geometry, shader });提供了 shader 之后texture变成可选项——采样什么完全由着色器决定且自定义 shader 会绕过批处理管线始终单独渲染。完整着色器编写细节参见pixijs-custom-renderingskilluniform-types.md仓库中还提供了可运行的示例如 mesh_shared-shader多个 mesh 共享同一份 shader与 mesh_custom_shader_geometry自定义 geometry shader 渲染三角形。共享几何Shared geometryconst sharedGeom new MeshGeometry({ positions, uvs, indices }); const mesh1 new Mesh({ geometry: sharedGeom, texture: tex1 }); const mesh2 new Mesh({ geometry: sharedGeom, texture: tex2 });Geometry 是引用计数的多个 Mesh 可以共享同一份几何数据从而在“形状相同、但变换或纹理不同”的多实例场景下节省显存与上传开销。MeshOptions.geometry的文档注释也明确写着 Can be shared between multiple Mesh objectsMesh.ts。仓库示例 mesh_shared-geometry 演示了这份用法。同样地shader也可在多个 Mesh 间共享。运行时更新顶点mesh.geometry.positions[1] Math.sin(performance.now() / 500) * 20; mesh.geometry.getBuffer(aPosition).update();MeshGeometry暴露的positions、uvs、indicesgetter 直接返回底层Float32Array/Uint32Array与getBuffer(aPosition).data/getBuffer(aUV).data/getIndex().data指向同一份数据。常规做法是原地修改也可以整体换新数组setter 执行buffer.data value不做长度检查真正的不变量是uvs.length positions.length保证每个顶点都有 UV见 MeshGeometry.ts。修改完data数组后必须调用对应 Buffer 的update()才会把改动推送到 GPU。如果希望自动更新请使用MeshSimple——它内部在每次onRender时自动调用getBuffer(aPosition).update()见 MeshSimple.ts非常适合逐帧动画顶点位置。仓库中的 mesh_mouse-trail 与 mesh_rope_textured 就是典型的“每帧改顶点 手动/自动上传”场景。常见错误[HIGH] 使用位置参数构造错误写法const mesh new Mesh(geometry, shader);正确写法const mesh new Mesh({ geometry, shader });v8 全面使用选项对象。位置参数形式已弃用debug 构建下会打印 deprecation 警告见 Mesh.ts。注意drawMode参数也已被移除请改用geometry.topology。同样地MeshGeometry的旧式位置参数构造new MeshGeometry(positions, uvs, indices)也已弃用MeshGeometry.ts。[HIGH] 误用vertices而不是positions错误写法const geometry new MeshGeometry({ vertices: new Float32Array([0, 0, 100, 0, 50, 100]), });正确写法const geometry new MeshGeometry({ positions: new Float32Array([0, 0, 100, 0, 50, 100]), });MeshGeometry的选项名是positions。vertices只是MeshSimple使用的便捷名称——MeshSimple构造时会内部转换成positionsMeshSimple.ts并暴露mesh.verticesgetter/setterMeshSimple.ts。[MEDIUM] 忘记设置拓扑错误写法const geometry new MeshGeometry({ positions: stripPositions, indices: stripIndices, });正确写法const geometry new MeshGeometry({ positions: stripPositions, indices: stripIndices, topology: triangle-strip, });默认拓扑是triangle-list。当数据是 strip 或 line 布局时不显式声明拓扑就会渲染出乱码。[MEDIUM] 带纹理的 Mesh 忘记提供 UVMeshSimple同样有此陷阱省略uvs会让底层MeshGeometry用零填充 UV 数组最终只能采样纹理的(0, 0)像素。始终在需要正确纹理映射时提供与vertices匹配的uvs。补充MeshSimple —— 快速几何的便捷封装虽然本指南以Mesh为主体但与之配套的MeshSimpleMesh的薄封装子类源码位于 MeshSimple.ts值得一并掌握它是快速纹理三角形/四边形 逐帧动画顶点的最短路径const triangle new MeshSimple({ texture, vertices: new Float32Array([0, 0, 100, 0, 50, 100]), uvs: new Float32Array([0, 0, 1, 0, 0.5, 1]), topology: triangle-list, }); app.stage.addChild(triangle);MeshSimple在构造时用definedProps过滤空值后内部构建MeshGeometry因此选项类型SimpleMeshOptions通过OmitMeshOptions, geometry剔除了geometry字段并默认autoUpdate true。由于默认自动更新直接赋值triangle.vertices verts或原地修改数组即可下一帧渲染时会自动上传设置triangle.autoUpdate false后则需要手动调用triangle.geometry.getBuffer(aPosition).update()。它同样支持全部五种拓扑例如topology: line-strip即可快速绘制折线。需要注意的是v7 的SimpleMesh在 v8 中已更名为MeshSimple旧名称不再导出且构造方式从位置参数改为选项对象。工程佐证与测试仓库对 Mesh 的行为有完整的单元测试覆盖Mesh.test.ts包括销毁不抛异常、渲染后销毁时geometry/texture置空且_gpuData清空、非批处理模式下不创建batchableMesh、tint染色支持mesh.tint red等价于0xff0000以及非批处理时根据纹理 alpha 模式自动调整blendMode如no-premultiply-alpha映射为normal-npm。这些测试同时验证了batchMode对管线行为的直接影响。小结PixiJS v8 的Mesh把 WebGL/WebGPU 的绘制本质抽象为三件套Geometry顶点、UV、索引、拓扑、Shader顶点/片元程序属性名必须与 geometry 匹配与State混合、深度、裁剪等 GPU 状态三者组合即可渲染任意 2D 乃至伪 3D 图形。上手时记住四个要点始终用选项对象构造、拓扑设置在 geometry 上、想合批就避免自定义 shader 与深度/裁剪状态、更新顶点后记得调用 Buffer 的update()。配合MeshSimple的自动更新从静态四边形到逐帧变形的布条、绳索、拖尾效果都能以最少的样板代码实现。API 参考MeshMeshOptionsMeshGeometryMeshGeometryOptionsMeshSimpleMeshPipe 渲染管线Topology 类型定义【免费下载链接】pixijsThe HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.项目地址: https://gitcode.com/gh_mirrors/pi/pixijs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。