Skip to content

Vertex Formats and GPU Instancing ​

GPU instancing 2.2.0

GPU-instanced particles previewed in the maximized editor

The instance project exercises the particle instance format while the full editor keeps Scene, Inspector, Resources, and Timeline visible.

Photon batches equal geometry/material state and draws many instances together. Base mesh vertices describe a quad, model, or trail segment; per-instance records supply transform, color, UV, light, and optional data.

Render Variants ​

DefineGeometryBase instance content
PARTICLE_INSTANCEbillboard/tile quadposition, rotation, size, color, UV/light
PARTICLE_MODEL_INSTANCEmodel meshtransform, color, UV/light with model attributes; optional tangent
TRAIL_INSTANCEflat/tube trail segmentendpoints, width/color/UV per point
BEAM_INSTANCEray/beam segmentstart/end, width/color/UV
ARA_TRAIL_INSTANCE / ARA_TRAIL_TUBE_INSTANCEflat / tube AraTrail segmentadjacent point data and interpolation

Non-instanced CPU paths still expose Particle Data. Changed 2.2.8 Since 2.2.8, a non-instanced Particle Emitter also uploads per-particle Additional and Custom Data records when one of its materials reads them (a Shader Graph or Kila Material). Trails, beams and AraTrail still need GPU instancing for those streams; without it they read zero.

Extra Defines ​

Photon adds these defines on top of a render variant:

DefineAdded toVersionMeaning
PHOTON_TANGENTPARTICLE_MODEL_INSTANCESince 2.2.5The Model render mode's tangent toggle is on. Adds layout(location = 3) in vec4 aTangent (xyz = tangent, w = handedness).
PHOTON_VATPARTICLE_MODEL_INSTANCESince 2.2.7An animated glTF model plays per particle. The vertex shader reads the pose from a baked table (PhotonVat).
PHOTON_CPU_DATAthe CPU path (no instance define)Since 2.2.8A Particle Emitter draws without instancing and a material reads particle data. Records are found per particle, not by gl_InstanceID.

getParticleData() handles all three. With PHOTON_TANGENT, the model's per-instance attributes stay at locations 4–8 and legacy Custom Data still starts at 9. The tangent toggle needs GPU instancing; glTF models keep their own tangents, and other models get tangents generated from their UVs.

ParticleData.Tangent and ObjectTangent carry the tangent frame. Instanced billboards, trails, beams and AraTrail build an exact tangent in the vertex shader, so they need no toggle. Model instancing without the toggle and the non-instanced CPU path report a constant vec4(1, 0, 0, 1). See glTF Models for animated models.

Two Data Layouts ​

The legacy attribute layout contains only inspector-enabled channels, in registry order. Turning a channel on can move later attribute locations. A hand-written shader and emitter configuration must therefore agree exactly.

Shader Graph uses a fixed vec4 slot layout per emitter kind, indexed by gl_InstanceID (per particle on the PHOTON_CPU_DATA path). The compiler records which channels are read and uploads the effective mask. One compiled graph can serve emitters with different inspector toggles.

Batching Limits ​

A draw batch breaks when geometry, material program, texture/sampler state, render layer, or required layout differs. Multi-material models require separate passes. Large custom data and many transparent layers also increase upload and sorting cost even when instance count is low.

Use the profiler and render statistics rather than assuming instancing is always faster. For a handful of particles, setup cost may dominate; for hundreds of identical quads/models, it normally wins.

Shader Author Checklist ​

  • Shader Graph uses Photon accessors. The legacy Custom Shader Material path must declare attribute locations matching the emitter toggles.
  • Read PhotonData through the photon_data_*() accessors instead of indexing it yourself: instanced draws index it by gl_InstanceID, the PHOTON_CPU_DATA path per particle.
  • Treat unsupported channels as zero and provide a sensible fallback.
  • Keep transparent sort/layer requirements consistent so emitters can batch.
  • Test tile, model, trail, and beam separately; their base inputs are not interchangeable.

See Additional GPU Data for the Custom Data location formula and complete shader code.

Released under the MIT License.