@myned-ai/gsplat-flame-avatar-renderer 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +160 -32
  2. package/dist/gsplat-flame-avatar-renderer.cjs.js +3510 -749
  3. package/dist/gsplat-flame-avatar-renderer.cjs.min.js +2 -0
  4. package/dist/gsplat-flame-avatar-renderer.cjs.min.js.map +1 -0
  5. package/dist/gsplat-flame-avatar-renderer.esm.js +3471 -751
  6. package/dist/gsplat-flame-avatar-renderer.esm.min.js +2 -0
  7. package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +1 -0
  8. package/package.json +14 -7
  9. package/dist/gsplat-flame-avatar-renderer.cjs.js.map +0 -1
  10. package/dist/gsplat-flame-avatar-renderer.esm.js.map +0 -1
  11. package/src/api/index.js +0 -7
  12. package/src/buffers/SplatBuffer.js +0 -1394
  13. package/src/buffers/SplatBufferGenerator.js +0 -41
  14. package/src/buffers/SplatPartitioner.js +0 -110
  15. package/src/buffers/UncompressedSplatArray.js +0 -106
  16. package/src/buffers/index.js +0 -11
  17. package/src/core/SplatGeometry.js +0 -48
  18. package/src/core/SplatMesh.js +0 -2620
  19. package/src/core/SplatScene.js +0 -43
  20. package/src/core/SplatTree.js +0 -200
  21. package/src/core/Viewer.js +0 -2895
  22. package/src/core/index.js +0 -13
  23. package/src/enums/EngineConstants.js +0 -58
  24. package/src/enums/LogLevel.js +0 -13
  25. package/src/enums/RenderMode.js +0 -11
  26. package/src/enums/SceneFormat.js +0 -21
  27. package/src/enums/SceneRevealMode.js +0 -11
  28. package/src/enums/SplatRenderMode.js +0 -10
  29. package/src/enums/index.js +0 -13
  30. package/src/flame/FlameAnimator.js +0 -271
  31. package/src/flame/FlameConstants.js +0 -21
  32. package/src/flame/FlameTextureManager.js +0 -293
  33. package/src/flame/index.js +0 -22
  34. package/src/flame/utils.js +0 -50
  35. package/src/index.js +0 -39
  36. package/src/loaders/DirectLoadError.js +0 -14
  37. package/src/loaders/INRIAV1PlyParser.js +0 -223
  38. package/src/loaders/PlyLoader.js +0 -261
  39. package/src/loaders/PlyParser.js +0 -19
  40. package/src/loaders/PlyParserUtils.js +0 -311
  41. package/src/loaders/index.js +0 -13
  42. package/src/materials/SplatMaterial.js +0 -1065
  43. package/src/materials/SplatMaterial2D.js +0 -358
  44. package/src/materials/SplatMaterial3D.js +0 -278
  45. package/src/materials/index.js +0 -11
  46. package/src/raycaster/Hit.js +0 -37
  47. package/src/raycaster/Ray.js +0 -123
  48. package/src/raycaster/Raycaster.js +0 -175
  49. package/src/raycaster/index.js +0 -10
  50. package/src/renderer/AnimationManager.js +0 -574
  51. package/src/renderer/AppConstants.js +0 -101
  52. package/src/renderer/GaussianSplatRenderer.js +0 -695
  53. package/src/renderer/index.js +0 -24
  54. package/src/utils/LoaderUtils.js +0 -65
  55. package/src/utils/Util.js +0 -375
  56. package/src/utils/index.js +0 -9
  57. package/src/worker/SortWorker.js +0 -284
  58. package/src/worker/index.js +0 -8
