pixijs-custom-rendering

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Custom shaders bind GLSL and WGSL programs to scene objects via
Shader.from({ gl, gpu, resources })
. Uniforms live in typed
UniformGroup
s, textures are passed as separate resources, and the same shader can target both WebGL and WebGPU.
自定义着色器通过
Shader.from({ gl, gpu, resources })
将GLSL和WGSL程序绑定到场景对象。Uniforms存储在带类型的
UniformGroup
中,纹理作为独立资源传递,同一个着色器可同时适配WebGL和WebGPU。

Quick Start

快速开始

ts
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;
});
Related skills:
pixijs-filters
(built-in filters),
pixijs-scene-mesh
(custom geometry),
pixijs-performance
(batch optimization),
pixijs-migration-v8
(shader API migration from v7).
ts
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;
});
相关技能:
pixijs-filters
(内置滤镜)、
pixijs-scene-mesh
(自定义几何体)、
pixijs-performance
(批处理优化)、
pixijs-migration-v8
(从v7迁移着色器API)。

Core Patterns

核心模式

Dual-renderer shader (WebGL + WebGPU)

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

ts
import { Shader, GlProgram, GpuProgram, UniformGroup } from "pixi.js";

const glVertex = `...`; // GLSL vertex (write `#version 300 es` yourself if you want WebGL2/GLSL ES 3.0)
const glFragment = `...`; // GLSL fragment
const wgslSource = `...`; // WGSL combined

const shader = Shader.from({
  gl: { vertex: glVertex, fragment: glFragment },
  gpu: {
    // entryPoint names are arbitrary; they must match the @vertex / @fragment
    // function names in your WGSL source. PixiJS ships examples using
    // 'mainVert' / 'mainFrag' but `main` is equally valid.
    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>" },
    }),
  },
});
If only
gl
is provided, the shader works with WebGL only. If only
gpu
is provided, it works with WebGPU only. The
compatibleRenderers
bitmask is set automatically.
GlProgram
does not auto-inject
#version 300 es
. If you write
#version 300 es
yourself, PixiJS preserves it and treats the shader as GLSL ES 3.0; otherwise it injects WebGL1 compat macros (
#define in varying
,
#define texture texture2D
) and runs the shader as WebGL1-style GLSL.
GlProgram
always injects a default precision (
highp
vertex,
mediump
fragment) and the program name. For GLSL ES 3.0, use
in
/
out
instead of
attribute
/
varying
,
texture()
instead of
texture2D()
, and an
out vec4
instead of
gl_FragColor
.
ts
import { Shader, GlProgram, GpuProgram, UniformGroup } from "pixi.js";

const glVertex = `...`; // GLSL顶点着色器(如果需要WebGL2/GLSL ES 3.0,请自行编写`#version 300 es`)
const glFragment = `...`; // GLSL片段着色器
const wgslSource = `...`; // WGSL组合代码

const shader = Shader.from({
  gl: { vertex: glVertex, fragment: glFragment },
  gpu: {
    // 入口点名称可自定义;必须与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。
compatibleRenderers
位掩码会自动设置。
GlProgram
不会自动注入
#version 300 es
。如果您自行编写
#version 300 es
,PixiJS会保留该声明并将着色器视为GLSL ES 3.0;否则会注入WebGL1兼容宏(
#define in varying
、
#define texture texture2D
),并以WebGL1风格运行着色器。
GlProgram
始终会注入默认精度(顶点着色器
highp
,片段着色器
mediump
)和程序名称。对于GLSL ES 3.0,请使用
in
/
out
替代
attribute
/
varying
,使用
texture()
替代
texture2D()
,并使用
out vec4
替代
gl_FragColor
。

Textures as resources

作为资源的纹理

Textures are resources, not uniforms. Pass the texture's
source
and
style
separately:
ts
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" },
    }),
  },
});

// Swap texture at runtime
shader.resources.uTexture = otherTexture.source;
Resources are a flat key-value map. The key must match the uniform/binding name in the shader source.
Resources can also be plain objects (auto-wrapped into
UniformGroup
):
ts
const shader = Shader.from({
  gl: { vertex: vertSrc, fragment: fragSrc },
  resources: {
    myUniforms: {
      uTime: { value: 0, type: "f32" },
    },
  },
});
纹理是资源,而非uniforms。需单独传递纹理的
source
和
style
:
ts
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名称匹配。
资源也可以是普通对象(会自动包装为
UniformGroup
):
ts
const shader = Shader.from({
  gl: { vertex: vertSrc, fragment: fragSrc },
  resources: {
    myUniforms: {
      uTime: { value: 0, type: "f32" },
    },
  },
});

UBO mode (Uniform Buffer Objects)

UBO模式(Uniform Buffer Objects)

UBO mode packs uniforms into a single GPU buffer. Required for WebGPU; optional (WebGL2+) for WebGL.
ts
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 },
);

