Skip to content

Commit 4081c8d

Browse files
mvaligurskyMartin Valigursky
andauthored
Converted remaining Gsplat shader chunks to WGSL (#7650)
Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
1 parent d343e4d commit 4081c8d

File tree

16 files changed

+619
-23
lines changed

16 files changed

+619
-23
lines changed

examples/assets/splats/skull.ply

14.4 MB
Binary file not shown.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { deviceType, rootPath } from 'examples/utils';
2+
import * as pc from 'playcanvas';
3+
4+
5+
const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas'));
6+
window.focus();
7+
8+
const gfxOptions = {
9+
deviceTypes: [deviceType],
10+
glslangUrl: `${rootPath}/static/lib/glslang/glslang.js`,
11+
twgslUrl: `${rootPath}/static/lib/twgsl/twgsl.js`,
12+
13+
// disable antialiasing as gaussian splats do not benefit from it and it's expensive
14+
antialias: false
15+
};
16+
17+
const device = await pc.createGraphicsDevice(canvas, gfxOptions);
18+
device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
19+
20+
const createOptions = new pc.AppOptions();
21+
createOptions.graphicsDevice = device;
22+
createOptions.mouse = new pc.Mouse(document.body);
23+
createOptions.touch = new pc.TouchDevice(document.body);
24+
25+
createOptions.componentSystems = [
26+
pc.RenderComponentSystem,
27+
pc.CameraComponentSystem,
28+
pc.LightComponentSystem,
29+
pc.ScriptComponentSystem,
30+
pc.GSplatComponentSystem
31+
];
32+
createOptions.resourceHandlers = [pc.TextureHandler, pc.ContainerHandler, pc.ScriptHandler, pc.GSplatHandler];
33+
34+
const app = new pc.AppBase(canvas);
35+
app.init(createOptions);
36+
37+
// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
38+
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
39+
app.setCanvasResolution(pc.RESOLUTION_AUTO);
40+
41+
// Ensure canvas is resized when window changes size
42+
const resize = () => app.resizeCanvas();
43+
window.addEventListener('resize', resize);
44+
app.on('destroy', () => {
45+
window.removeEventListener('resize', resize);
46+
});
47+
48+
const assets = {
49+
skull: new pc.Asset('gsplat', 'gsplat', { url: `${rootPath}/static/assets/splats/skull.ply` }),
50+
orbit: new pc.Asset('script', 'script', { url: `${rootPath}/static/scripts/camera/orbit-camera.js` }),
51+
hdri_street: new pc.Asset(
52+
'hdri',
53+
'texture',
54+
{ url: `${rootPath}/static/assets/hdri/st-peters-square.hdr` },
55+
{ mipmaps: false }
56+
)
57+
};
58+
59+
const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets);
60+
assetListLoader.load(() => {
61+
app.start();
62+
63+
// create a splat entity and place it in the world
64+
const skull = new pc.Entity();
65+
skull.addComponent('gsplat', {
66+
asset: assets.skull,
67+
castShadows: true
68+
});
69+
skull.setLocalPosition(-1.5, 0.05, 0);
70+
skull.setLocalEulerAngles(180, 90, 0);
71+
skull.setLocalScale(0.7, 0.7, 0.7);
72+
app.root.addChild(skull);
73+
74+
// set alpha clip value, used by shadows and picking
75+
skull.gsplat.material.setParameter('alphaClip', 0.4);
76+
skull.gsplat.material.setParameter('alphaClip', 0.1);
77+
78+
// Create an Entity with a camera component
79+
const camera = new pc.Entity();
80+
camera.addComponent('camera', {
81+
clearColor: new pc.Color(0.2, 0.2, 0.2),
82+
toneMapping: pc.TONEMAP_ACES
83+
});
84+
camera.setLocalPosition(-2, 1.5, 2);
85+
86+
// add orbit camera script with a mouse and a touch support
87+
camera.addComponent('script');
88+
camera.script.create('orbitCamera', {
89+
attributes: {
90+
inertiaFactor: 0.2,
91+
focusEntity: skull,
92+
distanceMax: 60,
93+
frameOnStart: false
94+
}
95+
});
96+
camera.script.create('orbitCameraInputMouse');
97+
camera.script.create('orbitCameraInputTouch');
98+
app.root.addChild(camera);
99+
100+
// create ground to receive shadows
101+
const material = new pc.StandardMaterial();
102+
material.diffuse = new pc.Color(0.5, 0.5, 0.4);
103+
material.gloss = 0.2;
104+
material.metalness = 0.5;
105+
material.useMetalness = true;
106+
material.update();
107+
108+
const ground = new pc.Entity();
109+
ground.addComponent('render', {
110+
type: 'box',
111+
material: material,
112+
castShadows: false
113+
});
114+
ground.setLocalScale(10, 1, 10);
115+
ground.setLocalPosition(0, -0.45, 0);
116+
app.root.addChild(ground);
117+
118+
// shadow casting directional light
119+
// Note: it does not affect gsplat, as lighting is not supported there currently
120+
const directionalLight = new pc.Entity();
121+
directionalLight.addComponent('light', {
122+
type: 'directional',
123+
color: pc.Color.WHITE,
124+
castShadows: true,
125+
intensity: 1,
126+
shadowBias: 0.2,
127+
normalOffsetBias: 0.05,
128+
shadowDistance: 10,
129+
shadowIntensity: 0.5,
130+
shadowResolution: 2048,
131+
shadowType: pc.SHADOW_PCSS_32F,
132+
penumbraSize: 10,
133+
penumbraFalloff: 4,
134+
shadowSamples: 16,
135+
shadowBlockerSamples: 16
136+
});
137+
directionalLight.setEulerAngles(55, 90, 20);
138+
app.root.addChild(directionalLight);
139+
});
140+
141+
export { app };
8.96 KB
Binary file not shown.
660 Bytes
Binary file not shown.

