PixiJS v8 自定义渲染完全指南:Shader、UniformGroup、Filter 与 Batcher 实战
2026/9/19 1:24:43 网站建设 项目流程

PixiJS v8 自定义渲染完全指南:Shader、UniformGroup、Filter 与 Batcher 实战

【免费下载链接】pixijsThe HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.项目地址: https://gitcode.com/gh_mirrors/pi/pixijs

本指南以 PixiJS v8 自定义渲染能力为主线,系统讲解如何通过Shader.from({ gl, gpu, resources })将 GLSL / WGSL 着色器绑定到场景对象,如何用带类型标注的UniformGroup管理 uniform、以独立资源方式传入纹理,以及如何基于Filter.from快速构建自定义滤镜、基于扩展机制注册自定义Batcher。读完本文,你将掌握 WebGL 与 WebGPU 双渲染器共用的着色器编写范式、UBO 模式的使用边界、滤镜的 GLSL ES 3.0 约定,以及常见踩坑点的规避方法。

本文对应的技能文档位于 skills/pixijs-custom-rendering/SKILL.md,配套的完整 uniform 类型表见 references/uniform-types.md;所有结论均可对照src/rendering/src/filters/下的源码验证。

快速上手:把第一个自定义着色器挂到 Mesh 上

自定义渲染的核心 API 是Shader.from。它接收一个 options 对象,其中gl声明 WebGL 着色器源码,resources声明 uniform 与纹理资源,然后与MeshGeometryMesh组合即可渲染:

const uniforms = new UniformGroup({ uTime: { value: 0, type: "f32" }, }); const shader = Shader.from({ gl: { vertex: vertexSrc, fragment: fragmentSrc }, resources: { uniforms }, }); 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]), }); const mesh = new Mesh({ geometry, shader }); app.stage.addChild(mesh); app.ticker.add(() => { shader.resources.uniforms.uniforms.uTime = performance.now() / 1000; });

从源码层面看,Shader是渲染管线中连接「着色器」与「几何体」的枢纽(见 Shader.ts):WebGL 侧持有GlProgram,WebGPU 侧持有GpuProgram,两者通过同一个资源对象共享 uniform 数据。若只提供其中一方,compatibleRenderers位掩码会自动按0b01(WebGL)/0b10(WebGPU)设置,缺失的一侧渲染器将无法使用该着色器(Shader.ts)。

需要强调的是,v8 的Shader.from只接受 options 对象,v7 时代的位置参数构造Shader.from(vertex, fragment, uniforms)已被移除(详见下文「常见错误」)。

核心模式

双渲染器着色器(WebGL + WebGPU)

一份着色器源码同时支持两种渲染器,只需同时提供glgpu两个程序描述:

import { Shader, GlProgram, GpuProgram, UniformGroup } from "pixi.js"; const glVertex = `...`; // GLSL vertex(如需 WebGL2/GLSL ES 3.0,可自行编写 `#version 300 es`) const glFragment = `...`; // GLSL fragment const wgslSource = `...`; // WGSL 合并源码 const shader = Shader.from({ gl: { vertex: glVertex, fragment: glFragment }, gpu: { // entryPoint 名称可任取,但必须与 WGSL 源码中的 @vertex / @fragment 函数名一致。 // PixiJS 自带示例习惯使用 'mainVert' / 'mainFrag',使用 `main` 同样合法。 vertex: { entryPoint: "mainVert", source: wgslSource }, fragment: { entryPoint: "mainFrag", source: wgslSource }, }, resources: { myUniforms: new UniformGroup({ uColor: { value: new Float32Array([1, 0, 0, 1]), type: "vec4<f32>" }, uMatrix: { value: new Float32Array(16), type: "mat4x4<f32>" }, }), }, });

