Skip to main content

Custom Shaders

The PlayCanvas Engine lets you customize how Gaussian Splats are rendered by overriding the gsplatModifyVS shader chunk. The chunk is set on the scene-wide gsplat material (app.scene.gsplat.material), so a single custom shader applies to all splats in the scene.

View Live Example - See shader chunk customization in action with animated splats.

Multi Splat

Overridable Functions

The gsplatModifyVS chunk lets you override three functions in the splat vertex stage:

FunctionPurpose
modifySplatCenterTransform the splat center position (model space)
modifySplatRotationScaleAdjust the splat rotation quaternion and scale
modifySplatColorTransform the splat color and opacity
void modifySplatCenter(inout vec3 center);
void modifySplatRotationScale(vec3 originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale);
void modifySplatColor(vec3 center, inout vec4 color);

You only need to implement the functions you want to change.

How the Example Works

The live example above animates every splat with a sine-wave displacement and a golden color pulse. It comes together in three steps.

1. Write the shader chunk, overriding the functions you need. The example animates using a uTime uniform:

uniform float uTime;

void modifySplatCenter(inout vec3 center) {
float heightIntensity = center.y * 0.2;
center.x += sin(uTime * 5.0 + center.y) * 0.3 * heightIntensity;
}

void modifySplatRotationScale(vec3 originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale) {
// no modification
}

void modifySplatColor(vec3 center, inout vec4 clr) {
float sineValue = abs(sin(uTime * 5.0 + center.y));
vec3 gold = vec3(1.0, 0.85, 0.0);
float blend = smoothstep(0.9, 1.0, sineValue);
clr.xyz = mix(clr.xyz, gold, blend);
}

2. Apply the chunk to the scene gsplat material, then update the material so it recompiles. Setting both the GLSL and WGSL chunk covers WebGL and WebGPU devices:

const sceneMat = app.scene.gsplat.material;

sceneMat.getShaderChunks('glsl').set('gsplatModifyVS', glslVertShader);
sceneMat.getShaderChunks('wgsl').set('gsplatModifyVS', wgslVertShader);
sceneMat.update();

3. Drive any uniforms each frame:

let currentTime = 0;
app.on('update', (dt) => {
currentTime += dt;
sceneMat.setParameter('uTime', currentTime);
sceneMat.update();
});

Removing a Custom Shader

To revert to default rendering, delete the chunk override and update the material:

const sceneMat = app.scene.gsplat.material;
sceneMat.getShaderChunks('glsl').delete('gsplatModifyVS');
sceneMat.getShaderChunks('wgsl').delete('gsplatModifyVS');
sceneMat.update();

See Also