src/scene/gsplat/gsplat-material.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CULLFACE_NONE, SEMANTIC_ATTR13, SEMANTIC_POSITION } from '../../platform/graphics/constants.js';
22
import { BLEND_NONE, BLEND_PREMULTIPLIED, DITHER_NONE } from '../constants.js';
33
import { ShaderMaterial } from '../materials/shader-material.js';
4+
import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
45
import { shaderChunks } from '../shader-lib/chunks/chunks.js';
56

67
/**
@@ -27,6 +28,8 @@ const createGSplatMaterial = (options = {}) => {
2728
uniqueName: 'SplatMaterial',
2829
vertexGLSL: options.vertex ?? shaderChunks.gsplatVS,
2930
fragmentGLSL: options.fragment ?? shaderChunks.gsplatPS,
31+
vertexWGSL: shaderChunksWGSL.gsplatVS,
32+
fragmentWGSL: shaderChunksWGSL.gsplatPS,
3033
attributes: {
3134
vertex_position: SEMANTIC_POSITION,
3235
vertex_id_attrib: SEMANTIC_ATTR13

src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,17 @@ import fresnelSchlickPS from './lit/frag/fresnelSchlick.js';
4444
import fullscreenQuadVS from './common/vert/fullscreenQuad.js';
4545
import gammaPS from './common/frag/gamma.js';
4646
import glossPS from './standard/frag/gloss.js';
47-
// import gsplatCenterVS from './gsplat/vert/gsplatCenter.js';
48-
// import gsplatColorVS from './gsplat/vert/gsplatColor.js';
47+
import gsplatCenterVS from './gsplat/vert/gsplatCenter.js';
48+
import gsplatColorVS from './gsplat/vert/gsplatColor.js';
4949
import gsplatCommonVS from './gsplat/vert/gsplatCommon.js';
50-
// import gsplatCompressedDataVS from './gsplat/vert/gsplatCompressedData.js';
51-
// import gsplatCompressedSHVS from './gsplat/vert/gsplatCompressedSH.js';
52-
// import gsplatCornerVS from './gsplat/vert/gsplatCorner.js';
53-
// import gsplatDataVS from './gsplat/vert/gsplatData.js';
54-
// import gsplatOutputVS from './gsplat/vert/gsplatOutput.js';
50+
import gsplatCompressedDataVS from './gsplat/vert/gsplatCompressedData.js';
51+
import gsplatCompressedSHVS from './gsplat/vert/gsplatCompressedSH.js';
52+
import gsplatCornerVS from './gsplat/vert/gsplatCorner.js';
53+
import gsplatDataVS from './gsplat/vert/gsplatData.js';
54+
import gsplatOutputVS from './gsplat/vert/gsplatOutput.js';
5555
import gsplatPS from './gsplat/frag/gsplat.js';
56-
// import gsplatSHVS from './gsplat/vert/gsplatSH.js';
57-
// import gsplatSourceVS from './gsplat/vert/gsplatSource.js';
56+
import gsplatSHVS from './gsplat/vert/gsplatSH.js';
57+
import gsplatSourceVS from './gsplat/vert/gsplatSource.js';
5858
import gsplatVS from './gsplat/vert/gsplat.js';
5959
import immediateLinePS from './internal/frag/immediateLine.js';
6060
import immediateLineVS from './internal/vert/immediateLine.js';
@@ -259,17 +259,17 @@ const shaderChunksWGSL = {
259259
fullscreenQuadVS,
260260
gammaPS,
261261
glossPS,
262-
// gsplatCenterVS,
263-
// gsplatCornerVS,
264-
// gsplatColorVS,
262+
gsplatCenterVS,
263+
gsplatCornerVS,
264+
gsplatColorVS,
265265
gsplatCommonVS,
266-
// gsplatCompressedDataVS,
267-
// gsplatCompressedSHVS,
268-
// gsplatDataVS,
269-
// gsplatOutputVS,
266+
gsplatCompressedDataVS,
267+
gsplatCompressedSHVS,
268+
gsplatDataVS,
269+
gsplatOutputVS,
270270
gsplatPS,
271-
// gsplatSHVS,
272-
// gsplatSourceVS,
271+
gsplatSHVS,
272+
gsplatSourceVS,
273273
gsplatVS,
274274
immediateLinePS,
275275
immediateLineVS,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export default /* wgsl */`
2+
uniform matrix_model: mat4x4f;
3+
uniform matrix_view: mat4x4f;
4+
uniform matrix_projection: mat4x4f;
5+
6+
// project the model space gaussian center to view and clip space
7+
fn initCenter(modelCenter: vec3f, center: ptr<function, SplatCenter>) -> bool {
8+
let modelView: mat4x4f = uniform.matrix_view * uniform.matrix_model;
9+
let centerView: vec4f = modelView * vec4f(modelCenter, 1.0);
10+
11+
// early out if splat is behind the camera
12+
if (centerView.z > 0.0) {
13+
return false;
14+
}
15+
16+
var centerProj: vec4f = uniform.matrix_projection * centerView;
17+
18+
// ensure gaussians are not clipped by camera near and far
19+
centerProj.z = clamp(centerProj.z, 0.0, abs(centerProj.w));
20+
21+
center.view = centerView.xyz / centerView.w;
22+
center.proj = centerProj;
23+
center.projMat00 = uniform.matrix_projection[0][0];
24+
center.modelView = modelView;
25+
return true;
26+
}
27+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default /* wgsl */`
2+
3+
var splatColor: texture_2d<f32>;
4+
5+
fn readColor(source: ptr<function, SplatSource>) -> vec4f {
6+
return textureLoad(splatColor, source.uv, 0);
7+
}
8+
`;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
export default /* wgsl */`
2+
var packedTexture: texture_2d<u32>;
3+
var chunkTexture: texture_2d<f32>;
4+
5+
// work values
6+
var<private> chunkDataA: vec4f; // x: min_x, y: min_y, z: min_z, w: max_x
7+
var<private> chunkDataB: vec4f; // x: max_y, y: max_z, z: scale_min_x, w: scale_min_y
8+
var<private> chunkDataC: vec4f; // x: scale_min_z, y: scale_max_x, z: scale_max_y, w: scale_max_z
9+
var<private> chunkDataD: vec4f; // x: min_r, y: min_g, z: min_b, w: max_r
10+
var<private> chunkDataE: vec4f; // x: max_g, y: max_b, z: unused, w: unused
11+
var<private> packedData: vec4u; // x: position bits, y: rotation bits, z: scale bits, w: color bits
12+
13+
fn unpack111011(bits: u32) -> vec3f {
14+
return (vec3f((vec3<u32>(bits) >> vec3<u32>(21u, 11u, 0u)) & vec3<u32>(0x7ffu, 0x3ffu, 0x7ffu))) / vec3f(2047.0, 1023.0, 2047.0);
15+
}
16+
17+
fn unpack8888(bits: u32) -> vec4f {
18+
return vec4f(
19+
f32((bits >> 24u) & 0xffu),
20+
f32((bits >> 16u) & 0xffu),
21+
f32((bits >> 8u) & 0xffu),
22+
f32(bits & 0xffu)
23+
) / 255.0;
24+
}
25+
26+
const norm_const: f32 = 1.0 / (sqrt(2.0) * 0.5);
27+
28+
fn unpackRotation(bits: u32) -> vec4f {
29+
let a = (f32((bits >> 20u) & 0x3ffu) / 1023.0 - 0.5) * norm_const;
30+
let b = (f32((bits >> 10u) & 0x3ffu) / 1023.0 - 0.5) * norm_const;
31+
let c = (f32(bits & 0x3ffu) / 1023.0 - 0.5) * norm_const;
32+
let m = sqrt(1.0 - (a * a + b * b + c * c));
33+
34+
let mode = bits >> 30u;
35+
if (mode == 0u) { return vec4f(m, a, b, c); }
36+
if (mode == 1u) { return vec4f(a, m, b, c); }
37+
if (mode == 2u) { return vec4f(a, b, m, c); }
38+
return vec4f(a, b, c, m);
39+
}
40+
41+
fn quatToMat3(R: vec4f) -> mat3x3f {
42+
let qw_scalar = R.x;
43+
let qx_vec = R.y;
44+
let qy_vec = R.z;
45+
let qz_vec = R.w;
46+
47+
return mat3x3f(
48+
vec3f(1.0 - 2.0 * (qy_vec * qy_vec + qz_vec * qz_vec),
49+
2.0 * (qx_vec * qy_vec + qw_scalar * qz_vec),
50+
2.0 * (qx_vec * qz_vec - qw_scalar * qy_vec)),
51+
52+
vec3f(2.0 * (qx_vec * qy_vec - qw_scalar * qz_vec),
53+
1.0 - 2.0 * (qx_vec * qx_vec + qz_vec * qz_vec),
54+
2.0 * (qy_vec * qz_vec + qw_scalar * qx_vec)),
55+
56+
vec3f(2.0 * (qx_vec * qz_vec + qw_scalar * qy_vec),
57+
2.0 * (qy_vec * qz_vec - qw_scalar * qx_vec),
58+
1.0 - 2.0 * (qx_vec * qx_vec + qy_vec * qy_vec))
59+
);
60+
}
61+
62+
// read center
63+
fn readCenter(source: ptr<function, SplatSource>) -> vec3f {
64+
let tex_size_u = textureDimensions(chunkTexture, 0);
65+
let w: u32 = tex_size_u.x / 5u;
66+
let chunkId: u32 = source.id / 256u;
67+
let chunkUV: vec2<i32> = vec2<i32>(i32((chunkId % w) * 5u), i32(chunkId / w));
68+
69+
// read chunk and packed compressed data
70+
chunkDataA = textureLoad(chunkTexture, chunkUV + vec2<i32>(0, 0), 0);
71+
chunkDataB = textureLoad(chunkTexture, chunkUV + vec2<i32>(1, 0), 0);
72+
chunkDataC = textureLoad(chunkTexture, chunkUV + vec2<i32>(2, 0), 0);
73+
chunkDataD = textureLoad(chunkTexture, chunkUV + vec2<i32>(3, 0), 0);
74+
chunkDataE = textureLoad(chunkTexture, chunkUV + vec2<i32>(4, 0), 0);
75+
packedData = textureLoad(packedTexture, source.uv, 0);
76+
77+
return mix(chunkDataA.xyz, vec3f(chunkDataA.w, chunkDataB.xy), unpack111011(packedData.x));
78+
}
79+
80+
fn readColor(source: ptr<function, SplatSource>) -> vec4f {
81+
let r = unpack8888(packedData.w);
82+
return vec4f(mix(chunkDataD.xyz, vec3f(chunkDataD.w, chunkDataE.xy), r.rgb), r.w);
83+
}
84+
85+
fn getRotation() -> vec4f {
86+
return unpackRotation(packedData.y);
87+
}
88+
89+
fn getScale() -> vec3f {
90+
return exp(mix(vec3f(chunkDataB.zw, chunkDataC.x), chunkDataC.yzw, unpack111011(packedData.z)));
91+
}
92+
93+
// given a rotation matrix and scale vector, compute 3d covariance A and B
94+
fn readCovariance(source: ptr<function, SplatSource>, covA_ptr: ptr<function, vec3f>, covB_ptr: ptr<function, vec3f>) {
95+
let rot = quatToMat3(getRotation());
96+
let scale = getScale();
97+
98+
// M = S * R
99+
let M = transpose(mat3x3f(
100+
scale.x * rot[0],
101+
scale.y * rot[1],
102+
scale.z * rot[2]
103+
));
104+
105+
*covA_ptr = vec3f(dot(M[0], M[0]), dot(M[0], M[1]), dot(M[0], M[2]));
106+
*covB_ptr = vec3f(dot(M[1], M[1]), dot(M[1], M[2]), dot(M[2], M[2]));
107+
}
108+
`;

0 commit comments

Comments
 (0)