Gaussian splatting for VRChat worlds, with runtime sorted rendering, standalone precomputed imports, and automatic editor Scene view sorting.
- Sorted-only runtime rendering through
GaussianSplatRenderer - Automatic editor-only Scene view sorting for every discovered
GaussianSplatObject - Importer for
.plysplats fromGaussian Splatting / Import PLY Splats... - Optional standalone precomputed-sorting import path for splats that should render without
GaussianSplatRenderer - Generated world-space UI from the renderer context menu
- Global/networked controls for:
- current splat selection
- SH band
- VRC Light Volumes
- Gaussian scale
- Local controls for:
- min/max sort distance
- camera quantization
- sorting steps
- sort every frame
- antialiasing
- light volume intensity
- alpha cutoff
- Import the unitypackage from releases, or clone the repo directly.
- Open
Gaussian Splatting / Import PLY Splats.... - Add one or more
.plyfiles and choose an output folder. - Configure the import options:
Compute Bounding BoxsRGB Color CorrectionImport Spherical HarmonicsDefault SH BandMulti-Pass RenderingSplat Count Per PassMax Alpha Mask CountPrecompute Sorting
- Import the splats.
- For the runtime sorted path, add the imported prefabs to a
GaussianSplatRendererin your scene. - Optionally use the renderer context menu to collect splats automatically, resize sorting textures, and generate a world-space control UI.
sRGB Color Correctionadds 2 extra grab passes. It fixes transparency/compositing behavior, but it is heavier. Without it, the renderer falls back to back-to-front blending, which also means multi-pass rendering will not work correctly.sRGB Color Correctiononly works correctly when the world uses HDR camera render targets.Multi-Pass Renderingsplits a splat into sequential chunks. This can improve VR rendering performance for large splats.Max Alpha Mask Countinserts optional alpha-mask passes between multi-pass chunks to occlude later chunks behind opaque geometry. This can help performance, but grab passes are expensive, so it is a tradeoff.Precompute Sortingbakes direction-based order into the imported data so the splat can render standalone, including outside the runtime renderer path, but it uses much more texture memory and can introduce artifacts.
For normally trained splats, exact color reproduction requires the color-space transform grab-pass path.
If you turn sRGB Color Correction off, there are two important side effects:
- Rendering order has to fall back to back-to-front blending because there is no grab pass caching the current view color, so the multi-pass optimization path is no longer applicable.
- Colors are no longer reproduced exactly. The color conversion still happens per splat, but the blending itself is no longer mathematically valid for the original training color space.
One workaround is to train the splats on images that were already color-converted into inverse sRGB space. Then the splats can be rendered without the runtime color-space conversion path, but you also need to turn off fake sRGB on the material.
Use this path when you want the splat to be camera-sorted at runtime in VRChat worlds (uses Udon):
- Add a
GaussianSplatRendererto the scene. - Add imported splat prefabs to its
splatObjectslist. - Or use the component context menu:
Collect Gaussian Splat Objects for the rendererUpdate Sorting Resource TexturesGenerate UI
- Enter play mode or build the world. The renderer selects one splat at a time and updates sorted render order for the active cameras.
Use Precompute Sorting in the importer when you want a splat to render without GaussianSplatRenderer - for avatars or in general outside of VRChat.
- This path bakes direction-based render order into the imported material data.
- It is a standalone import mode.
- It is not intended to be driven by
GaussianSplatRenderer.
Generate UI creates a world-space control canvas for the renderer.
Current synced/global controls:
Current Splat (global)Splat Selection (global)SH Band (global)VRC Light Volumes (global)Gaussian Scale (global)
Current local controls:
Min Sort DistanceMax Sort DistanceCamera QuantSorting StepsSort every frameAntialiasingLight Volume IntensityAlpha Cutoff (lower = better quality)
The generated UI is intended as a practical in-world control surface, not just a demo. The synced controls behave the same way as the selected splat index and update for other users.
- Large imports are still limited by available RAM.
- SH import memory now scales with the selected SH band instead of always allocating for the highest band.
- If
Import Spherical Harmonicsis disabled, the importer skips SH textures and forces SH0. - If SH import is enabled, the importer only creates textures up to the selected max/default band and falls back to the highest lower non-zero band when needed.
.plyfiles larger than 2 GB are still not supported.- If you have an especially large splat list on a renderer, use
Update Sorting Resource TexturesonGaussianSplatRendererto resize the sorting textures to fit the largest assigned splat instead of managing those assets by hand.
Tip
In VRChat, splats should not rely on MSAA. GaussianSplatRenderer disables game-mode MSAA, and leaving it enabled is usually just extra cost for little or no visual benefit on splats.
Tip
The renderer currently shows one selected splat at a time. If you need a splat to render without the runtime sorter, import it with Precompute Sorting instead.
Tip
Use Update Sorting Resource Textures after assigning splats to a renderer. That resizes the sorting textures to fit the largest assigned splat and is the preferred replacement for manually editing the radix-sort render textures.
Tip
Lower Alpha Cutoff keeps more splats and improves quality, but it also increases rendering cost. More Sorting Steps improve ordering accuracy, but they also make sorting more expensive.
Tip
sRGB Color Correction gives the exact color/compositing path for normally trained splats, but it adds 2 grab passes. Disabling it can be worthwhile for performance, but rendering falls back to back-to-front blending, multi-pass optimization no longer applies, and the blended color is no longer exact unless the splat data was trained for that path.
Tip
VRC Light Volumes is a scene-integration control. Leave it off if the splat should stay close to its baked appearance. Turn it on if you want the splat to pick up scene lighting, then tune Light Volume Intensity to control how strongly the sampled lighting affects it.
- Runtime rendering is sorted-only.
- The active runtime path uses sorted render-order textures and front-to-back compositing.
- SH selection is controlled numerically through
_SHBand. - Runtime SH band is clamped by the textures available on the imported material, so a splat cannot be pushed past the SH data it actually has.
- Material/render controls now include:
- Gaussian scale
- antialiasing
- alpha cutoff
- VRC Light Volumes on/off
- light volume intensity
- Game-mode MSAA is disabled by the renderer. Splats should not rely on MSAA for quality or performance.
The shader can integrate with VRC Light Volumes through the VRC Light Volumes (global) toggle and the Light Volume Intensity control.
- When enabled, the splat shader samples VRC Light Volume spherical-harmonic lighting at the splat world position.
- The sampled lighting is applied to the non-emissive part of the splat color, while values above
1.0are preserved as emissive. Light Volume Intensityscales the contribution of the sampled light volume lighting.- This affects shading only. It does not change the sorting path or render-order generation.
- Some splats look better as mostly self-lit imagery, while others benefit from picking up scene lighting, so this is intentionally exposed as a runtime control.
GaussianSplatRenderercurrently renders a single selected splat at a time. If you need standalone rendering without the runtime sorter, use the precomputed-sorting import path instead.- The sort texture size still matters for performance and memory. The renderer helper is now the preferred way to size these textures, but the underlying rule is the same: fit them to the padded element count of the largest splat you want to sort.
- For small splats or performance-constrained scenes, disabling sRGB correction can be worthwhile, but you are trading away correct transparency behavior.
- Lower alpha cutoff keeps more splats alive and improves visual quality, but it also increases rendering cost.
- More sorting steps improve order accuracy by sorting more bits of the distance key, but they also increase the sorting cost.
- Scene view sorting is automatic for
GaussianSplatObject. - It is editor-only and does not depend on Udon.
- It creates and owns its own transient sorting resources.
- It does not reuse the runtime
GaussianSplatRenderersorting textures or sceneRadixSortresources. - It skips standalone precomputed-sorting materials and only applies to the sorted runtime path.
GaussianSplatRendererrenders one selected splat at a time.- Standalone precomputed sorting is a separate import path and is not the same thing as runtime sorting.
- Very large splats can still be heavy to import and render even with the newer SH memory reductions.
- The current Scene view sorter targets Scene view cameras; inspector previews are not part of this pass.
.PLYimporter adapted from aras-p's UnityGaussianSplatting- This repository is a heavily modified version of lambdalemon's gaussian splats
- The radix sort uses d4rkpl4y3r's mipmap prefix sum trick
- My Gaussian Splat Gallery
- My Gaussian Splat Mega Gallery
- 双葉水辺公園 [ 3DGS × Photogrammetry ] — Tokoyoshi
- - 川北東橋 ⁄ Kawakita-higashi Bridge - 3DGS — DEKA_KEIJI777V
- Хотинська фортеця - Gaussian Splatting - 3Dimka
As this is VRChat, we only have access to the normal rasterization pipeline without writable textures, buffers, or atomics, so the sorting path has to stay inside ordinary rendering primitives.
The runtime sorter uses a radix sort built on mipmap-based prefix sums. For radix sorting you only need prefix sums over digit occurrences, and from that you can reconstruct the sorted sequence. In this implementation each step sorts 4 bits at a time over 16-value digits, which keeps the number of passes practical while still giving a useful quality/performance tradeoff through the Sorting Steps setting.
This same general sorted-order approach is now used both by the runtime renderer and by the automatic editor Scene view renderer, but the editor path owns its own materials and transient render textures instead of reusing the runtime scene resources.
The same core idea could be reused for other VRChat rendering or simulation problems where you need ordering but do not have access to compute-style GPU primitives.
Splats are still rendered as projected billboards, but the ellipse projection path has been updated substantially.
Instead of relying on emulated double precision, the current implementation uses a more stable float-only ellipse fitting approach built around sampling the projected tangent outline of the ellipsoid and fitting the screen-space ellipse from those samples. The math now includes guarded divisions, bounded intermediate values, and safer normalization paths to keep thin splats stable without the older extended-precision workaround.
Compared with normal 3DGS rendering, this avoids the center-Jacobian affine projection approximation entirely. Standard 3DGS uses the Jacobian of a local affine projection around the Gaussian center, which is fast but introduces projection error that shows up as blur, shape drift, and scene inconsistency, especially important for VR applications where the camera can be very near the splats and can have very large fields of view.
Numerically, this projection is intended to recover the same projected ellipse as exact ellipsoid-projection approaches such as "Projecting Gaussian Ellipsoids While Avoiding Affine Projection Approximation" (arXiv:2411.07579v2), rather than being a lower-quality substitute for them. The difference is in how that ellipse is obtained: here it is recovered through a practical float-based outline-sampling fit that stays robust inside VRChat's shader/runtime constraints.
That also gives this approach an important practical advantage: it can naturally extend to distorted camera models as well, instead of being tied only to the standard pinhole-style projection derivation.
Because splats are rendered as billboards, keeping the projected ellipse tight matters a lot for overdraw. The current projection path is aimed at preserving a practical, stable screen-space footprint for the Gaussian while avoiding the numerical instability that showed up on thin ellipsoids in the older implementation.