要点:

  • 只传gl,着色器仅支持 WebGL;只传gpu,仅支持 WebGPU;两者都传则自动获得双渲染器兼容。
  • resources中 UniformGroup 的键名必须与着色器源码中的 uniform/binding 名称一致——WebGPU 侧是硬性要求,WebGL 侧则相对宽松(见 Shader.ts)。
  • 仓库中可参考的完整双渲染器示例:mesh_custom_shader_geometry/index.ts(同时提供了triangle.vert/triangle.frag/triangle.wgsl三份源码),以及 mesh_custom_color_attributes/index.ts。

关于 GLSL 版本的关键事实GlProgram不会自动注入#version 300 es。它的预处理流水线依次执行stripVersionensurePrecisionaddProgramDefinessetProgramNameinsertVersion(见 GlProgram.ts):

  • 若你在源码里自行写了#version 300 es,PixiJS 会保留它并按 GLSL ES 3.0 处理;
  • 否则会注入 WebGL1 兼容宏(#define in varying#define texture texture2D),按 WebGL1 风格 GLSL 运行;
  • 无论哪种情况,GlProgram都会注入默认精度(顶点highp、片元mediump,见 GlProgram.ts)与程序名。

因此编写 GLSL ES 3.0 时请遵循:用in/out替代attribute/varying,用texture()替代texture2D(),用out vec4替代gl_FragColor。另外,GlProgram.from会按「顶点 + 片元源码」建立程序缓存,复用相同源码不会重复编译(GlProgram.ts),这也是为什么官方建议尽可能复用程序对象。

纹理是资源,不是 uniform

纹理不能放进UniformGroup,而是作为独立的顶级资源传入:纹理的sourceTextureSource)与styleTextureStyle)要分开传递:

import { Shader, UniformGroup, Texture, Assets } from "pixi.js"; const texture = await Assets.load("myImage.png"); const shader = Shader.from({ gl: { vertex: vertSrc, fragment: fragSrc }, resources: { uTexture: texture.source, uSampler: texture.source.style, myUniforms: new UniformGroup({ uAlpha: { value: 1.0, type: "f32" }, }), }, }); // 运行时切换纹理 shader.resources.uTexture = otherTexture.source;

资源是一个扁平的键值映射,键名必须匹配着色器源码中的 uniform/binding 名。渲染时修改shader.resources中的任意资源即可热更新。

便捷机制resources中的普通对象会被自动包装为UniformGroup。这一逻辑在Shader构造器中实现——凡是没有source属性、也不是BindResource的值,都会走value = new UniformGroup(value)(Shader.ts)。因此下面两种写法等价:

const shader = Shader.from({ gl: { vertex: vertSrc, fragment: fragSrc }, resources: { myUniforms: { uTime: { value: 0, type: "f32" }, }, }, });

UBO 模式(Uniform Buffer Objects)

UBO 模式把一组 uniform 打包进单个 GPU 缓冲区。WebGPU 只能通过 UBO 使用 uniform,因此想在 WebGPU 下跑自定义着色器就必须开启它;WebGL2+ 下则是可选优化。

import { UniformGroup } from "pixi.js"; const ubo = new UniformGroup( { uProjection: { value: new Float32Array(16), type: "mat4x4<f32>" }, uAlpha: { value: 1.0, type: "f32" }, }, { ubo: true, isStatic: true }, ); // isStatic 为 true 时必须手动调用 update() 触发上传 ubo.uniforms.uAlpha = 0.5; ubo.update();

UBO 规则(源码注释见 UniformGroup.ts):

  • 仅支持f32i32系类型(标量与向量),矩阵仅支持浮点(mat*<f32>);u32不在UniformGroup的类型表中,会直接抛错。
  • 采样器/纹理不能放进 UBO(GPU 限制)。
  • resources 中的键名必须与着色器中的 UBO 块名完全一致。
  • 结构(字段名与顺序)必须与着色器布局完全一致,否则渲染结果错乱且不会报错。
  • UBO 同步底层依赖new Function动态生成同步函数。在禁止unsafe-eval的严格 CSP 环境下,需要在启动时一次性引入pixi.js/unsafe-eval以切换到回退同步路径;否则首次使用 UBO(进而 WebGPU)时会抛错。该入口导出generateUboSyncPolyfill等回退实现,见 src/unsafe-eval/index.ts 与 src/unsafe-eval/ubo/。

