pixijs-3d について
pixijs-3dはPixiJSの3D拡張を使う開発者向けのコーディングスキルです。シーン、カメラ、マテリアル、モデルを解説し、タスク別のリファレンスを提供します。
主な機能
- 013Dシーングラフとカメラの解説
- 02ジオメトリ、ライティング、マテリアルの解説
- 03モデル読み込みとアニメーションの案内
- 042Dシーンと3Dシーンの組み合わせ
- 05描画とインタラクションの注意点
活用シーン
- 01ライトとシャドウのある3Dシーンを構築
- 02モデルを読み込んでアニメーションを再生
- 033Dビューに2Dインターフェースを重ねる
- 04カメラ、マテリアル、入力の問題を調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pixijs/ pixijs-skills --skill pixijs-3dインストール後、Claude Code で/pixijs-3dを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/2d-and-3d-together.mdcameras.mdcustom-shaders.mdenvironment-and-sky.mdfrom-threejs.mdgeometry.mdinstancing.mdmaterials.mdmath.mdmodels-and-animation.mdparticles.mdpicking-and-input.mdpost-processing.mdrender-to-texture.mdscene-and-lighting.mdscene-graph.mdreferences/from-threejs/materials-and-lights.mdobjects-and-animation.mdrenderer-and-scene.mdREADME(原文)English
プロジェクトの README よりこの README の日本語訳はまだありません。以下は英語の原文です。日本語の紹介はページ上部をご覧ください。
PixiJS 3D (@pixi/3d) is PixiJS's 3D extension. A View3D is an ordinary 2D display object on the Pixi stage that renders a 3D scene into its rectangle, so 3D and 2D mix freely. The scene hangs from view.root, the view owns its camera, lights are nodes in the scene, and the Pixi application ticker is the frame loop.
This file gives you the mental model, a complete starting app, and the conventions that catch people. The references/ folder has one complete, runnable recipe per job; open the one you need.
For a signature or option this skill doesn't cover, fetch the class's API page: https://pixijs.download/3d/release/docs/<module>.<Class>.html.md (scene.View3D, materials.Material3D, extras_camera.OrbitCamera, maths.Vector3). To find a class's module, or a guide on a topic, look it up in https://pixijs.download/3d/release/docs/llms.txt.
Quick Start
A lit scene with shadows, two primitives, a loaded model and mouse orbit controls. Complete src/main.ts for a Vite + TypeScript app with pixi.js and @pixi/3d installed.
import {
CubeGeometry,
DirectionalLight,
Material3D,
Mesh3D,
Model3D,
PlaneGeometry,
SphereGeometry,
View3D,
} from '@pixi/3d';
import type { Scene3DSource } from '@pixi/3d';
import { OrbitCamera } from '@pixi/3d/extras';
import { Application, Assets } from 'pixi.js';
const app = new Application();
await app.init({ resizeTo: window, backgroundColor: 0x87b7e6, antialias: true });
document.body.appendChild(app.canvas);
// A View3D is a 2D display object that renders a 3D scene into its rectangle.
const view = new View3D({
autoResize: true,
shadowQuality: 'medium',
environment: { skyColor: 0xbcd4f0, groundColor: 0x6b6250, intensity: 0.7 }, // hemisphere fill light
});
app.stage.addChild(view);
// Lights are nodes under view.root. Lights and cameras face their local -Z axis.
const sun = new DirectionalLight({ color: 0xfff2dc, intensity: 3, castShadow: true });
sun.rotation.set(-Math.PI / 4, Math.PI / 6, 0);
view.root.addChild(sun);
// PlaneGeometry lies in XY with a +Z normal: rotate it -90° around X to make a floor.
const ground = new Mesh3D({
geometry: new PlaneGeometry({ width: 30, height: 30 }),
material: new Material3D({ baseColor: 0x7f8f6a, roughness: 0.95, metallic: 0 }),
rotation: { x: -Math.PI / 2, y: 0, z: 0 },
castShadow: false,
receiveShadow: true,
});
view.root.addChild(ground);
// Meshes take a geometry and a material. Options objects set position, rotation, scale, shadows.
view.root.addChild(
new Mesh3D({
geometry: new CubeGeometry({ width: 2, height: 2, depth: 2 }),
material: new Material3D({ baseColor: 0xd9534f, roughness: 0.6, metallic: 0 }),
position: { x: -3, y: 1, z: 0 },
}),
new Mesh3D({
geometry: new SphereGeometry({ radius: 1.2 }),
material: new Material3D({ baseColor: 0x4a90d9, roughness: 0.3, metallic: 0.2 }),
position: { x: 3, y: 1.2, z: 0 },
}),
);
// Models: load a glTF/GLB with Assets, then wrap the source in a Model3D.
const source = await Assets.load<Scene3DSource>('/assets/models/Duck.glb');
// The file's own node transforms set its size (this duck is about 1.6 units); read duck.getLocalBounds() before scaling.
const duck = new Model3D({ source, position: { x: 0, y: 0, z: 3 } });
view.root.addChild(duck);
// The view owns its camera. OrbitCamera (extras) drives it from the mouse.
view.camera.position.set(0, 5, 12);
view.camera.lookAt({ x: 0, y: 1, z: 0 });
new OrbitCamera({ view, enableDamping: true });
// Per-frame work goes on the application ticker.
app.ticker.add((ticker) => {
duck.rotation.y += 0.01 * ticker.deltaTime;
});
Conventions
- Scene graph. Everything 3D goes under
view.root(aContainer3D); a mesh onapp.stagedoes nothing useful. Nodes haveposition,rotation(Euler radians),quaternion,scale,visible,lookAt(point),addChild,traverse(callback)over a subtree, and bounds viagetLocalBounds(). - Options objects. Constructors take one options object:
new Mesh3D({ geometry, material, position: { x, y, z }, castShadow, parent: view.root }). The same values are plain properties afterwards (mesh.position.set(x, y, z)). - Camera.
view.camerais aCamera3D:fovis the vertical field of view in degrees (default 60),near0.1,far1000,mode'perspective'or'orthographic'withorthographicSize(half the view's width in world units, default 5). Aim it withposition.set(...)andlookAt(point). - Facing. Cameras and lights look down their local -Z; meshes and glTF models face +Z. A
DirectionalLightis aimed by itsrotation(orlookAt), not a target. - Planes.
PlaneGeometrylies in the XY plane with a +Z normal. For a floor, rotate it -π/2 around X. - Primitive options.
CubeGeometry({ width, height, depth }),SphereGeometry({ radius }),CylinderGeometry({ radiusTop, radiusBottom, height })(noradius),ConeGeometry({ radius, height }),CapsuleGeometry({ radius, height })(height includes the caps),TorusGeometry({ radius, tube })(lies in XZ),PlaneGeometry({ width, height }),IcosahedronGeometry({ radius, detail }). Each also takes segment counts. - Lighting.
DirectionalLight,PointLight,SpotLight,AmbientLightare nodes. Soft fill comes from the view'senvironment:{ skyColor, groundColor, intensity }for a hemisphere, or aCubeTexture(.cubemapor.hdrviaAssets.load) for image-based lighting. Shadows:castShadowon the light,castShadow/receiveShadowon meshes,shadowQuality('low' | 'medium' | 'high' | 'ultra') on the view. - Materials.
Material3Dis physically based (baseColor,metallic,roughness,baseColorTexture,alphaMode,alpha); glow is an opt-in group,emissive: { color, strength }, never a plain colour.FlatMaterialis unlit. Custom looks come from shader bits inextraBits, not a ShaderMaterial class.material.clone(options)copies a material with options changed or bits added. - Models.
Assets.load<Scene3DSource>(url)thennew Model3D({ source }). Animation clips play throughmodel.player. Models from one source share its materials;model.materialsgives one model its own. - Extras.
OrbitCamera,FPSCamera,ParticleSystem3D,BloomEffect,SsaoEffect,TiltShiftEffect,MotionBlurEffect,TintEffect,FxaaEffect,ToonMaterial,PortalPass,PortalMaterial,dissolveBitlive in@pixi/3d/extras. - Many copies.
BatchedMesh3Ddraws thousands of instances in one call (setMatrixAt,setInstanceDataAt). SeparateMesh3Dnodes batch into instanced draws on their own when they share a geometry, the same kind of material and the same texture sources: the frames of one sprite sheet count as one source, soSprite3Ds or textured meshes showing different frames of one sheet still draw together. Pack one effect's images into one sheet. - 2D over 3D. HUD text, buttons and sprites are ordinary Pixi objects added to the stage after the view.
view.worldToScreen(point)places a 2D label at a 3D position; it always draws over the 3D. For a picture inside the scene that walls can hide, usenew Sprite3D({ texture, height, billboardMode, parent: view.root }). - The app around it is pixi.js.
Applicationandawait app.init,Assets,Text, pointer events and the ticker are pixi.js v8; when thepixijsskill is installed, use it for those. - Renderers.
app.inituses WebGL unless you passpreference: 'webgpu'; nothing else in your code changes. MSAA isapp.init({ antialias: true });View3Dhas noantialiasoption. - No enums. Options like
shadowQuality,alphaModeandmodeare string literals. There is noLightTypeorShadowQualityenum.
Pitfalls that fail silently
Each line is the fix; its link owns the rule. These run without errors and draw or behave wrongly. Check the groups for what you built.
Scene graph and math
lookAt,reparentChildand loaded glTF nodes setquaternion, and a node turns by whichever ofrotationorquaternionwas written last: on those nodesrotation.y += xreplaces the real turn, so turn them withquaternion.multiply(q). scene-graphaddChildkeeps a node's local numbers, so it jumps to the same offset under its new parent;reparentChildkeeps its place in the world. scene-graphview.rootholds the camera: clearing or hidingview.rootmakes rendering throw. Put level content under its ownContainer3Dand clear that. scene-graphgetLocalBounds()is in the node's own space and leaves out hidden children: for a world box usegetLocalBounds().applyMatrix4(node.getGlobalTransform()). mathVector3math works in place (a.add(b)changesa; pass anoutto keep it) and usessubtract,multiplyScalar,magnitudeandcopyFrom. A node'spositionis live, so copy what you keep. math- A ground plane at height
hisnew Plane(new Vector3(0, 1, 0), -h)(note the minus), orsetFromNormalAndCoplanarPoint. math
Light and colour
- Views default to
toneMapping: 'none', where lit colour clips above 1: settoneMapping: 'aces'for strong lights, HDR environments or bloom. environment-and-sky - Porting from Three.js: a HemisphereLight(sky, ground, i) becomes
environment: { skyColor, groundColor, intensity: i / Math.PI }; directional, point, spot and ambient intensities port 1:1. from-threejs - Direct lights and
AmbientLightare divided by π and the hemisphere is not: a sun near 3 and a hemisphere near 0.7 lightbaseColorfully, and anAmbientLightof 0.2 is barely visible. scene-and-lighting - Vertex colours are linear light: convert sRGB picks with
srgbToLinear, or mid tones come out pale. geometry - A view shades 32 lights and 16 shadow maps; past 256 casters the sun's shadow box is measured once, so call
sun.fitShadowBounds()after the scene changes. scene-and-lighting
Surfaces
alphaModedefaults to'opaque', which ignoresalphaand texture alpha (transparent PNG texels draw black): set'blend'or'mask'. materials- A
'mask'cut-out, or a shader bit that moves or discards vertices (dissolveBit), still casts the whole undisplaced shadow and outline: give itcastShadow: falseandmaterial.disablePass('outline')if that shows. materials, custom-shaders - Images show upside down on
PlaneGeometryunlessflipV: true, and onCubeGeometryfaces unless the material hasuvTransform: new Matrix(1, 0, 0, -1, 0, 1)(Matrixfrom pixi.js). from-threejs - A
ContainerTextureon an opaque material draws black where the 2D is transparent: add a backdrop,clearColor, oralphaMode: 'blend'. render-to-texture
Models and animation
- A glTF file's own node transforms already size it and stand it Y-up facing +Z: read
getLocalBounds()before scaling and add no fix-up rotation. models-and-animation - A loaded model draws its source's materials, so a write to one changes every model from that file: give one model copies with
model.materials = (m) => m.clone(), and build a changed loaded material withclone(options), notnew Material3D, which drops its textures. models-and-animation - Most bones aren't scene nodes, so
getChildByLabel('Hand', true)returns null: usemodel.skeletons[0]. models-and-animation - Write into
mesh.weights; assigning a new array disconnects it from the clips that animate it. models-and-animation - A
Mesh3Dwith no geometry or material draws nothing and raises no error. geometry
Input and cameras
-
OrbitCamera,FPSCamera,ParticleSystem3Dand a model's animationplayerupdate themselves onTicker.shared, notapp.ticker, so their step can land after your frame's code or a frame late: passautoUpdate: falseand callupdate(ticker)from yourapp.tickercallback when order matters (orbit.update()returnstruewhen the camera moved). cameras -
3D nodes default to
eventMode: 'passive': set'static'(also aMesh3Doption) or nothing picks.pointertapfires for every mouse button, so checke.button. picking-and-input -
A full-screen view hit-tests its whole rectangle, so it blocks clicks to 2D objects behind it: put interactive 2D above the view or set
view.hitArea. 2d-and-3d-together -
OrbitCamera.enabled = falseonly stops input;update()keeps re-aiming the camera, so also setautoUpdate = false(ordestroy()) before moving the camera yourself. cameras -
FPSCamerawalking follows the pitch, so looking down sinks the camera: re-pinposition.yafterupdate()for a ground walker. cameras
Effects and render to texture
BloomEffect's threshold is 1 in linear light: make glowing thingsemissivewithstrengthabove 1; a threshold near 0.5 blooms every lit surface. MSAA carries through effects, so addFxaaEffectonly withoutantialias. post-processing- A camera feed shown on a mesh under a filmic curve (
'aces') goes through the curve twice and looks washed out;format: 'rgba16float'is the documented fix with bloom-style effects, but on aFlatMaterialmonitor it currently draws too dark, so compare the screen with the scene by eye, or show the feed in a 2DSprite. Make feeds withantialias: trueor their edges are jagged. render-to-texture - Toggle effects with
enabled; adding the first or removing the last pre-curve effect recompiles every material. post-processing
Particles
ParticleContainer3Dshows nothing untilupload()aftersetParticleAt. particles
Current library limitations (workarounds until they're fixed)
- A glTF mesh without normals (Fox.glb is one) lights as if every face pointed +Z, black when it turns away: rebuild flat normals with
getAttributeData. from-threejs SphereGeometrymaps images mirrored left to right:uvTransform: new Matrix(-1, 0, 0, 1, 1, 0)plusrotation.y = Math.PIfixes it. from-threejs- A
Sprite3Dcreated with noposition,scaleoranchordraws 1×1 units instead of its texture size until one of them changes: always pass aposition. 2d-and-3d-together
Which reference to open
| job | file |
|---|---|
| scene graph: parent and child, transforms, rotation vs quaternion, lookAt, local and world space, find by label, reparent, bounds and size, visibility, scene events, destroy | references/scene-graph.md |
| vectors, quaternions, matrices, smooth turning, boxes, spheres, rays, planes, frustum tests, spherical orbits | references/math.md |
| scene, ground, sun, point and spot lights, shadows | references/scene-and-lighting.md |
| load a glTF, frame it, play and cross-fade clips, one model's own materials, dissolve, bones, morph targets | references/models-and-animation.md |
| PBR, unlit, toon, emissive, transparency, textures | references/materials.md |
| built-in geometries, custom vertex data, terrain, lines and points | references/geometry.md |
| thousands of instances, per-instance data, batching, LOD | references/instancing.md |
| click, hover, drag, raycast, screen-to-world, keyboard | references/picking-and-input.md |
| camera control, orbit, first-person, orthographic, clipping, follow | references/cameras.md |
| skybox, cubemaps, HDR (.hdr), image-based lighting, fog, tone mapping | references/environment-and-sky.md |
| bloom, SSAO, tilt-shift, motion blur, FXAA, effect order, scene resolution | references/post-processing.md |
| emitters, bursts, hand-driven particles | references/particles.md |
| second camera to a texture, live 2D on a surface, portals | references/render-to-texture.md |
| HUD, labels, Sprite3D billboards, 2D filters and masks, several views, resolution | references/2d-and-3d-together.md |
| shader bits, custom material effects | references/custom-shaders.md |
| port a Three.js app: paired program, differences that change the picture, full name tables | references/from-threejs.md |
If you know Three.js or the unrelated pixi3d package
| you may reach for | use here |
|---|---|
Scene, renderer.render | View3D on app.stage; Pixi renders it |
| renderer.setAnimationLoop | app.ticker.add |
| PerspectiveCamera, OrthographicCamera | view.camera; camera.mode = 'orthographic' + orthographicSize |
| OrbitControls, CameraOrbitControl | OrbitCamera (extras) |
Mesh(geometry, material), Mesh3D.createCube() | new Mesh3D({ geometry: new CubeGeometry(), material }) |
| MeshStandardMaterial, StandardMaterial | Material3D |
| MeshBasicMaterial | FlatMaterial |
| ShaderMaterial | a ShaderBit in extraBits |
| HemisphereLight, LightingEnvironment, ImageBasedLighting | the view's environment |
| ShadowCastingLight | castShadow: true on a light |
GLTFLoader, Model.from | Assets.load<Scene3DSource> + new Model3D({ source }) |
| AnimationMixer | model.player |
| InstancedMesh | BatchedMesh3D |
| Raycaster | pointer events on meshes, or view.raycast |
| Box3 | node.getLocalBounds() |
| EffectComposer | view.passes with effects from extras |
| CSS2DRenderer | Pixi Text on the stage + worldToScreen |
Every other Three.js name, and the behaviour that differs after a rename, is in references/from-threejs.md.