playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
145 lines (117 loc) • 4.11 kB
JavaScript
var gsplat_default = (
/* wgsl */
`
varying gaussianUV: half2;
varying gaussianColor: half4;
varying id: f32;
const discardVec: vec4f = vec4f(0.0, 0.0, 2.0, 1.0);
varying vLinearDepth: f32;
varying @interpolate(flat) vPickId: u32;
uniform colorRampIntensity: f32;
var colorRamp: texture_2d<f32>;
var colorRampSampler: sampler;
@vertex
fn vertexMain(input: VertexInput) -> VertexOutput {
var output: VertexOutput;
// read gaussian details
var source: SplatSource;
if (!initSource(&source)) {
output.position = discardVec;
return output;
}
var modelCenter: vec3f = getCenter();
var center: SplatCenter;
center.modelCenterOriginal = modelCenter;
modifySplatCenter(&modelCenter);
center.modelCenterModified = modelCenter;
if (!initCenter(modelCenter, ¢er)) {
output.position = discardVec;
return output;
}
// project center to screen space
var corner: SplatCorner;
if (!initCorner(&source, ¢er, &corner)) {
output.position = discardVec;
return output;
}
// read color (~11 bit source data, use half precision)
let opacity = getOpacity(); // must run before getColor() to cache color data
var clr: half4 = half4(vec4f(getColor(), opacity));
var clr: half4 = half4(getColor());
clr.a = clr.a * corner.aaFactor;
// evaluate spherical harmonics
// calculate the model-space view direction
let modelView3x3 = mat3x3f(center.modelView[0].xyz, center.modelView[1].xyz, center.modelView[2].xyz);
let dir = normalize(center.view * modelView3x3);
// read sh coefficients
var sh: array<half3, SH_COEFFS>;
var scale: f32;
readSHData(&sh, &scale);
// evaluate (SH coefficients and result are half precision)
clr = half4(clr.xyz + evalSH(&sh, dir) * half(scale), clr.a);
// user hook operates in f32
var clrF32 = vec4f(clr);
modifySplatColor(modelCenter, &clrF32);
clr = half4(clrF32);
// discard splats with alpha too low (threshold matches fragment pass)
let alphaClipValue = half(uniform.alphaClip);
let alphaClipValue = half(uniform.alphaClipForward);
if (clr.w <= alphaClipValue) {
output.position = discardVec;
return output;
}
clipCorner(&corner, clr.w);
// write output
// 2DGS: Project world corner directly
let modelCorner: vec3f = center.modelCenterModified + corner.offset;
output.position = uniform.matrix_projection * center.modelView * vec4f(modelCorner, 1.0);
// 3DGS: Add clip-space offset to projected center
output.position = center.proj + vec4f(corner.offset.xyz, 0.0);
output.gaussianUV = corner.uv;
// Overdraw visualization mode: color by elevation
let t: f32 = clamp(center.modelCenterOriginal.y / 20.0, 0.0, 1.0);
let rampColor: vec3f = textureSampleLevel(colorRamp, colorRampSampler, vec2f(t, 0.5), 0.0).rgb;
clr.a = clr.a * half(1.0 / 32.0) * half(uniform.colorRampIntensity);
output.gaussianColor = half4(half3(rampColor), clr.a);
output.gaussianColor = half4(half3(prepareOutputFromGamma(max(vec3f(clr.xyz), vec3f(0.0)), -center.view.z)), clr.w);
output.id = f32(splat.index);
output.vLinearDepth = -center.view.z;
output.vPickId = loadPcId().r;
return output;
}
`
);
export {
gsplat_default as default
};