另外注意UniformGroup的默认选项为ubo: falseisStatic: false(UniformGroup.ts):非静态模式下数据每帧自动重传,无需手动update()isStatic: true则把上传时机完全交给你,适合每帧只更新一次的 UBO,以获得最大性能收益。

自定义滤镜:Filter.from

Filter.from({ gl, resources })是创建自定义滤镜的快捷方式:只需提供片元着色器,PixiJS 会自动补一个负责输出帧定位的默认顶点着色器。

import { Filter } from "pixi.js"; const filter = Filter.from({ gl: { fragment: ` in vec2 vTextureCoord; out vec4 finalColor; uniform sampler2D uTexture; uniform float uStrength; void main(void) { vec4 color = texture(uTexture, vTextureCoord); finalColor = mix(color, vec4(1.0 - color.rgb, color.a), uStrength); } `, }, resources: { filterUniforms: { uStrength: { value: 0.5, type: "f32" }, }, }, }); filter.resources.filterUniforms.uniforms.uStrength = 1.0;

需要自定义顶点着色器时,改用完整构造:

new Filter({ glProgram: new GlProgram({ vertex, fragment }), resources, });

Filter继承自Shader(Filter.ts),因此它天然具备 Shader 的全部资源能力。其默认配置在 Filter.ts 中定义:blendMode: 'normal'resolution: 1padding: 0antialias: 'off'blendRequired: falseclipToViewport: true。其中:

  • resolution可设为数字或'inherit',调低分辨率可显著提升滤镜性能(模糊类滤镜常用);
  • padding为滤镜扩展的边界像素,模糊等会外溢的效果需要它防止裁切;
  • antialias支持'on' | 'off' | 'inherit''inherit'会跟随渲染目标的抗锯齿设置。

滤镜可作用于任何继承自Container的对象(Sprite、Graphics 等)。它的底层流程是:打断当前批次 → 用getGlobalBounds测量目标 → 从纹理池取纹理 → 把目标渲染到该纹理 → 再以滤镜程序把纹理作为 quad 画回主帧缓冲(见 Filter.ts)。正因如此,对一个容器应用一次滤镜远比给大量对象各挂一个滤镜快得多

滤镜着色器约定(GLSL ES 3.0)
  • in vec2 vTextureCoord;替代varying vec2 vTextureCoord;
  • out vec4 finalColor;替代gl_FragColor
  • texture(uTexture, uv)替代texture2D(uTexture, uv)
  • 默认顶点着色器暴露uInputSizeuOutputFrameuOutputTexture及辅助函数filterVertexPosition()/filterTextureCoord()

Filter构造时会自动为uTexture预留资源槽位(addResource('uTexture', 0, 1),见 Filter.ts),所以片元着色器里声明uniform sampler2D uTexture;即可取到滤镜输入。

采样滤镜背后的渲染目标

当滤镜需要感知「背后已经画好的像素」(例如实现混合类效果)时,设置blendRequired: true,然后在片元着色器中采样uBackTexture——滤镜系统会先把目标区域的像素拷贝进该 uniform 再运行滤镜:

const blendFilter = Filter.from({ gl: { fragment: blendFragSrc }, resources: { uniforms: { uAmount: { value: 0.5, type: "f32" } } }, blendRequired: true, });

从实现看,开启后Filter会额外执行addResource('uBackTexture', 0, 3)(Filter.ts),而关闭时该资源槽不存在。只在确实需要时开启blendRequired——它每帧都会强制增加一次额外的 GPU 拷贝(对应源码注释 "otherwise its an extra gpu copy you don't need!")。

运行时更新 uniform

