pixijs-custom-rendering
pixijs/pixijs
在 PixiJS v8 中编写自定义着色器、uniform、滤波器或批处理器时,请使用此技能。内容涵盖 Shader.from({gl, gpu, resources})、GlProgram/GpuProgram、带类型 uniform(f32、vec2、mat4x4)的 UniformGroup、 UBO 模式、作为资源的纹理、通过 Filter.from 实现的自定义滤波器、GLSL ES 3.0 规范(in/out、finalColor、texture())、uBackTexture 采样、用于严格 CSP 的 pixi.js/unsafe-eval,以及通过扩展实现的自定义批处理器。 触发条件:着色器、GlProgram、GpuProgram、UniformGroup、Batcher、Filter、Filter.from、GLSL、WGSL、UBO、uniform、自定义着色器、finalColor、uBackTexture、blendRequired、unsafe-eval。
...展开全部自定义着色器通过Shader.from({ gl, gpu, resources }) 将 GLSL 和 WGSL 程序绑定到场景对象上。Uniform 变量存储在带类型的UniformGroup中,纹理作为独立资源传递,同一着色器可同时支持 WebGL 和 WebGPU。
快速入门
constuniforms =new UniformGroup({
uTime: {value:0,type:"f32"},
});constshader =Shader.from({
gl: {vertex: vertexSrc,fragment: fragmentSrc },
resources: { uniforms },
});constgeometry =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]),
});constmesh =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)。
核心模式
双渲染器着色器(WebGL + WebGPU)
import{Shader,GlProgram,GpuProgram,UniformGroup}from "pixi.js";constglVertex =`...`;// GLSL 顶点(若需 WebGL2/GLSL ES 3.0,请自行添加 `#version 300 es`)
constglFragment =`...`;// GLSL 片段
constwgslSource =`...`;// WGSL组合代码constshader =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" },
uMatrix: {value:new Float32Array(16),type:"mat4x4 "},
}),
},
});
如果仅提供gl,则着色器仅在 WebGL 环境下运行;如果仅提供gpu,则仅在 WebGPU 环境下运行。compatibleRenderers位掩码将自动设置。
GlProgram 不会自动注入#version 300 es。 如果您自行编写#version 300 es,PixiJS 会保留该声明并将着色器视为 GLSL ES 3.0;否则,它会注入 WebGL1 兼容宏(在 varying 中使用 #define,在 texture 中使用#define texture2D),并以 WebGL1 风格的 GLSL 运行该着色器。GlProgram会始终注入默认精度(顶点为highp,片段为mediump)以及程序名称。对于 GLSL ES 3.0,请使用in/out代替attribute/varying,使用texture()代替texture2D(),并使用out vec4代替gl_FragColor。
作为资源的纹理
纹理是资源,而非统一变量。请分别传递纹理的来源和样式:
import{Shader,UniformGroup,Texture,Assets}from "pixi.js";consttexture =await Assets.load("myImage.png");constshader =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):
constshader =Shader.from({
gl: {vertex: vertSrc,fragment: fragSrc },
resources: {
myUniforms: {
uTime: {value:0,type:"f32"},
},
},
});
UBO 模式(统一缓冲区对象)
UBO 模式将统一变量打包到单个 GPU 缓冲区中。WebGPU 必须使用此模式;WebGL(WebGL2 及以上版本)中可选。
import{UniformGroup}from "pixi.js";constubo =new UniformGroup(
{
uProjection: {value:new Float32Array(16),type:"mat4x4" },
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 同步在底层使用了
新的 Function 函数。在严格 CSP 环境中(无unsafe-eval),请在启动时导入一次pixi.js/unsafe-eval以切换到备用同步路径;否则,基于 UBO 的着色器(以及 WebGPU)将在首次使用时抛出异常。
自定义滤波器
Filter.from({ gl, resources })是其简写形式。只需传入片段着色器;PixiJS 会提供一个默认顶点着色器来处理输出帧的定位。
import{Filter}from "pixi.js";constfilter =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 着色器规范(GLSL ES 3.0)
使用 vec2 vTextureCoord;代替varying vec2 vTextureCoord;- 使用
out vec4 finalColor;代替gl_FragColor - 使用
texture(uTexture, uv)代替texture2D(uTexture, uv) - 默认顶点着色器公开了
uInputSize、uOutputFrame、uOutputTexture以及辅助函数filterVertexPosition()/filterTextureCoord()
采样滤镜后方的渲染目标
将blendRequired 设为 true,并在片段着色器中采样uBackTexture。PixiJS 会在运行滤波器之前将目标像素复制到该统一变量中:
constblendFilter =Filter.from({
gl: {fragment: blendFragSrc },
resources: {uniforms: {uAmount: {value:0.5,type:"f32"} } },
blendRequired:true,
});
仅在需要时启用blendRequired;它会强制每帧进行一次额外的 GPU 复制。
运行时更新统一变量
// 通过 resources 访问 UniformGroup
shader.resources.myUniforms.uniforms.uTime= performance.now() /1000;// 对于 isStatic 类型的 UBO,修改值后需调用 update()
shader.resources.myUniforms.update();
Uniform 类型参考
请参阅references/uniform-types.md,查看支持类型的完整列表、其 WGSL/GLSL 对应类型以及值格式。
自定义批处理器(基于扩展)
Batcher抽象类支持针对特殊渲染场景进行自定义批处理。请继承该类并通过扩展进行注册:
import{Batcher, extensions,ExtensionType}from "pixi.js";
import type{
BatcherOptions,
BatchableMeshElement,
BatchableQuadElement,
Geometry,
Shader,
}from "pixi.js";class MyBatcher extends Batcher{
public staticextension = {
type: [ExtensionType.Batcher],
name:"my-batcher",
}; publicname ="my-batcher";
protectedvertexSize =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。
常见错误
[严重] 旧版 Shader.from(vertex, fragment, uniforms) 构造函数
错误示例:
constshader =Shader.from(vertex, fragment, {uTime:1});
正确写法:
constshader =Shader.from({
gl: { vertex, fragment },
resources: {
uniforms:new UniformGroup({
uTime: {value:1,type:"f32"},
}),
},
});
V8 要求gl/gpu程序和资源必须使用选项对象。位置 API 已被移除。
[严重] UniformGroup 未进行类型标注
错误:
new UniformGroup({uTime:1});
正确写法:
new UniformGroup({uTime: {value:1,type:"f32"} });
每个统一变量都需要显式指定{ value, type }对。省略类型会导致运行时错误:“不支持未定义的统一类型。”
[高] 包含不受支持类型或结构错误的 UBO
UBO 模式支持基于f32和i32的类型(标量和向量)。u32不在支持的UniformGroup类型列表中,使用时会抛出异常。矩阵仅支持浮点数(mat* )。采样器不能放置在 UBO 中。
结构体名称和字段顺序必须与着色器的 UBO 声明完全一致。不匹配会导致渲染结果混乱,且不会报错。
[HIGH] 将纹理放入 UniformGroup
错误示例:
new UniformGroup({
uTexture: {value: texture,type:"f32"},
});
正确:
constshader =Shader.from({
gl: { vertex, fragment },
resources: {
uTexture: texture.source,
uSampler: texture.source.style,
myUniforms:new UniformGroup({
uAlpha: {value:1.0,type:"f32"},
}),
},
});
纹理属于资源,而非统一变量。请将texture.source(TextureSource)和texture.source.style(TextureStyle)作为顶级资源条目传入。
API 参考
Custom shaders bind GLSL and WGSL programs to scene objects via Shader.from({ gl, gpu, resources }). Uniforms live in typed UniformGroups, textures are passed as separate resources, and the same shader can target both WebGL and WebGPU.
Quick Start
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).
Core Patterns
Dual-renderer shader (WebGL + WebGPU)
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 combinedconst 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.
Textures as resources
Textures are resources, not uniforms. Pass the texture's source and style separately:
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):
const shader = Shader.from({
gl: { vertex: vertSrc, fragment: fragSrc },
resources: {
myUniforms: {
uTime: { value: 0, type: "f32" },
},
},
});
UBO mode (Uniform Buffer Objects)
UBO mode packs uniforms into a single GPU buffer. Required for WebGPU; optional (WebGL2+) for WebGL.
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
f32andi32based types are supported (nou32). 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 Functionunder the hood. In strict-CSP environments (nounsafe-eval), importpixi.js/unsafe-evalonce at startup to swap in the fallback sync path; without it, UBO-backed shaders (and therefore WebGPU) will throw on first use.
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.
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 shader conventions (GLSL ES 3.0)
in vec2 vTextureCoord;instead ofvarying vec2 vTextureCoord;out vec4 finalColor;instead ofgl_FragColortexture(uTexture, uv)instead oftexture2D(uTexture, uv)- The default vertex shader exposes
uInputSize,uOutputFrame,uOutputTextureand helpersfilterVertexPosition()/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:
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.
Updating uniforms at runtime
// 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();
Uniform type reference
See references/uniform-types.md for the complete table of supported types, their WGSL/GLSL equivalents, and value formats.
Custom Batcher (extension-based)
The Batcher abstract class enables custom batching for specialized rendering. Subclass it and register via extensions:
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.
Common Mistakes
[CRITICAL] Old Shader.from(vertex, fragment, uniforms) constructor
Wrong:
const shader = Shader.from(vertex, fragment, { uTime: 1 });
Correct:
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.
[CRITICAL] UniformGroup without type annotation
Wrong:
new UniformGroup({ uTime: 1 });
Correct:
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."
[HIGH] UBO with unsupported types or wrong structure
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.
[HIGH] Putting textures in UniformGroup
Wrong:
new UniformGroup({
uTexture: { value: texture, type: "f32" },
});
Correct:
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.





首页
