v2.22.0
CoreBunch/Instaticv2.22.0Sep 4, 2026by mvaligursky
AI Summary
A major feature release focusing on WebGPU performance enhancements, advanced rendering capabilities like flat shading and volumetric fog, and significant updates to the Gaussian Splatting and physics systems.
Key Highlights
- Added WebGPU support for multisampled textures and independent blending for multiple render targets
- Enhanced Gaussian Splatting with LOD modes, depth writing, and tonemapping options
- Introduced `WireRenderer` for debug shapes and added physics `timeScale` controls
- Added Camera projection offset for off-center projections
Breaking Changes
- Store frustum planes in a typed array
- Remove wire drawing methods from `AppBase`
New Features
- Dual-source blending support
- Camera projection offset for off-center projections
- Volumetric fog support for clustered lights
- Independent blending for multiple render targets
- WireRenderer for debug shapes
Full Release Notes
## Breaking Changes * [BREAKING] Store frustum planes in a typed array by @mvaligursky in #9196 * [BREAKING] Remove wire drawing methods from `AppBase` by @mvaligursky in #9289 ## Changes * Remove `Scene#forcePassThroughSpecular` compatibility flag by @mvaligursky in #9250 * Remove passive event listener detection and `platform.passiveEvents` by @willeastcott in #9252 * Remove unused `BatchManager#clone` method by @mvaligursky in #9106 * Add dual-source blending support by @mvaligursky in #9096 * Add `Camera.projectionOffset` for off-center (shift lens) projection by @mvaligursky in #9098 * Add `RenderTarget` origin option, deprecate `flipY` by @mvaligursky in #9101 * Expose Bayer 2/4 dither modes and add Bayer 16 dither mode by @mvaligursky in #9117 * Add shadow pass support for `ShaderMaterial` using `#include` by @mvaligursky in #9116 * Cull omni shadow casters against all six faces in a single pass by @mvaligursky in #9194 * Cull mesh instances by their bounding box instead of bounding sphere by @mvaligursky in #9197 * Add flat shading support by @mvaligursky in #9191 * Flat shade glTF primitives without vertex normals by @mvaligursky in #9193 * Add `shadowCascadeMask` to the render component by @mvaligursky in #9195 * Add `shadowCascadeMask` in batch by @AlexAPPi in #9104 * Configurable maximum number of clustered lights by @mvaligursky in #9188 * Add volumetric fog support for clustered omni and spot lights by @mvaligursky in #9137 * Support refraction without the metalness workflow by @mvaligursky in #9147 * Add independent blending for multiple render targets by @mvaligursky in #9140 * Add per-attachment clear colors for multiple render targets by @mvaligursky in #9142 * Add clear support for integer format color attachments by @mvaligursky in #9143 * Render the scene depth from the scene pass, replacing the depth prepass where possible by @mvaligursky in #9174 * Support writing the scene textures from the forward pass by @mvaligursky in #9166 * Add `SceneDepthReader` for reading a camera's scene depth to the CPU by @mvaligursky in #9274 * Add a scene depth mode to `CameraFrame` debug rendering by @mvaligursky in #9182 * Wire `alphaToCoverage` into the WebGPU render pipeline state by @mvaligursky in #9208 * Add `sample_mask` builtin support to WGSL fragment shaders by @mvaligursky in #9222 * Add `samples` option to `Texture` for multisampled textures on WebGPU by @mvaligursky in #9217 * Support multisampled texture bindings in WGSL reflection and bind groups by @SashaRX in #9167 * Support multisampled color buffers and custom resolves on `RenderTarget` by @mvaligursky in #9221 * Support multisampled depth buffers and depth resolve targets on `RenderTarget` by @mvaligursky in #9230 * Add `RenderTarget.depthResolveMode` controlling multisampled depth resolve by @mvaligursky in #9226 * Support blendable and R/RG float formats in `getRenderableHdrFormat` by @mvaligursky in #9164 * Expose `textureFloatBlendable` capability on all devices by @mvaligursky in #9160 * Support the `RG32F` texture format on WebGL by @mvaligursky in #9218 * Add readonly detection for the WebGPU subgroup-size-control feature by @mvaligursky in #9158 * Fix parallax mapping correctness and promote to public by @mvaligursky in #9207 * Add parallax occlusion mapping as an opt-in parallax mode by @mvaligursky in #9213 * Add soft self shadowing to parallax occlusion mapping by @mvaligursky in #9225 * Add `heightMapBase` to unify the parallax zero level across modes by @mvaligursky in #9245 * Add `WireRenderer` for wireframe debug shapes and a debug examples category by @mvaligursky in #9288 * Add `AppOptions.physicsWorld` for explicit physics backend selection by @willeastcott in #9266 * Add `timeScale` and public `step()` to `RigidBodyComponentSystem` by @willeastcott in #9300 * Wake constrained bodies when a joint constraint is created or destroyed by @willeastcott in #9177 * Add justified text support to text elements by @mvaligursky in #9185 * Allow typed arrays in geometry and mesh data APIs by @mvaligursky in #9121 * Add support for transform feedback separate buffer mode by @AlexAPPi in #9109 * Support multiple transform feedback buffers by @mvaligursky in #9131 * Add rotation attribute to the `ProceduralSky` script by @mvaligursky in #9150 * Detach element component graph nodes on destroy by @mvaligursky in #9183 * Release application references on destroy by @mvaligursky in #9190 * Destroy the asset registry with the application by @mvaligursky in #9189 * Relocate deprecated API shims so the ESM engine tree-shakes by @willeastcott in #9251 * Remove WebGL1-era POT checks from texture mipmap upload and `gpuSize` by @willeastcott in #9262 * Use `Geometry#calculateTangents` in procedural geometry subclasses by @willeastcott in #9253 * Stop the `Shader` constructor modifying the supplied definition by @willeastcott in #9281 ## Gaussian Splatting * Error-driven GSplat LOD selection with a derived fallback by @mvaligursky in #9233 * Add a LOD mode and per-splat detail falloff for streamed GSplats by @mvaligursky in #9267 * Gaussian splats write the scene depth by @mvaligursky in #9175 * Add `scene.gsplat.useTonemap` to opt GSplats out of tonemapping and exposure by @mvaligursky in #9229 * Use `shadowCasterPS` chunk for gsplat shadow output by @mvaligursky in #9119 * Fix unified CPU splat sort for non-uniformly scaled splats by @AriaLian in #9268 * Fix stale gsplat projector pipeline after a `dataFormat` swap by @mvaligursky in #9269 * Fix gsplat rendering on Firefox/Windows WebGPU by @mvaligursky in #9148 * Fix SOG parsers throwing when a load completes after app teardown by @mvaligursky in #9232 * Fix radix sort value buffer rotation with initial values by @slimbuck in #9276 * Fix blending disabled when generating SOG centers into an integer render target by @slimbuck in #9224 ## Fixes * Fix inverted normal mapping on WebGPU (derivative TBN) by @mvaligursky in #9099 * Fix MSDF coverage ramp exceeding field range at small sizes by @willeastcott in #9100 * Fix reported WebGPU sampled texture limit by @mvaligursky in #9103 * Discard out of range depth in VSM shadow casters by @mvaligursky in #9124 * Fix `textureBias` redefinition in tiled nine-slice shaders by @mvaligursky in #9129 * Fix the bright outline around MSDF text glyphs by @mvaligursky in #9139 * Fix vertex array object cache key collisions by @mvaligursky in #9127 * Fix instancing/draw-commands mismatch skipping indirect draws by @mvaligursky in #9159 * Fix phantom sampler binding for a trailing texture in WGSL reflection by @mvaligursky in #9198 * Fix `ScriptComponent` clone dropping scripts awaiting their script type by @mvaligursky in #9200 #9201 #9202 #9203 #9204 * Fix `AnimationComponent` leaking asset event handlers by @mvaligursky in #9141 * Fix component event leak when removing an enabled component by @mvaligursky in #9145 * Fix layer event leak when the scene layer composition is replaced by @mvaligursky in #9146 * Fix `XrMenu` rendering outside of XR sessions by @willeastcott in #9155 * Fix looping sounds that specify a start time and duration by @willeastcott in #9231 * Correct sound duration and playback timing by @mvaligursky in #9249 * Fix inverted entity name guard in `AnimComponentBinder` by @willeastcott in #9187 * Fix volumetric fog missing for one frame after a canvas resize by @mvaligursky in #9283 * Fix `'set:outline'` event reporting the text color by @willeastcott in #9260 * Handle `mapAsync` rejection when the WebGPU device is lost by @mvaligursky in #9115 * Fix `ComponentSystemRegistry.remove` leaving the system in the list by @willeastcott in #9280 * Settle the WebGL texture read when the device is destroyed by @mvaligursky in #9275 * Recalculate particle local bounds when the spawn volume changes by @mvaligursky in #9192 * Anchor the AppleWebKit 15.4 antialias workaround UA match by @willeastcott in #9265 ## Examples * Add VAT character crowd rendering scripts and example by @mvaligursky in #9123 * Add catapult siege demo example by @mvaligursky in #9130 * Add a parallax terrain example by @mvaligursky in #9223 * Support multiple transform feedback buffers and add a visual example by @mvaligursky in #9131 * Point the downtown example at a single full-scene streamed SOG bundle by @mvaligursky in #9290 * Make the examples MiniStats toggle drive example-owned instances by @mvaligursky in #9292 * Fix painter example render target orientation across APIs by @mvaligursky in #9108 ## Engine Dependencies * Update all npm dependencies (major) by @renovate[bot] in #9056 ### Contributors * @AlexAPPi * @AriaLian * @kpal81xd * @mvaligursky * @SashaRX * @slimbuck * @willeastcott ## New Contributors * @SashaRX made their first contribution in #9167 * @AriaLian made their first contribution in #9268 **Full Changelog**: https://github.com/playcanvas/engine/compare/v2.21.4...v2.22.0