// 通过 resources 访问 UniformGroup shader.resources.myUniforms.uniforms.uTime = performance.now() / 1000; // 对于 isStatic 的 UBO,修改值后要手动调用 update() shader.resources.myUniforms.update();

注意UniformGroup.update()的实现是递增_dirtyId以标记数据待上传(UniformGroup.ts),渲染器据此决定是否重传 GPU 缓冲区。

Uniform 类型参考

UniformGroup中每个 uniform 都必须以{ value, type }形式声明,type字符串直接对应 WebGPU 类型。完整类型表见 references/uniform-types.md,以下为速查:

PixiJS 类型WGSL 等价GLSL 等价JS 值
f32f32floatnumber
i32i32intnumber
vec2<f32>vec2<f32>vec2Float32Array(2)[x, y]
vec3<f32>vec3<f32>vec3Float32Array(3)
vec4<f32>vec4<f32>vec4Float32Array(4)
vec2<i32>vec2<i32>ivec2Int32Array(2)
vec3<i32>vec3<i32>ivec3Int32Array(3)
vec4<i32>vec4<i32>ivec4Int32Array(4)
mat2x2<f32>mat2x2<f32>mat2Float32Array(4)
mat3x3<f32>mat3x3<f32>mat3Float32Array(9)Matrix
mat4x4<f32>mat4x4<f32>mat4Float32Array(16)
mat3x2<f32>mat3x2<f32>mat3x2Float32Array(6)
mat4x2<f32>mat4x2<f32>mat4x2Float32Array(8)
mat2x3<f32>mat2x3<f32>mat2x3Float32Array(6)
mat4x3<f32>mat4x3<f32>mat4x3Float32Array(12)
mat2x4<f32>mat2x4<f32>mat2x4Float32Array(8)
mat3x4<f32>mat3x4<f32>mat3x4Float32Array(12)

底层支持类型白名单定义在 types.ts,UniformGroup构造时会校验每个 type,未命中白名单会抛出Uniform type ... is not supported错误(UniformGroup.ts)。

数组 uniform:不要用array<...>语法写进 type 字段,改用size属性:

import { UniformGroup } from "pixi.js"; // 10 个 vec4 组成的数组 const uniforms = new UniformGroup({ uColors: { value: new Float32Array(40), type: "vec4<f32>", size: 10 }, });

若在 type 中写array<vec4<f32>, 10>,构造器会直接抛错,并提示改用type: 'vec4<f32>', size: 10(UniformGroup.ts)。

常见用法示例

// 标量 const uniforms = new UniformGroup({ uTime: { value: 0, type: "f32" }, uIndex: { value: 0, type: "i32" }, }); uniforms.uniforms.uTime = 1.5; uniforms.uniforms.uIndex = 3; // 向量 const uniforms = new UniformGroup({ uPosition: { value: new Float32Array([100, 200]), type: "vec2<f32>" }, uDirection: { value: new Float32Array([0, 1, 0]), type: "vec3<f32>" }, uColor: { value: new Float32Array([1, 0, 0, 1]), type: "vec4<f32>" }, uGridSize: { value: new Int32Array([16, 16]), type: "vec2<i32>" }, }); // 矩阵 import { Matrix } from "pixi.js"; const uniforms = new UniformGroup({ uTransform: { value: new Matrix(), type: "mat3x3<f32>" }, uProjection: { value: new Float32Array(16), type: "mat4x4<f32>" }, uRotation: { value: new Float32Array(4), type: "mat2x2<f32>" }, });

注意:PixiJS 的 2D 仿射变换矩阵Matrix对应mat3x3<f32>;3D 投影矩阵请使用裸Float32Array(16)+mat4x4<f32>。另外,即便声明了value之外不传值,UniformGroup也会按类型自动补默认值(getDefaultUniformValue,见 UniformGroup.ts)。

自定义 Batcher(扩展机制)

Batcher抽象类用于实现针对特殊渲染需求的自定义合批。继承它并实现属性打包逻辑,再通过扩展机制注册:

