pixijs-custom-rendering
pixijs/pixijs
PixiJS v8에서 사용자 정의 셰이더, 유니폼, 필터 또는 배처를 작성할 때 이 기술을 활용하세요. Shader.from({gl, gpu, resources}), GlProgram/GpuProgram, 형식이 지정된 유니폼(f32, vec2, mat4x4)을 사용하는 UniformGroup, UBO 모드, 리소스로 사용되는 텍스처, Filter.from을 통한 사용자 정의 필터, GLSL ES 3.0 규칙(in/out, finalColor, texture()), uBackTexture 샘플링, 엄격한 CSP를 위한 pixi.js/unsafe-eval, 확장 기능을 통한 사용자 정의 배처 등을 다룹니다. 트리거 대상: Shader, GlProgram, GpuProgram, UniformGroup, Batcher, Filter, Filter.from, GLSL, WGSL, UBO, uniform, 사용자 정의 셰이더, finalColor, uBackTexture, blendRequired, unsafe-eval.
...모든 것을 확장하십시오커스텀 셰이더는 Shader.from({ gl, gpu, resources })를 통해 GLSL 및 WGSL 프로그램을 씬 오브젝트에 바인딩합니다. 유니폼은 타입이 지정된 UniformGroups에포함되며, 텍스처는 별도의 리소스로 전달되며, 동일한 셰이더가 WebGL과 WebGPU 모두를 대상으로 할 수 있습니다.
빠른 시작
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에서 v8로의 셰이더 API 마이그레이션).
핵심 패턴
듀얼 렌더러 셰이더 (WebGL + WebGPU)
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: {
// 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" },
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, texture2D 내의 #define)를 삽입하고 셰이더를 WebGL1 스타일의 GLSL로 실행합니다. GlProgram은 항상 기본 정밀도(버텍스:highp, 프래그먼트: mediump )와 프로그램 이름을 삽입합니다. GLSL ES 3.0의 경우,attribute/varying 대신in/out을, texture2D() 대신 texture()를, gl_FragColor 대신 out vec4를 사용하십시오.
리소스로서의 텍스처
텍스처는 유니폼이 아닌 리소스입니다. 텍스처의 소스와 스타일을 별도로 전달하세요:
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;
리소스는 평면적인 키-값 맵입니다. 키는 셰이더 소스 내의 유니폼/바인딩 이름과 일치해야 합니다.
리소스는 일반 객체일 수도 있습니다( UniformGroup으로 자동 래핑됨):
const shader = Shader.from({
gl: { vertex: vertSrc, fragment: fragSrc },
resources: {
myUniforms: {
uTime: { value: 0, type: "f32" },
},
},
});
UBO 모드 (Uniform Buffer Objects)
UBO 모드는 유니폼을 단일 GPU 버퍼에 묶어줍니다. WebGPU에서는 필수이며, WebGL에서는 선택 사항(WebGL2+)입니다.
import { UniformGroup } from "pixi.js";const ubo = 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는지원되지 않음). 행렬은 float형만 허용됩니다.- 샘플러/텍스처는 UBO에 포함될 수 없습니다.
- 리소스 내의 UniformGroup 이름은 셰이더 내의 UBO 블록 이름과 정확히 일치해야 합니다.
- 구조와 순서는 셰이더 레이아웃과 정확히 일치해야 합니다.
- UBO 동기화는 내부적으로
새로운 Function을사용합니다. 엄격한 CSP 환경(unsafe-eval없음)에서는 시작 시pixi.js/unsafe-eval을한 번 임포트하여 대체 동기화 경로로 전환해야 합니다. 이를 수행하지 않으면 UBO 기반 셰이더(그리고 결과적으로 WebGPU)가 첫 사용 시 오류가 발생합니다.
사용자 정의 필터
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 })를 사용하십시오.
필터 셰이더 규칙 (GLSL ES 3.0)
varyingvec2 vTextureCoord;대신vec2 vTextureCoord;gl_FragColor대신out vec4 finalColor;texture2D(uTexture, uv)대신texture(uTexture, uv)- 기본 버텍스 셰이더는
uInputSize,uOutputFrame,uOutputTexture및 헬퍼 함수filterVertexPosition()/filterTextureCoord()를노출합니다.
필터 뒤의 렌더 타겟 샘플링
blendRequired: true를 설정하고 프래그먼트 셰이더에서 uBackTexture를 샘플링합니다. PixiJS는 필터를 실행하기 전에 대상 픽셀을 해당 유니폼으로 복사합니다:
const blendFilter = 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();
유니폼 유형 참조
지원되는 유형, 해당 WGSL/GLSL 대응 유형 및 값 형식에 대한 전체 표는 references/uniform-types.md를 참조하십시오.
사용자 정의 배처 (확장 기능 기반)
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; // 정점당 부동소수점 값 개수
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);
요소(Elements)는 batcherName을 통해 배처(batcher)를 참조합니다. BatchableElement 인터페이스에는 batcherName, texture, blendMode, indexSize, attributeSize, topology 및 packAsQuad가 필요합니다.
흔히 저지르는 실수
[중요] 구식 Shader.from(vertex, fragment, uniforms) 생성자
잘못된 예:
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 프로그램 및 리소스와 함께 옵션 객체가 필요합니다. 위치 기반 API는 제거되었습니다.
[중요] 유형 주석이 없는 UniformGroup
잘못된 예:
new UniformGroup({ uTime: 1 });
올바른 예:
new UniformGroup({ uTime: { value: 1, type: "f32" } });
모든 유니폼에는 { value, type } 쌍을 명시적으로 지정해야 합니다. type을 생략하면 "Uniform type undefined is not supported."라는 런타임 오류가 발생합니다.
[HIGH] 지원되지 않는 유형 또는 잘못된 구조를 가진 UBO
UBO 모드는 f32 및 i32 기반 유형(스칼라 및 벡터)을 지원합니다. u32는 지원되는 UniformGroup 유형 목록에 포함되어 있지 않아 예외가 발생합니다. 행렬은 float 전용입니다(mat*). 샘플러는 UBO에 배치할 수 없습니다.
구조체 이름과 필드 순서는 셰이더의 UBO 선언과 정확히 일치해야 합니다. 일치하지 않을 경우 오류 메시지 없이 렌더링 결과가 왜곡됩니다.
[HIGH] UniformGroup에 텍스처 배치
잘못된 예:
new UniformGroup({
uTexture: { value: texture, type: "f32" },
});
올바른 예:
const shader = 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.
API Reference
pixijs-custom-rendering 설치
스킬 파일을 다운로드하여 .claude/skills/ 디렉터리에 압축을 풀어주세요.
ZIP 다운로드저장소를 클론하고 스킬 파일을 프로젝트에 복사하세요.
git clone https://github.com/pixijs/pixijs/tree/dev/skills/pixijs-custom-rendering # Copy the skill folder to .claude/skills/ or .codex/skills/
복사





집