// Must call update() manually when isStatic is true
ubo.uniforms.uAlpha = 0.5;
ubo.update();
UBO rules:
  • Only
    f32
    and
    i32
    based types are supported (no
    u32
    ). Matrices are float-only.
  • Samplers/textures cannot go in a UBO.
  • The UniformGroup name in resources must exactly match the UBO block name in the shader.
  • Structure and order must exactly match the shader layout.
  • UBO sync uses
    new Function
    under the hood. In strict-CSP environments (no
    unsafe-eval
    ), import
    pixi.js/unsafe-eval
    once at startup to swap in the fallback sync path; without it, UBO-backed shaders (and therefore WebGPU) will throw on first use.
UBO模式将uniforms打包到单个GPU缓冲区中。WebGPU必须使用该模式;WebGL为可选(需WebGL2+)。
ts
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规则:
  • 仅支持基于
    f32
    和
    i32
    的类型(不支持
    u32
    )。矩阵仅支持浮点类型。
  • 采样器/纹理不能放入UBO。
  • 资源中的UniformGroup名称必须与着色器中的UBO块名称完全匹配。
  • 结构和顺序必须与着色器布局完全匹配。
  • UBO同步底层使用
    new Function
    。在严格CSP环境(禁用
    unsafe-eval
    )中,需在启动时导入
    pixi.js/unsafe-eval
    一次,以切换到回退同步路径;否则基于UBO的着色器(以及WebGPU)在首次使用时会抛出错误。

Custom filter

自定义滤镜

Filter.from({ gl, resources })
is the shorthand. Pass only a fragment shader; PixiJS supplies a default vertex shader that handles output frame positioning.
ts
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;
For a custom vertex shader, use
new Filter({ glProgram: new GlProgram({ vertex, fragment }), resources })
.
Filter.from({ gl, resources })
是简写方式。只需传递片段着色器;PixiJS会提供默认顶点着色器,处理输出帧定位。
ts
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 conventions (GLSL ES 3.0)

滤镜着色器规范(GLSL ES 3.0)

  • in vec2 vTextureCoord;
    instead of
    varying vec2 vTextureCoord;
  • out vec4 finalColor;
    instead of
    gl_FragColor
  • texture(uTexture, uv)
    instead of
    texture2D(uTexture, uv)
  • The default vertex shader exposes
    uInputSize
    ,
    uOutputFrame
    ,
    uOutputTexture
    and helpers
    filterVertexPosition()
    /
    filterTextureCoord()
  • 使用
    in vec2 vTextureCoord;
    替代
    varying vec2 vTextureCoord;
  • 使用
    out vec4 finalColor;
    替代
    gl_FragColor
  • 使用
    texture(uTexture, uv)
    替代
    texture2D(uTexture, uv)
  • 默认顶点着色器暴露
    uInputSize
    、
    uOutputFrame
    、
    uOutputTexture
    以及辅助函数
    filterVertexPosition()
    /
    filterTextureCoord()

Sampling the render target behind the filter

采样滤镜后方的渲染目标

Set
blendRequired: true
and sample
uBackTexture
in the fragment shader. PixiJS copies the destination pixels into that uniform before running the filter:
ts
const blendFilter = Filter.from({
  gl: { fragment: blendFragSrc },
  resources: { uniforms: { uAmount: { value: 0.5, type: "f32" } } },
  blendRequired: true,
});
Only enable
blendRequired
when you need it; it forces an extra GPU copy every frame.
设置
blendRequired: true
,并在片段着色器中采样
uBackTexture
。PixiJS会在运行滤镜前将目标像素复制到该uniform中:
ts
const blendFilter = Filter.from({
  gl: { fragment: blendFragSrc },
  resources: { uniforms: { uAmount: { value: 0.5, type: "f32" } } },
  blendRequired: true,
});
仅在需要时启用
blendRequired
;它会强制每帧进行一次额外的GPU复制。

Updating uniforms at runtime

运行时更新uniforms

ts
// Access the UniformGroup via resources
shader.resources.myUniforms.uniforms.uTime = performance.now() / 1000;

// For isStatic UBOs, call update() after changing values
shader.resources.myUniforms.update();
ts
// 通过resources访问UniformGroup
shader.resources.myUniforms.uniforms.uTime = performance.now() / 1000;

// 对于isStatic的UBO,修改值后需调用update()
shader.resources.myUniforms.update();

Uniform type reference

Uniform类型参考

See references/uniform-types.md for the complete table of supported types, their WGSL/GLSL equivalents, and value formats.
完整的支持类型表、对应的WGSL/GLSL等价类型以及值格式,请查看references/uniform-types.md。

Custom Batcher (extension-based)

自定义批处理器(基于扩展)

The
Batcher
abstract class enables custom batching for specialized rendering. Subclass it and register via extensions:
ts
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; // floats per vertex
  public geometry: Geometry;
  public shader: Shader;

  constructor(options: BatcherOptions) {
    super(options);
    // Initialize geometry and shader
  }

  public packAttributes(
    element: BatchableMeshElement,
    float32View: Float32Array,
    uint32View: Uint32Array,
    index: number,
    textureId: number,
  ): void {
    // Pack mesh vertex attributes into the batch buffer
  }

  public packQuadAttributes(
    element: BatchableQuadElement,
    float32View: Float32Array,
    uint32View: Uint32Array,
    index: number,
    textureId: number,
  ): void {
    // Pack quad vertex attributes into the batch buffer
  }
}