import { Batcher, extensions, ExtensionType } from "pixi.js"; import type { BatcherOptions, BatchableMeshElement, BatchableQuadElement, Geometry, Shader, } from "pixi.js"; class MyBatcher extends Batcher { public static extension = { type: [ExtensionType.Batcher], name: "my-batcher", }; public name = "my-batcher"; protected vertexSize = 6; // 每个顶点的 float 数 public geometry: Geometry; public shader: Shader; constructor(options: BatcherOptions) { super(options); // 初始化 geometry 和 shader } public packAttributes( element: BatchableMeshElement, float32View: Float32Array, uint32View: Uint32Array, index: number, textureId: number, ): void { // 把 mesh 顶点属性打包进合批缓冲区 } public packQuadAttributes( element: BatchableQuadElement, float32View: Float32Array, uint32View: Uint32Array, index: number, textureId: number, ): void { // 把 quad 顶点属性打包进合批缓冲区 } } extensions.add(MyBatcher);

要素说明:

  • 扩展通过静态extension属性声明type: [ExtensionType.Batcher]与唯一name
  • vertexSize决定每个顶点的浮点数量,与你的顶点格式匹配;
  • 元素通过batcherName引用该合批器;
  • 要实现BatchableElement接口,需提供batcherNametextureblendModeindexSizeattributeSizetopologypackAsQuad等字段。

仓库中可直接运行的自定义着色器/滤镜示例还包括 filters_custom-shader_glsl/index.ts、mesh_multipass_shader_effects/index.ts 与 text_filters_cartoon/CartoonTextFilter.ts,可作为扩展阅读。

常见错误

[严重] 沿用 v7 的位置参数构造 Shader

错误写法:

const shader = Shader.from(vertex, fragment, { uTime: 1 });

正确写法:

const shader = Shader.from({ gl: { vertex, fragment }, resources: { uniforms: new UniformGroup({ uTime: { value: 1, type: "f32" }, }), }, });

v8 要求传入包含gl/gpu程序与resources的 options 对象,位置参数 API 已移除。

[严重] UniformGroup 缺少类型标注

错误写法:

new UniformGroup({ uTime: 1 });

正确写法:

new UniformGroup({ uTime: { value: 1, type: "f32" } });

每个 uniform 都必须提供显式的{ value, type }对。省略 type 会在运行时抛出Uniform type undefined is not supported(该校验位于 UniformGroup.ts)。

[高] UBO 使用了不支持的类型或结构不匹配

UBO 模式只支持f32/i32系类型(标量与向量),u32不在支持列表会抛错;矩阵仅限浮点(mat*<f32>);采样器不能放入 UBO。此外,UBO 的块名、字段名与顺序必须与着色器声明完全一致,否则渲染结果会错乱且不会报错

[高] 把纹理放进 UniformGroup

错误写法:

new UniformGroup({ uTexture: { value: texture, type: "f32" }, });

正确写法:纹理作为顶级资源传入sourcestyle,uniform 单独放一组:

const shader = Shader.from({ gl: { vertex, fragment }, resources: { uTexture: texture.source, uSampler: texture.source.style, myUniforms: new UniformGroup({ uAlpha: { value: 1.0, type: "f32" }, }), }, });

纹理是资源而非 uniform:顶层资源条目请传texture.sourceTextureSource)与texture.source.styleTextureStyle)。

关联技能与 API 参考

  • 相关技能文档:pixijs-filters(内置滤镜)、pixijs-scene-mesh(自定义几何体)、pixijs-performance(合批优化)、pixijs-migration-v8(从 v7 迁移着色器 API)。
  • 核心 API:ShaderGlProgramGpuProgramUniformGroupFilterBatcherBatcherPipe,源码入口分别为 Shader.ts、GlProgram.ts、GpuProgram.ts、UniformGroup.ts、Filter.ts 与 Batcher.ts。

【免费下载链接】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),仅供参考

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

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

立即咨询