@@ -1,358 +0,0 @@
1
- /**
2
- * SplatMaterial2D
3
- *
4
- * Based on @mkkellogg/gaussian-splats-3d (MIT License)
5
- * https://github.com/mkkellogg/GaussianSplats3D
6
- *
7
- * Modified for FLAME avatar support:
8
- * - Extended vertex shader for FLAME integration
9
- */
10
-
11
- import { DoubleSide, NormalBlending, ShaderMaterial, Vector2 } from 'three';
12
- import { SplatMaterial } from './SplatMaterial.js';
13
-
14
- export class SplatMaterial2D {
15
-
16
- /**
17
- * Build the Three.js material that is used to render the splats.
18
- * @param {number} dynamicMode If true, it means the scene geometry represented by this splat mesh is not stationary or
19
- * that the splat count might change
20
- * @param {boolean} enableOptionalEffects When true, allows for usage of extra properties and attributes in the shader for effects
21
- * such as opacity adjustment. Default is false for performance reasons.
22
- * @param {number} splatScale Value by which all splats are scaled in screen-space (default is 1.0)
23
- * @param {number} pointCloudModeEnabled Render all splats as screen-space circles
24
- * @param {number} maxSphericalHarmonicsDegree Degree of spherical harmonics to utilize in rendering splats
25
- * @return {THREE.ShaderMaterial}
26
- */
27
- static build(dynamicMode = false, enableOptionalEffects = false, splatScale = 1.0,
28
- pointCloudModeEnabled = false, maxSphericalHarmonicsDegree = 0) {
29
-
30
- const customVertexVars = `
31
- uniform vec2 scaleRotationsTextureSize;
32
- uniform highp sampler2D scaleRotationsTexture;
33
- varying mat3 vT;
34
- varying vec2 vQuadCenter;
35
- varying vec2 vFragCoord;
36
- `;
37
-
38
- let vertexShaderSource = SplatMaterial.buildVertexShaderBase(dynamicMode, enableOptionalEffects,
39
- maxSphericalHarmonicsDegree, customVertexVars);
40
- vertexShaderSource += SplatMaterial2D.buildVertexShaderProjection();
41
- const fragmentShaderSource = SplatMaterial2D.buildFragmentShader();
42
-
43
- const uniforms = SplatMaterial.getUniforms(dynamicMode, enableOptionalEffects,
44
- maxSphericalHarmonicsDegree, splatScale, pointCloudModeEnabled);
45
-
46
- uniforms['scaleRotationsTexture'] = {
47
- 'type': 't',
48
- 'value': null
49
- };
50
- uniforms['scaleRotationsTextureSize'] = {
51
- 'type': 'v2',
52
- 'value': new Vector2(1024, 1024)
53
- };
54
-
55
- const material = new ShaderMaterial({
56
- uniforms: uniforms,
57
- vertexShader: vertexShaderSource,
58
- fragmentShader: fragmentShaderSource,
59
- transparent: true,
60
- alphaTest: 1.0,
61
- blending: NormalBlending,
62
- depthTest: true,
63
- depthWrite: false,
64
- side: DoubleSide
65
- });
66
-
67
- return material;
68
- }
69
-
70
- static buildVertexShaderProjection() {
71
-
72
- // Original CUDA code for calculating splat-to-screen transformation, for reference
73
- /*
74
- glm::mat3 R = quat_to_rotmat(rot);
75
- glm::mat3 S = scale_to_mat(scale, mod);
76
- glm::mat3 L = R * S;
77
-
78
- // center of Gaussians in the camera coordinate
79
- glm::mat3x4 splat2world = glm::mat3x4(
80
- glm::vec4(L[0], 0.0),
81
- glm::vec4(L[1], 0.0),
82
- glm::vec4(p_orig.x, p_orig.y, p_orig.z, 1)
83
- );
84
-
85
- glm::mat4 world2ndc = glm::mat4(
86
- projmatrix[0], projmatrix[4], projmatrix[8], projmatrix[12],
87
- projmatrix[1], projmatrix[5], projmatrix[9], projmatrix[13],
88
- projmatrix[2], projmatrix[6], projmatrix[10], projmatrix[14],
89
- projmatrix[3], projmatrix[7], projmatrix[11], projmatrix[15]
90
- );
91
-
92
- glm::mat3x4 ndc2pix = glm::mat3x4(
93
- glm::vec4(float(W) / 2.0, 0.0, 0.0, float(W-1) / 2.0),
94
- glm::vec4(0.0, float(H) / 2.0, 0.0, float(H-1) / 2.0),
95
- glm::vec4(0.0, 0.0, 0.0, 1.0)
96
- );
97
-
98
- T = glm::transpose(splat2world) * world2ndc * ndc2pix;
99
- normal = transformVec4x3({L[2].x, L[2].y, L[2].z}, viewmatrix);
100
- */
101
-
102
- // Compute a 2D-to-2D mapping matrix from a tangent plane into a image plane
103
- // given a 2D gaussian parameters. T = WH (from the paper: https://arxiv.org/pdf/2403.17888)
104
- let vertexShaderSource = `
105
-
106
- vec4 scaleRotationA = texture(scaleRotationsTexture, getDataUVF(nearestEvenIndex, 1.5,
107
- oddOffset, scaleRotationsTextureSize));
108
- vec4 scaleRotationB = texture(scaleRotationsTexture, getDataUVF(nearestEvenIndex, 1.5,
109
- oddOffset + uint(1), scaleRotationsTextureSize));
110
-
111
- vec3 scaleRotation123 = vec3(scaleRotationA.rgb) * (1.0 - fOddOffset) +
112
- vec3(scaleRotationA.ba, scaleRotationB.r) * fOddOffset;
113
- vec3 scaleRotation456 = vec3(scaleRotationA.a, scaleRotationB.rg) * (1.0 - fOddOffset) +
114
- vec3(scaleRotationB.gba) * fOddOffset;
115
-
116
- float missingW = sqrt(1.0 - scaleRotation456.x * scaleRotation456.x - scaleRotation456.y *
117
- scaleRotation456.y - scaleRotation456.z * scaleRotation456.z);
118
- mat3 R = quaternionToRotationMatrix(scaleRotation456.r, scaleRotation456.g, scaleRotation456.b, missingW);
119
- mat3 S = mat3(scaleRotation123.r, 0.0, 0.0,
120
- 0.0, scaleRotation123.g, 0.0,
121
- 0.0, 0.0, scaleRotation123.b);
122
-
123
- mat3 L = R * S;
124
-
125
- mat3x4 splat2World = mat3x4(vec4(L[0], 0.0),
126
- vec4(L[1], 0.0),
127
- vec4(splatCenter.x, splatCenter.y, splatCenter.z, 1.0));
128
-
129
- mat4 world2ndc = transpose(projectionMatrix * transformModelViewMatrix);
130
-
131
- mat3x4 ndc2pix = mat3x4(vec4(viewport.x / 2.0, 0.0, 0.0, (viewport.x - 1.0) / 2.0),
132
- vec4(0.0, viewport.y / 2.0, 0.0, (viewport.y - 1.0) / 2.0),
133
- vec4(0.0, 0.0, 0.0, 1.0));
134
-
135
- mat3 T = transpose(splat2World) * world2ndc * ndc2pix;
136
- vec3 normal = vec3(viewMatrix * vec4(L[0][2], L[1][2], L[2][2], 0.0));
137
- `;
138
-
139
- // Original CUDA code for projection to 2D, for reference
140
- /*
141
- float3 T0 = {T[0][0], T[0][1], T[0][2]};
142
- float3 T1 = {T[1][0], T[1][1], T[1][2]};
143
- float3 T3 = {T[2][0], T[2][1], T[2][2]};
144
-
145
- // Compute AABB
146
- float3 temp_point = {1.0f, 1.0f, -1.0f};
147
- float distance = sumf3(T3 * T3 * temp_point);
148
- float3 f = (1 / distance) * temp_point;
149
- if (distance == 0.0) return false;
150
-
151
- point_image = {
152
- sumf3(f * T0 * T3),
153
- sumf3(f * T1 * T3)
154
- };
155
-
156
- float2 temp = {
157
- sumf3(f * T0 * T0),
158
- sumf3(f * T1 * T1)
159
- };
160
- float2 half_extend = point_image * point_image - temp;
161
- extent = sqrtf2(maxf2(1e-4, half_extend));
162
- return true;
163
- */
164
-
165
- // Computing the bounding box of the 2D Gaussian and its center
166
- // The center of the bounding box is used to create a low pass filter.
167
- // This code is based off the reference implementation and creates an AABB aligned
168
- // with the screen for the quad to be rendered.
169
- const referenceQuadGeneration = `
170
- vec3 T0 = vec3(T[0][0], T[0][1], T[0][2]);
171
- vec3 T1 = vec3(T[1][0], T[1][1], T[1][2]);
172
- vec3 T3 = vec3(T[2][0], T[2][1], T[2][2]);
173
-
174
- vec3 tempPoint = vec3(1.0, 1.0, -1.0);
175
- float distance = (T3.x * T3.x * tempPoint.x) + (T3.y * T3.y * tempPoint.y) + (T3.z * T3.z * tempPoint.z);
176
- vec3 f = (1.0 / distance) * tempPoint;
177
- if (abs(distance) < 0.00001) return;
178
-
179
- float pointImageX = (T0.x * T3.x * f.x) + (T0.y * T3.y * f.y) + (T0.z * T3.z * f.z);
180
- float pointImageY = (T1.x * T3.x * f.x) + (T1.y * T3.y * f.y) + (T1.z * T3.z * f.z);
181
- vec2 pointImage = vec2(pointImageX, pointImageY);
182
-
183
- float tempX = (T0.x * T0.x * f.x) + (T0.y * T0.y * f.y) + (T0.z * T0.z * f.z);
184
- float tempY = (T1.x * T1.x * f.x) + (T1.y * T1.y * f.y) + (T1.z * T1.z * f.z);
185
- vec2 temp = vec2(tempX, tempY);
186
-
187
- vec2 halfExtend = pointImage * pointImage - temp;
188
- vec2 extent = sqrt(max(vec2(0.0001), halfExtend));
189
- float radius = max(extent.x, extent.y);
190
-
191
- vec2 ndcOffset = ((position.xy * radius * 3.0) * basisViewport * 2.0);
192
-
193
- vec4 quadPos = vec4(ndcCenter.xy + ndcOffset, ndcCenter.z, 1.0);
194
- gl_Position = quadPos;
195
-
196
- vT = T;
197
- vQuadCenter = pointImage;
198
- vFragCoord = (quadPos.xy * 0.5 + 0.5) * viewport;
199
- `;
200
-
201
- const useRefImplementation = false;
202
- if (useRefImplementation) {
203
- vertexShaderSource += referenceQuadGeneration;
204
- } else {
205
- // Create a quad that is aligned with the eigen vectors of the projected gaussian for rendering.
206
- // This is a different approach than the reference implementation, similar to how the rendering of
207
- // 3D gaussians in this viewer differs from the reference implementation. If the quad is too small
208
- // (smaller than a pixel), then revert to the reference implementation.
209
- vertexShaderSource += `
210
-
211
- mat4 splat2World4 = mat4(vec4(L[0], 0.0),
212
- vec4(L[1], 0.0),
213
- vec4(L[2], 0.0),
214
- vec4(splatCenter.x, splatCenter.y, splatCenter.z, 1.0));
215
-
216
- mat4 Tt = transpose(transpose(splat2World4) * world2ndc);
217
-
218
- vec4 tempPoint1 = Tt * vec4(1.0, 0.0, 0.0, 1.0);
219
- tempPoint1 /= tempPoint1.w;
220
-
221
- vec4 tempPoint2 = Tt * vec4(0.0, 1.0, 0.0, 1.0);
222
- tempPoint2 /= tempPoint2.w;
223
-
224
- vec4 center = Tt * vec4(0.0, 0.0, 0.0, 1.0);
225
- center /= center.w;
226
-
227
- vec2 basisVector1 = tempPoint1.xy - center.xy;
228
- vec2 basisVector2 = tempPoint2.xy - center.xy;
229
-
230
- vec2 basisVector1Screen = basisVector1 * 0.5 * viewport;
231
- vec2 basisVector2Screen = basisVector2 * 0.5 * viewport;
232
-
233
- const float minPix = 1.;
234
- if (length(basisVector1Screen) < minPix || length(basisVector2Screen) < minPix) {
235
- ${referenceQuadGeneration}
236
- } else {
237
- vec2 ndcOffset = vec2(position.x * basisVector1 + position.y * basisVector2) * 3.0 * inverseFocalAdjustment;
238
- vec4 quadPos = vec4(ndcCenter.xy + ndcOffset, ndcCenter.z, 1.0);
239
- gl_Position = quadPos;
240
-
241
- vT = T;
242
- vQuadCenter = center.xy;
243
- vFragCoord = (quadPos.xy * 0.5 + 0.5) * viewport;
244
- }
245
- `;
246
- }
247
-
248
- vertexShaderSource += SplatMaterial.getVertexShaderFadeIn();
249
- vertexShaderSource += `}`;
250
-
251
- return vertexShaderSource;
252
- }
253
-
254
- static buildFragmentShader() {
255
-
256
- // Original CUDA code for splat intersection, for reference
257
- /*
258
- const float2 xy = collected_xy[j];
259
- const float3 Tu = collected_Tu[j];
260
- const float3 Tv = collected_Tv[j];
261
- const float3 Tw = collected_Tw[j];
262
- float3 k = pix.x * Tw - Tu;
263
- float3 l = pix.y * Tw - Tv;
264
- float3 p = cross(k, l);
265
- if (p.z == 0.0) continue;
266
- float2 s = {p.x / p.z, p.y / p.z};
267
- float rho3d = (s.x * s.x + s.y * s.y);
268
- float2 d = {xy.x - pixf.x, xy.y - pixf.y};
269
- float rho2d = FilterInvSquare * (d.x * d.x + d.y * d.y);
270
-
271
- // compute intersection and depth
272
- float rho = min(rho3d, rho2d);
273
- float depth = (rho3d <= rho2d) ? (s.x * Tw.x + s.y * Tw.y) + Tw.z : Tw.z;
274
- if (depth < near_n) continue;
275
- float4 nor_o = collected_normal_opacity[j];
276
- float normal[3] = {nor_o.x, nor_o.y, nor_o.z};
277
- float opa = nor_o.w;
278
-
279
- float power = -0.5f * rho;
280
- if (power > 0.0f)
281
- continue;
282
-
283
- // Eq. (2) from 3D Gaussian splatting paper.
284
- // Obtain alpha by multiplying with Gaussian opacity
285
- // and its exponential falloff from mean.
286
- // Avoid numerical instabilities (see paper appendix).
287
- float alpha = min(0.99f, opa * exp(power));
288
- if (alpha < 1.0f / 255.0f)
289
- continue;
290
- float test_T = T * (1 - alpha);
291
- if (test_T < 0.0001f)
292
- {
293
- done = true;
294
- continue;
295
- }
296
-
297
- float w = alpha * T;
298
- */
299
- let fragmentShaderSource = `
300
- precision highp float;
301
- #include <common>
302
-
303
- uniform vec3 debugColor;
304
-
305
- varying vec4 vColor;
306
- varying vec2 vUv;
307
- varying vec2 vPosition;
308
- varying mat3 vT;
309
- varying vec2 vQuadCenter;
310
- varying vec2 vFragCoord;
311
-
312
- void main () {
313
-
314
- const float FilterInvSquare = 2.0;
315
- const float near_n = 0.2;
316
- const float T = 1.0;
317
-
318
- vec2 xy = vQuadCenter;
319
- vec3 Tu = vT[0];
320
- vec3 Tv = vT[1];
321
- vec3 Tw = vT[2];
322
- vec3 k = vFragCoord.x * Tw - Tu;
323
- vec3 l = vFragCoord.y * Tw - Tv;
324
- vec3 p = cross(k, l);
325
- if (p.z == 0.0) discard;
326
- vec2 s = vec2(p.x / p.z, p.y / p.z);
327
- float rho3d = (s.x * s.x + s.y * s.y);
328
- vec2 d = vec2(xy.x - vFragCoord.x, xy.y - vFragCoord.y);
329
- float rho2d = FilterInvSquare * (d.x * d.x + d.y * d.y);
330
-
331
- // compute intersection and depth
332
- float rho = min(rho3d, rho2d);
333
- float depth = (rho3d <= rho2d) ? (s.x * Tw.x + s.y * Tw.y) + Tw.z : Tw.z;
334
- if (depth < near_n) discard;
335
- // vec4 nor_o = collected_normal_opacity[j];
336
- // float normal[3] = {nor_o.x, nor_o.y, nor_o.z};
337
- float opa = vColor.a;
338
-
339
- float power = -0.5f * rho;
340
- if (power > 0.0f) discard;
341
-
342
- // Eq. (2) from 3D Gaussian splatting paper.
343
- // Obtain alpha by multiplying with Gaussian opacity
344
- // and its exponential falloff from mean.
345
- // Avoid numerical instabilities (see paper appendix).
346
- float alpha = min(0.99f, opa * exp(power));
347
- if (alpha < 1.0f / 255.0f) discard;
348
- float test_T = T * (1.0 - alpha);
349
- if (test_T < 0.0001)discard;
350
-
351
- float w = alpha * T;
352
- gl_FragColor = vec4(vColor.rgb, w);
353
- }
354
- `;
355
-
356
- return fragmentShaderSource;
357
- }
358
- }
@@ -1,278 +0,0 @@
1
- /**
2
- * SplatMaterial3D
3
- *
4
- * Based on @mkkellogg/gaussian-splats-3d (MIT License)
5
- * https://github.com/mkkellogg/GaussianSplats3D
6
- *
7
- * Modified for FLAME avatar support:
8
- * - Extended vertex shader for FLAME integration
9
- */
10
-
11
- import { DoubleSide, NormalBlending, ShaderMaterial, Vector2 } from 'three';
12
- import { SplatMaterial } from './SplatMaterial.js';
13
-
14
- export class SplatMaterial3D {
15
-
16
- /**
17
- * Build the Three.js material that is used to render the splats.
18
- * @param {number} dynamicMode If true, it means the scene geometry represented by this splat mesh is not stationary or
19
- * that the splat count might change
20
- * @param {boolean} enableOptionalEffects When true, allows for usage of extra properties and attributes in the shader for effects
21
- * such as opacity adjustment. Default is false for performance reasons.
22
- * @param {boolean} antialiased If true, calculate compensation factor to deal with gaussians being rendered at a significantly
23
- * different resolution than that of their training
24
- * @param {number} maxScreenSpaceSplatSize The maximum clip space splat size
25
- * @param {number} splatScale Value by which all splats are scaled in screen-space (default is 1.0)
26
- * @param {number} pointCloudModeEnabled Render all splats as screen-space circles
27
- * @param {number} maxSphericalHarmonicsDegree Degree of spherical harmonics to utilize in rendering splats
28
- * @return {THREE.ShaderMaterial}
29
- */
30
- static build(dynamicMode = false, enableOptionalEffects = false, antialiased = false, maxScreenSpaceSplatSize = 2048,
31
- splatScale = 1.0, pointCloudModeEnabled = false, maxSphericalHarmonicsDegree = 0, kernel2DSize = 0.3, useFlame = true) {
32
-
33
- const customVertexVars = `
34
- uniform vec2 covariancesTextureSize;
35
- uniform highp sampler2D covariancesTexture;
36
- uniform highp usampler2D covariancesTextureHalfFloat;
37
- uniform int covariancesAreHalfFloat;
38
-
39
- void fromCovarianceHalfFloatV4(uvec4 val, out vec4 first, out vec4 second) {
40
- vec2 r = unpackHalf2x16(val.r);
41
- vec2 g = unpackHalf2x16(val.g);
42
- vec2 b = unpackHalf2x16(val.b);
43
-
44
- first = vec4(r.x, r.y, g.x, g.y);
45
- second = vec4(b.x, b.y, 0.0, 0.0);
46
- }
47
- `;
48
-
49
- let vertexShaderSource = SplatMaterial.buildVertexShaderBase(dynamicMode, enableOptionalEffects,
50
- maxSphericalHarmonicsDegree, customVertexVars, useFlame);
51
- vertexShaderSource += SplatMaterial3D.buildVertexShaderProjection(antialiased, enableOptionalEffects,
52
- maxScreenSpaceSplatSize, kernel2DSize);
53
- const fragmentShaderSource = SplatMaterial3D.buildFragmentShader();
54
-
55
- const uniforms = SplatMaterial.getUniforms(dynamicMode, enableOptionalEffects,
56
- maxSphericalHarmonicsDegree, splatScale, pointCloudModeEnabled);
57
-
58
- uniforms['covariancesTextureSize'] = {
59
- 'type': 'v2',
60
- 'value': new Vector2(1024, 1024)
61
- };
62
- uniforms['covariancesTexture'] = {
63
- 'type': 't',
64
- 'value': null
65
- };
66
- uniforms['covariancesTextureHalfFloat'] = {
67
- 'type': 't',
68
- 'value': null
69
- };
70
- uniforms['covariancesAreHalfFloat'] = {
71
- 'type': 'i',
72
- 'value': 0
73
- };
74
-
75
- const material = new ShaderMaterial({
76
- uniforms: uniforms,
77
- vertexShader: vertexShaderSource,
78
- fragmentShader: fragmentShaderSource,
79
- transparent: true,
80
- alphaTest: 1.0,
81
- blending: NormalBlending,
82
- depthTest: true,
83
- depthWrite: false,
84
- side: DoubleSide
85
- });
86
-
87
- return material;
88
- }
89
-
90
- static buildVertexShaderProjection(antialiased, enableOptionalEffects, maxScreenSpaceSplatSize, kernel2DSize) {
91
- let vertexShaderSource = `
92
-
93
- vec4 sampledCovarianceA;
94
- vec4 sampledCovarianceB;
95
- vec3 cov3D_M11_M12_M13;
96
- vec3 cov3D_M22_M23_M33;
97
- if (covariancesAreHalfFloat == 0) {
98
- sampledCovarianceA = texture(covariancesTexture, getDataUVF(nearestEvenIndex, 1.5, oddOffset,
99
- covariancesTextureSize));
100
- sampledCovarianceB = texture(covariancesTexture, getDataUVF(nearestEvenIndex, 1.5, oddOffset + uint(1),
101
- covariancesTextureSize));
102
-
103
- cov3D_M11_M12_M13 = vec3(sampledCovarianceA.rgb) * (1.0 - fOddOffset) +
104
- vec3(sampledCovarianceA.ba, sampledCovarianceB.r) * fOddOffset;
105
- cov3D_M22_M23_M33 = vec3(sampledCovarianceA.a, sampledCovarianceB.rg) * (1.0 - fOddOffset) +
106
- vec3(sampledCovarianceB.gba) * fOddOffset;
107
- } else {
108
- uvec4 sampledCovarianceU = texture(covariancesTextureHalfFloat, getDataUV(1, 0, covariancesTextureSize));
109
- fromCovarianceHalfFloatV4(sampledCovarianceU, sampledCovarianceA, sampledCovarianceB);
110
- cov3D_M11_M12_M13 = sampledCovarianceA.rgb;
111
- cov3D_M22_M23_M33 = vec3(sampledCovarianceA.a, sampledCovarianceB.rg);
112
- }
113
-
114
- // Construct the 3D covariance matrix
115
- mat3 Vrk = mat3(
116
- cov3D_M11_M12_M13.x, cov3D_M11_M12_M13.y, cov3D_M11_M12_M13.z,
117
- cov3D_M11_M12_M13.y, cov3D_M22_M23_M33.x, cov3D_M22_M23_M33.y,
118
- cov3D_M11_M12_M13.z, cov3D_M22_M23_M33.y, cov3D_M22_M23_M33.z
119
- );
120
-
121
- mat3 J;
122
- if (orthographicMode == 1) {
123
- // Since the projection is linear, we don't need an approximation
124
- J = transpose(mat3(orthoZoom, 0.0, 0.0,
125
- 0.0, orthoZoom, 0.0,
126
- 0.0, 0.0, 0.0));
127
- } else {
128
- // Construct the Jacobian of the affine approximation of the projection matrix. It will be used to transform the
129
- // 3D covariance matrix instead of using the actual projection matrix because that transformation would
130
- // require a non-linear component (perspective division) which would yield a non-gaussian result.
131
- float s = 1.0 / (viewCenter.z * viewCenter.z);
132
- J = mat3(
133
- focal.x / viewCenter.z, 0., -(focal.x * viewCenter.x) * s,
134
- 0., focal.y / viewCenter.z, -(focal.y * viewCenter.y) * s,
135
- 0., 0., 0.
136
- );
137
- }
138
-
139
- // Concatenate the projection approximation with the model-view transformation
140
- mat3 W = transpose(mat3(transformModelViewMatrix));
141
- mat3 T = W * J;
142
-
143
- // Transform the 3D covariance matrix (Vrk) to compute the 2D covariance matrix
144
- mat3 cov2Dm = transpose(T) * Vrk * T;
145
- `;
146
-
147
- if (antialiased) {
148
- vertexShaderSource += `
149
- float detOrig = cov2Dm[0][0] * cov2Dm[1][1] - cov2Dm[0][1] * cov2Dm[0][1];
150
- cov2Dm[0][0] += ${kernel2DSize};
151
- cov2Dm[1][1] += ${kernel2DSize};
152
- float detBlur = cov2Dm[0][0] * cov2Dm[1][1] - cov2Dm[0][1] * cov2Dm[0][1];
153
- vColor.a *= sqrt(max(detOrig / detBlur, 0.0));
154
- if (vColor.a < minAlpha) return;
155
- `;
156
- } else {
157
- vertexShaderSource += `
158
- cov2Dm[0][0] += ${kernel2DSize};
159
- cov2Dm[1][1] += ${kernel2DSize};
160
- `;
161
- }
162
-
163
- vertexShaderSource += `
164
-
165
- // We are interested in the upper-left 2x2 portion of the projected 3D covariance matrix because
166
- // we only care about the X and Y values. We want the X-diagonal, cov2Dm[0][0],
167
- // the Y-diagonal, cov2Dm[1][1], and the correlation between the two cov2Dm[0][1]. We don't
168
- // need cov2Dm[1][0] because it is a symetric matrix.
169
- vec3 cov2Dv = vec3(cov2Dm[0][0], cov2Dm[0][1], cov2Dm[1][1]);
170
-
171
- // We now need to solve for the eigen-values and eigen vectors of the 2D covariance matrix
172
- // so that we can determine the 2D basis for the splat. This is done using the method described
173
- // here: https://people.math.harvard.edu/~knill/teaching/math21b2004/exhibits/2dmatrices/index.html
174
- // After calculating the eigen-values and eigen-vectors, we calculate the basis for rendering the splat
175
- // by normalizing the eigen-vectors and then multiplying them by (sqrt(8) * sqrt(eigen-value)), which is
176
- // equal to scaling them by sqrt(8) standard deviations.
177
- //
178
- // This is a different approach than in the original work at INRIA. In that work they compute the
179
- // max extents of the projected splat in screen space to form a screen-space aligned bounding rectangle
180
- // which forms the geometry that is actually rasterized. The dimensions of that bounding box are 3.0
181
- // times the square root of the maximum eigen-value, or 3 standard deviations. They then use the inverse
182
- // 2D covariance matrix (called 'conic') in the CUDA rendering thread to determine fragment opacity by
183
- // calculating the full gaussian: exp(-0.5 * (X - mean) * conic * (X - mean)) * splat opacity
184
- float a = cov2Dv.x;
185
- float d = cov2Dv.z;
186
- float b = cov2Dv.y;
187
- float D = a * d - b * b;
188
- float trace = a + d;
189
- float traceOver2 = 0.5 * trace;
190
- float term2 = sqrt(max(0.1f, traceOver2 * traceOver2 - D));
191
- float eigenValue1 = traceOver2 + term2;
192
- float eigenValue2 = traceOver2 - term2;
193
-
194
- if (pointCloudModeEnabled == 1) {
195
- eigenValue1 = eigenValue2 = 0.2;
196
- }
197
-
198
- if (eigenValue2 <= 0.0) return;
199
-
200
- vec2 eigenVector1 = normalize(vec2(b, eigenValue1 - a));
201
- // since the eigen vectors are orthogonal, we derive the second one from the first
202
- vec2 eigenVector2 = vec2(eigenVector1.y, -eigenVector1.x);
203
-
204
- // We use sqrt(8) standard deviations instead of 3 to eliminate more of the splat with a very low opacity.
205
- vec2 basisVector1 = eigenVector1 * splatScale * min(sqrt8 * sqrt(eigenValue1), ${parseInt(maxScreenSpaceSplatSize)}.0);
206
- vec2 basisVector2 = eigenVector2 * splatScale * min(sqrt8 * sqrt(eigenValue2), ${parseInt(maxScreenSpaceSplatSize)}.0);
207
- `;
208
-
209
- if (enableOptionalEffects) {
210
- vertexShaderSource += `
211
- vColor.a *= splatOpacityFromScene;
212
- `;
213
- }
214
-
215
- vertexShaderSource += `
216
- vec2 ndcOffset = vec2(vPosition.x * basisVector1 + vPosition.y * basisVector2) *
217
- basisViewport * 2.0 * inverseFocalAdjustment;
218
-
219
- vec4 quadPos = vec4(ndcCenter.xy + ndcOffset, ndcCenter.z, 1.0);
220
- gl_Position = quadPos;
221
-
222
- // Scale the position data we send to the fragment shader
223
- vPosition *= sqrt8;
224
- `;
225
-
226
- vertexShaderSource += SplatMaterial.getVertexShaderFadeIn();
227
- vertexShaderSource += `}`;
228
-
229
- return vertexShaderSource;
230
- }
231
-
232
- static buildFragmentShader() {
233
- let fragmentShaderSource = `
234
- precision highp float;
235
- #include <common>
236
-
237
- uniform vec3 debugColor;
238
-
239
- varying vec4 vColor;
240
- varying vec2 vUv;
241
- varying vec2 vPosition;
242
- varying vec2 vSplatIndex;
243
-
244
- `;
245
-
246
- fragmentShaderSource += `
247
- void main () {
248
- // Compute the positional squared distance from the center of the splat to the current fragment.
249
- float A = dot(vPosition, vPosition);
250
- // Since the positional data in vPosition has been scaled by sqrt(8), the squared result will be
251
- // scaled by a factor of 8. If the squared result is larger than 8, it means it is outside the ellipse
252
- // defined by the rectangle formed by vPosition. It also means it's farther
253
- // away than sqrt(8) standard deviations from the mean.
254
-
255
- // if(vSplatIndex.x > 20000.0) discard;
256
- // if (A > 8.0) discard;
257
- vec3 color = vColor.rgb;
258
-
259
- // Since the rendered splat is scaled by sqrt(8), the inverse covariance matrix that is part of
260
- // the gaussian formula becomes the identity matrix. We're then left with (X - mean) * (X - mean),
261
- // and since 'mean' is zero, we have X * X, which is the same as A:
262
- float opacity = exp( -0.5*A) * vColor.a;
263
- if(opacity < 1.0 / 255.0)
264
- discard;
265
-
266
- // uint a = uint(255);
267
- // vec3 c = vec3(vSplatIndex.x / 256.0 / 256.0, float(uint(vSplatIndex.x / 256.0 )% a) / 256.0, float(uint(vSplatIndex.x)% a) / 256.0);
268
- // gl_FragColor = vec4(c, 1.0);
269
- gl_FragColor = vec4(color, opacity);
270
-
271
-
272
- }
273
- `;
274
-
275
- return fragmentShaderSource;
276
- }
277
-
278
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * gsplat-flame-avatar - Materials Module
3
- * WebGL shaders and materials for Gaussian Splat rendering.
4
- *
5
- * Based on @mkkellogg/gaussian-splats-3d (MIT License)
6
- * Extended with FLAME skinning shaders.
7
- */
8
-
9
- export { SplatMaterial } from './SplatMaterial.js';
10
- export { SplatMaterial2D } from './SplatMaterial2D.js';
11
- export { SplatMaterial3D } from './SplatMaterial3D.js';
@@ -1,37 +0,0 @@
1
- /**
2
- * Hit
3
- *
4
- * Derived from @mkkellogg/gaussian-splats-3d (MIT License)
5
- * https://github.com/mkkellogg/GaussianSplats3D
6
- *
7
- * This file is functionally identical to the original.
8
- */
9
-
10
- import * as THREE from 'three';
11
-
12
- export class Hit {
13
-
14
- constructor() {
15
- this.origin = new THREE.Vector3();
16
- this.normal = new THREE.Vector3();
17
- this.distance = 0;
18
- this.splatIndex = 0;
19
- }
20
-
21
- set(origin, normal, distance, splatIndex) {
22
- this.origin.copy(origin);
23
- this.normal.copy(normal);
24
- this.distance = distance;
25
- this.splatIndex = splatIndex;
26
- }
27
-
28
- clone() {
29
- const hitClone = new Hit();
30
- hitClone.origin.copy(this.origin);
31
- hitClone.normal.copy(this.normal);
32
- hitClone.distance = this.distance;
33
- hitClone.splatIndex = this.splatIndex;
34
- return hitClone;
35
- }
36
-
37
- }