extensions.add(MyBatcher);
Elements reference the batcher by
batcherName
. The
BatchableElement
interface requires:
batcherName
,
texture
,
blendMode
,
indexSize
,
attributeSize
,
topology
, and
packAsQuad
.
Batcher
抽象类支持为特殊渲染场景创建自定义批处理。继承该类并通过扩展注册:
ts
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; // 每个顶点的浮点数量
  public geometry: Geometry;
  public shader: Shader;

  constructor(options: BatcherOptions) {
    super(options);
    // 初始化几何体和着色器
  }

  public packAttributes(
    element: BatchableMeshElement,
    float32View: Float32Array,
    uint32View: Uint32Array,
    index: number,
    textureId: number,
  ): void {
    // 将网格顶点属性打包到批处理缓冲区
  }

  public packQuadAttributes(
    element: BatchableQuadElement,
    float32View: Float32Array,
    uint32View: Uint32Array,
    index: number,
    textureId: number,
  ): void {
    // 将四边形顶点属性打包到批处理缓冲区
  }
}

extensions.add(MyBatcher);
元素通过
batcherName
关联批处理器。
BatchableElement
接口要求:
batcherName
、
texture
、
blendMode
、
indexSize
、
attributeSize
、
topology
和
packAsQuad
。

Common Mistakes

常见错误

[CRITICAL] Old Shader.from(vertex, fragment, uniforms) constructor

[严重] 旧版Shader.from(vertex, fragment, uniforms)构造函数

Wrong:
ts
const shader = Shader.from(vertex, fragment, { uTime: 1 });
Correct:
ts
const shader = Shader.from({
  gl: { vertex, fragment },
  resources: {
    uniforms: new UniformGroup({
      uTime: { value: 1, type: "f32" },
    }),
  },
});
v8 requires an options object with
gl
/
gpu
programs and
resources
. The positional API was removed.
错误写法:
ts
const shader = Shader.from(vertex, fragment, { uTime: 1 });
正确写法:
ts
const shader = Shader.from({
  gl: { vertex, fragment },
  resources: {
    uniforms: new UniformGroup({
      uTime: { value: 1, type: "f32" },
    }),
  },
});
v8要求传入包含
gl
/
gpu
程序和
resources
的选项对象,位置参数API已被移除。

[CRITICAL] UniformGroup without type annotation

[严重] 无类型注解的UniformGroup

Wrong:
ts
new UniformGroup({ uTime: 1 });
Correct:
ts
new UniformGroup({ uTime: { value: 1, type: "f32" } });
Every uniform requires an explicit
{ value, type }
pair. Omitting the type causes a runtime error: "Uniform type undefined is not supported."
错误写法:
ts
new UniformGroup({ uTime: 1 });
正确写法:
ts
new UniformGroup({ uTime: { value: 1, type: "f32" } });
每个uniform都需要显式的
{ value, type }
对。省略类型会导致运行时错误:"Uniform type undefined is not supported."

[HIGH] UBO with unsupported types or wrong structure

[高风险] 使用不支持类型或结构错误的UBO

UBO mode supports
f32
and
i32
based types (scalars and vectors).
u32
is not in the supported
UniformGroup
type list and will throw. Matrices are float-only (
mat*<f32>
). Samplers cannot be placed in UBOs.
The struct name and field order must exactly match the shader's UBO declaration. Mismatches produce garbled rendering with no error.
UBO模式支持基于
f32
和
i32
的类型(标量和向量)。
u32
不在
UniformGroup
支持类型列表中,会抛出错误。矩阵仅支持浮点类型(
mat*<f32>
)。采样器不能放入UBO。
结构体名称和字段顺序必须与着色器中的UBO声明完全匹配。不匹配会导致渲染混乱且无错误提示。

[HIGH] Putting textures in UniformGroup

[高风险] 将纹理放入UniformGroup

Wrong:
ts
new UniformGroup({
  uTexture: { value: texture, type: "f32" },
});
Correct:
ts
const shader = Shader.from({
  gl: { vertex, fragment },
  resources: {
    uTexture: texture.source,
    uSampler: texture.source.style,
    myUniforms: new UniformGroup({
      uAlpha: { value: 1.0, type: "f32" },
    }),
  },
});
Textures are resources, not uniforms. Pass
texture.source
(TextureSource) and
texture.source.style
(TextureStyle) as top-level resource entries.
错误写法:
ts
new UniformGroup({
  uTexture: { value: texture, type: "f32" },
});
正确写法:
ts
const shader = Shader.from({
  gl: { vertex, fragment },
  resources: {
    uTexture: texture.source,
    uSampler: texture.source.style,
    myUniforms: new UniformGroup({
      uAlpha: { value: 1.0, type: "f32" },
    }),
  },
});
纹理是资源,而非uniforms。需将
texture.source
(TextureSource)和
texture.source.style
(TextureStyle)作为顶级资源项传递。

API Reference

API参考