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参考