@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.
- package/README.md +160 -32
- package/dist/gsplat-flame-avatar-renderer.cjs.js +3510 -749
- package/dist/gsplat-flame-avatar-renderer.cjs.min.js +2 -0
- package/dist/gsplat-flame-avatar-renderer.cjs.min.js.map +1 -0
- package/dist/gsplat-flame-avatar-renderer.esm.js +3471 -751
- package/dist/gsplat-flame-avatar-renderer.esm.min.js +2 -0
- package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +1 -0
- package/package.json +14 -7
- package/dist/gsplat-flame-avatar-renderer.cjs.js.map +0 -1
- package/dist/gsplat-flame-avatar-renderer.esm.js.map +0 -1
- package/src/api/index.js +0 -7
- package/src/buffers/SplatBuffer.js +0 -1394
- package/src/buffers/SplatBufferGenerator.js +0 -41
- package/src/buffers/SplatPartitioner.js +0 -110
- package/src/buffers/UncompressedSplatArray.js +0 -106
- package/src/buffers/index.js +0 -11
- package/src/core/SplatGeometry.js +0 -48
- package/src/core/SplatMesh.js +0 -2620
- package/src/core/SplatScene.js +0 -43
- package/src/core/SplatTree.js +0 -200
- package/src/core/Viewer.js +0 -2895
- package/src/core/index.js +0 -13
- package/src/enums/EngineConstants.js +0 -58
- package/src/enums/LogLevel.js +0 -13
- package/src/enums/RenderMode.js +0 -11
- package/src/enums/SceneFormat.js +0 -21
- package/src/enums/SceneRevealMode.js +0 -11
- package/src/enums/SplatRenderMode.js +0 -10
- package/src/enums/index.js +0 -13
- package/src/flame/FlameAnimator.js +0 -271
- package/src/flame/FlameConstants.js +0 -21
- package/src/flame/FlameTextureManager.js +0 -293
- package/src/flame/index.js +0 -22
- package/src/flame/utils.js +0 -50
- package/src/index.js +0 -39
- package/src/loaders/DirectLoadError.js +0 -14
- package/src/loaders/INRIAV1PlyParser.js +0 -223
- package/src/loaders/PlyLoader.js +0 -261
- package/src/loaders/PlyParser.js +0 -19
- package/src/loaders/PlyParserUtils.js +0 -311
- package/src/loaders/index.js +0 -13
- package/src/materials/SplatMaterial.js +0 -1065
- package/src/materials/SplatMaterial2D.js +0 -358
- package/src/materials/SplatMaterial3D.js +0 -278
- package/src/materials/index.js +0 -11
- package/src/raycaster/Hit.js +0 -37
- package/src/raycaster/Ray.js +0 -123
- package/src/raycaster/Raycaster.js +0 -175
- package/src/raycaster/index.js +0 -10
- package/src/renderer/AnimationManager.js +0 -574
- package/src/renderer/AppConstants.js +0 -101
- package/src/renderer/GaussianSplatRenderer.js +0 -695
- package/src/renderer/index.js +0 -24
- package/src/utils/LoaderUtils.js +0 -65
- package/src/utils/Util.js +0 -375
- package/src/utils/index.js +0 -9
- package/src/worker/SortWorker.js +0 -284
- package/src/worker/index.js +0 -8
|
@@ -1,1065 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SplatMaterial
|
|
3
|
-
*
|
|
4
|
-
* Based on @mkkellogg/gaussian-splats-3d (MIT License)
|
|
5
|
-
* https://github.com/mkkellogg/GaussianSplats3D
|
|
6
|
-
*
|
|
7
|
-
* HEAVILY MODIFIED for FLAME avatar support:
|
|
8
|
-
* - Added FLAME bone/pose uniforms and textures
|
|
9
|
-
* - Added expression blendshape support
|
|
10
|
-
* - Extended vertex shader with GPU skinning
|
|
11
|
-
* - Additional ~500 lines of FLAME-specific shader code
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { Color, DoubleSide, Matrix4, NormalBlending, ShaderMaterial, Vector2, Vector3 } from 'three';
|
|
15
|
-
import { Constants } from '../enums/EngineConstants.js';
|
|
16
|
-
|
|
17
|
-
export class SplatMaterial {
|
|
18
|
-
|
|
19
|
-
static buildVertexShaderBase(dynamicMode = false, enableOptionalEffects = false, maxSphericalHarmonicsDegree = 0, customVars = '', useFlame = true) {
|
|
20
|
-
let vertexShaderSource = ``;
|
|
21
|
-
if (useFlame == true) {
|
|
22
|
-
vertexShaderSource += `#define USE_FLAME`;
|
|
23
|
-
} else {
|
|
24
|
-
vertexShaderSource += `#define USE_SKINNING`;
|
|
25
|
-
}
|
|
26
|
-
vertexShaderSource += `
|
|
27
|
-
precision highp float;
|
|
28
|
-
#include <common>
|
|
29
|
-
|
|
30
|
-
attribute uint splatIndex;
|
|
31
|
-
uniform highp usampler2D flameModelTexture;
|
|
32
|
-
uniform highp usampler2D boneTexture;
|
|
33
|
-
uniform highp usampler2D boneWeightTexture;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
uniform highp usampler2D centersColorsTexture;
|
|
37
|
-
uniform highp sampler2D sphericalHarmonicsTexture;
|
|
38
|
-
uniform highp sampler2D sphericalHarmonicsTextureR;
|
|
39
|
-
uniform highp sampler2D sphericalHarmonicsTextureG;
|
|
40
|
-
uniform highp sampler2D sphericalHarmonicsTextureB;
|
|
41
|
-
|
|
42
|
-
uniform highp usampler2D sceneIndexesTexture;
|
|
43
|
-
uniform vec2 sceneIndexesTextureSize;
|
|
44
|
-
uniform int sceneCount;
|
|
45
|
-
uniform int gaussianSplatCount;
|
|
46
|
-
uniform int bsCount;
|
|
47
|
-
uniform float headBoneIndex;
|
|
48
|
-
#ifdef USE_SKINNING
|
|
49
|
-
attribute vec4 skinIndex;
|
|
50
|
-
attribute vec4 skinWeight;
|
|
51
|
-
#endif
|
|
52
|
-
`;
|
|
53
|
-
|
|
54
|
-
if (enableOptionalEffects) {
|
|
55
|
-
vertexShaderSource += `
|
|
56
|
-
uniform float sceneOpacity[${Constants.MaxScenes}];
|
|
57
|
-
uniform int sceneVisibility[${Constants.MaxScenes}];
|
|
58
|
-
`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (dynamicMode) {
|
|
62
|
-
vertexShaderSource += `
|
|
63
|
-
uniform highp mat4 transforms[${Constants.MaxScenes}];
|
|
64
|
-
`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
vertexShaderSource += `
|
|
68
|
-
${customVars}
|
|
69
|
-
uniform vec2 focal;
|
|
70
|
-
uniform float orthoZoom;
|
|
71
|
-
uniform int orthographicMode;
|
|
72
|
-
uniform int pointCloudModeEnabled;
|
|
73
|
-
uniform float inverseFocalAdjustment;
|
|
74
|
-
uniform vec2 viewport;
|
|
75
|
-
uniform vec2 basisViewport;
|
|
76
|
-
uniform vec2 centersColorsTextureSize;
|
|
77
|
-
uniform vec2 flameModelTextureSize;
|
|
78
|
-
uniform vec2 boneWeightTextureSize;
|
|
79
|
-
uniform vec2 boneTextureSize;
|
|
80
|
-
|
|
81
|
-
uniform int sphericalHarmonicsDegree;
|
|
82
|
-
uniform vec2 sphericalHarmonicsTextureSize;
|
|
83
|
-
uniform int sphericalHarmonics8BitMode;
|
|
84
|
-
uniform int sphericalHarmonicsMultiTextureMode;
|
|
85
|
-
uniform float visibleRegionRadius;
|
|
86
|
-
uniform float visibleRegionFadeStartRadius;
|
|
87
|
-
uniform float firstRenderTime;
|
|
88
|
-
uniform float currentTime;
|
|
89
|
-
uniform int fadeInComplete;
|
|
90
|
-
uniform vec3 sceneCenter;
|
|
91
|
-
uniform float splatScale;
|
|
92
|
-
uniform float sphericalHarmonics8BitCompressionRangeMin[${Constants.MaxScenes}];
|
|
93
|
-
uniform float sphericalHarmonics8BitCompressionRangeMax[${Constants.MaxScenes}];
|
|
94
|
-
|
|
95
|
-
varying vec4 vColor;
|
|
96
|
-
varying vec2 vUv;
|
|
97
|
-
varying vec2 vPosition;
|
|
98
|
-
varying vec2 vSplatIndex;
|
|
99
|
-
#ifdef USE_SKINNING
|
|
100
|
-
uniform mat4 bindMatrix;
|
|
101
|
-
uniform mat4 bindMatrixInverse;
|
|
102
|
-
uniform highp sampler2D boneTexture0;
|
|
103
|
-
mat4 getBoneMatrix0( const in float i ) {
|
|
104
|
-
int size = textureSize( boneTexture0, 0 ).x;
|
|
105
|
-
int j = int( i ) * 4;
|
|
106
|
-
int x = j % size;
|
|
107
|
-
int y = j / size;
|
|
108
|
-
vec4 v1 = texelFetch( boneTexture0, ivec2( x, y ), 0 );
|
|
109
|
-
vec4 v2 = texelFetch( boneTexture0, ivec2( x + 1, y ), 0 );
|
|
110
|
-
vec4 v3 = texelFetch( boneTexture0, ivec2( x + 2, y ), 0 );
|
|
111
|
-
vec4 v4 = texelFetch( boneTexture0, ivec2( x + 3, y ), 0 );
|
|
112
|
-
return mat4( v1, v2, v3, v4 );
|
|
113
|
-
}
|
|
114
|
-
#endif
|
|
115
|
-
|
|
116
|
-
mat3 quaternionToRotationMatrix(float x, float y, float z, float w) {
|
|
117
|
-
float s = 1.0 / sqrt(w * w + x * x + y * y + z * z);
|
|
118
|
-
|
|
119
|
-
return mat3(
|
|
120
|
-
1. - 2. * (y * y + z * z),
|
|
121
|
-
2. * (x * y + w * z),
|
|
122
|
-
2. * (x * z - w * y),
|
|
123
|
-
2. * (x * y - w * z),
|
|
124
|
-
1. - 2. * (x * x + z * z),
|
|
125
|
-
2. * (y * z + w * x),
|
|
126
|
-
2. * (x * z + w * y),
|
|
127
|
-
2. * (y * z - w * x),
|
|
128
|
-
1. - 2. * (x * x + y * y)
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const float sqrt8 = sqrt(8.0);
|
|
133
|
-
const float minAlpha = 1.0 / 255.0;
|
|
134
|
-
|
|
135
|
-
const vec4 encodeNorm4 = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
|
|
136
|
-
const uvec4 mask4 = uvec4(uint(0x000000FF), uint(0x0000FF00), uint(0x00FF0000), uint(0xFF000000));
|
|
137
|
-
const uvec4 shift4 = uvec4(0, 8, 16, 24);
|
|
138
|
-
int internal = 1;//show a gaussian splatting point every internal points.
|
|
139
|
-
vec4 uintToRGBAVec (uint u) {
|
|
140
|
-
uvec4 urgba = mask4 & u;
|
|
141
|
-
urgba = urgba >> shift4;
|
|
142
|
-
vec4 rgba = vec4(urgba) * encodeNorm4;
|
|
143
|
-
return rgba;
|
|
144
|
-
}
|
|
145
|
-
float getRealIndex(int sIndex, int reducedFactor) {
|
|
146
|
-
int remainder = sIndex % reducedFactor;
|
|
147
|
-
|
|
148
|
-
if(remainder == int(0)) {
|
|
149
|
-
return float(sIndex);
|
|
150
|
-
}
|
|
151
|
-
else
|
|
152
|
-
{
|
|
153
|
-
return float(sIndex - remainder);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
vec2 getDataUV(in int stride, in int offset, in vec2 dimensions) {
|
|
158
|
-
vec2 samplerUV = vec2(0.0, 0.0);
|
|
159
|
-
float d = float(uint(getRealIndex(int(splatIndex), internal)) * uint(stride) + uint(offset)) / dimensions.x;
|
|
160
|
-
samplerUV.y = float(floor(d)) / dimensions.y;
|
|
161
|
-
samplerUV.x = fract(d);
|
|
162
|
-
return samplerUV;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
vec2 getFlameDataUV(in int stride, in int offset, in vec2 dimensions) {
|
|
166
|
-
vec2 samplerUV = vec2(0.0, 0.0);
|
|
167
|
-
float d = float(uint(int(splatIndex) / internal) * uint(stride) + uint(offset) + uint(gaussianSplatCount * bsCount)) / dimensions.x;
|
|
168
|
-
samplerUV.y = float(floor(d)) / dimensions.y;
|
|
169
|
-
samplerUV.x = fract(d);
|
|
170
|
-
return samplerUV;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
vec2 getBoneWeightUV(in int stride, in int offset, in vec2 dimensions) {
|
|
174
|
-
vec2 samplerUV = vec2(0.0, 0.0);
|
|
175
|
-
float d = float(uint(int(splatIndex) / internal) * uint(stride) + uint(offset)) / dimensions.x;
|
|
176
|
-
samplerUV.y = float(floor(d)) / dimensions.y;
|
|
177
|
-
samplerUV.x = fract(d);
|
|
178
|
-
return samplerUV;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
vec2 getBSFlameDataUV(in int bsInedex, in int stride, in int offset, in vec2 dimensions) {
|
|
182
|
-
vec2 samplerUV = vec2(0.0, 0.0);
|
|
183
|
-
float d = float(uint(int(splatIndex) / internal) * uint(stride) + uint(offset) + uint(gaussianSplatCount * bsInedex)) / dimensions.x;
|
|
184
|
-
samplerUV.y = float(floor(d)) / dimensions.y;
|
|
185
|
-
samplerUV.x = fract(d);
|
|
186
|
-
return samplerUV;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
vec2 getDataUVF(in uint sIndex, in float stride, in uint offset, in vec2 dimensions) {
|
|
190
|
-
vec2 samplerUV = vec2(0.0, 0.0);
|
|
191
|
-
float d = float(uint(float(getRealIndex(int(sIndex), internal)) * stride) + offset) / dimensions.x;
|
|
192
|
-
samplerUV.y = float(floor(d)) / dimensions.y;
|
|
193
|
-
samplerUV.x = fract(d);
|
|
194
|
-
return samplerUV;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const float SH_C1 = 0.4886025119029199f;
|
|
198
|
-
const float[5] SH_C2 = float[](1.0925484, -1.0925484, 0.3153916, -1.0925484, 0.5462742);
|
|
199
|
-
|
|
200
|
-
mat4 getBoneMatrix( float i ) {
|
|
201
|
-
float y = i;
|
|
202
|
-
float x = 0.0;
|
|
203
|
-
|
|
204
|
-
vec2 samplerUV = vec2(0.0, 0.0);
|
|
205
|
-
float d = float(i * 4.0) / boneTextureSize.x;//4
|
|
206
|
-
samplerUV.y = float(floor(d)) / boneTextureSize.y;//5
|
|
207
|
-
samplerUV.x = fract(d);
|
|
208
|
-
|
|
209
|
-
vec4 v1 = uintBitsToFloat(texture( boneTexture, samplerUV ));
|
|
210
|
-
vec4 v2 = uintBitsToFloat(texture( boneTexture, vec2(samplerUV.x + 1.0 / boneTextureSize.x, samplerUV.y)));
|
|
211
|
-
vec4 v3 = uintBitsToFloat(texture( boneTexture, vec2(samplerUV.x + 2.0 / boneTextureSize.x, samplerUV.y) ));
|
|
212
|
-
vec4 v4 = uintBitsToFloat(texture( boneTexture, vec2(samplerUV.x + 3.0 / boneTextureSize.x, samplerUV.y)));
|
|
213
|
-
|
|
214
|
-
return mat4( v1, v2, v3, v4 );
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
void main () {
|
|
218
|
-
|
|
219
|
-
uint oddOffset = splatIndex & uint(0x00000001);
|
|
220
|
-
uint doubleOddOffset = oddOffset * uint(2);
|
|
221
|
-
bool isEven = oddOffset == uint(0);
|
|
222
|
-
uint nearestEvenIndex = splatIndex - oddOffset;
|
|
223
|
-
float fOddOffset = float(oddOffset);
|
|
224
|
-
|
|
225
|
-
uvec4 sampledCenterColor = texture(centersColorsTexture, getDataUV(1, 0, centersColorsTextureSize));
|
|
226
|
-
// vec3 splatCenter = uintBitsToFloat(uvec3(sampledCenterColor.gba));
|
|
227
|
-
|
|
228
|
-
uvec3 sampledCenter = texture(centersColorsTexture, getDataUV(1, 0, centersColorsTextureSize)).gba;
|
|
229
|
-
vec3 splatCenter = uintBitsToFloat(uvec3(sampledCenter));
|
|
230
|
-
|
|
231
|
-
vec2 flameTextureUV = getBSFlameDataUV(bsCount, 1, 0, flameModelTextureSize);
|
|
232
|
-
uvec3 sampledflamePos = texture(flameModelTexture, flameTextureUV).rgb;
|
|
233
|
-
// splatCenter += uintBitsToFloat(uvec3(sampledflamePos.rgb));
|
|
234
|
-
|
|
235
|
-
for(int i = 0; i < bsCount; ++i) {
|
|
236
|
-
vec2 flameBSTextureUV = getBSFlameDataUV(i, 1, 0, flameModelTextureSize);
|
|
237
|
-
uvec3 sampledBSPos = texture(flameModelTexture, flameBSTextureUV).rgb;
|
|
238
|
-
|
|
239
|
-
vec2 samplerUV = vec2(0.0, 0.0);
|
|
240
|
-
float d = float(i / 4 + 5 * 4) / boneTextureSize.x;//4
|
|
241
|
-
samplerUV.y = float(floor(d)) / boneTextureSize.y;//32
|
|
242
|
-
samplerUV.x = fract(d);
|
|
243
|
-
|
|
244
|
-
vec4 bsWeight = uintBitsToFloat(texture(boneTexture, samplerUV));
|
|
245
|
-
float weight = bsWeight.r;
|
|
246
|
-
if(i % 4 == 1) {
|
|
247
|
-
weight = bsWeight.g;
|
|
248
|
-
}
|
|
249
|
-
if(i % 4 == 2) {
|
|
250
|
-
weight = bsWeight.b;
|
|
251
|
-
}
|
|
252
|
-
if(i % 4 == 3) {
|
|
253
|
-
weight = bsWeight.a;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
splatCenter = splatCenter + weight * uintBitsToFloat(sampledBSPos);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
#ifdef USE_SKINNING
|
|
261
|
-
mat4 boneMatX = getBoneMatrix0( skinIndex.x );
|
|
262
|
-
mat4 boneMatY = getBoneMatrix0( skinIndex.y );
|
|
263
|
-
mat4 boneMatZ = getBoneMatrix0( skinIndex.z );
|
|
264
|
-
mat4 boneMatW = getBoneMatrix0( skinIndex.w );
|
|
265
|
-
#endif
|
|
266
|
-
#ifdef USE_SKINNING
|
|
267
|
-
mat4 skinMatrix = mat4( 0.0 );
|
|
268
|
-
skinMatrix += skinWeight.x * boneMatX;
|
|
269
|
-
skinMatrix += skinWeight.y * boneMatY;
|
|
270
|
-
skinMatrix += skinWeight.z * boneMatZ;
|
|
271
|
-
skinMatrix += skinWeight.w * boneMatW;
|
|
272
|
-
// skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;
|
|
273
|
-
#endif
|
|
274
|
-
vec3 transformed = vec3(splatCenter.xyz);
|
|
275
|
-
#ifdef USE_SKINNING
|
|
276
|
-
// vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );
|
|
277
|
-
vec4 skinVertex = vec4( transformed, 1.0 );
|
|
278
|
-
|
|
279
|
-
vec4 skinned = vec4( 0.0 );
|
|
280
|
-
// There is an offset between the Gaussian point and the mesh vertex,
|
|
281
|
-
// which will cause defects in the skeletal animation driving the Gaussian point.
|
|
282
|
-
//In order to circumvent this problem, only the head bone(index is 110 currently) is used to drive
|
|
283
|
-
|
|
284
|
-
if (headBoneIndex >= 0.0)
|
|
285
|
-
{
|
|
286
|
-
mat4 boneMat = getBoneMatrix0( headBoneIndex );
|
|
287
|
-
skinned += boneMat * skinVertex * 1.0;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// skinned += boneMatX * skinVertex * skinWeight.x;
|
|
291
|
-
// skinned += boneMatY * skinVertex * skinWeight.y;
|
|
292
|
-
// skinned += boneMatZ * skinVertex * skinWeight.z;
|
|
293
|
-
// skinned += boneMatW * skinVertex * skinWeight.w;
|
|
294
|
-
|
|
295
|
-
// transformed = ( bindMatrixInverse * skinned ).xyz;
|
|
296
|
-
transformed = skinned.xyz;
|
|
297
|
-
|
|
298
|
-
#endif
|
|
299
|
-
splatCenter = transformed.xyz;
|
|
300
|
-
|
|
301
|
-
#ifdef USE_FLAME
|
|
302
|
-
mat4 boneMatX = getBoneMatrix( 0.0 );
|
|
303
|
-
mat4 boneMatY = getBoneMatrix( 1.0 );
|
|
304
|
-
mat4 boneMatZ = getBoneMatrix( 2.0 );
|
|
305
|
-
mat4 boneMatW = getBoneMatrix( 3.0 );
|
|
306
|
-
mat4 boneMat0 = getBoneMatrix( 4.0 );
|
|
307
|
-
|
|
308
|
-
vec2 boneWeightUV0 = getBoneWeightUV(2, 0, boneWeightTextureSize);
|
|
309
|
-
vec2 boneWeightUV1 = getBoneWeightUV(2, 1, boneWeightTextureSize);
|
|
310
|
-
|
|
311
|
-
uvec4 sampledBoneMatrixValue = texture(boneWeightTexture, boneWeightUV0);
|
|
312
|
-
uvec4 sampledBoneMatrixValue0 = texture(boneWeightTexture, boneWeightUV1);
|
|
313
|
-
|
|
314
|
-
vec4 boneMatrixValue = uintBitsToFloat(sampledBoneMatrixValue);
|
|
315
|
-
vec4 boneMatrixValue0 = uintBitsToFloat(sampledBoneMatrixValue0);
|
|
316
|
-
|
|
317
|
-
vec4 skinVertex = vec4( splatCenter, 1.0 );
|
|
318
|
-
vec4 skinned = vec4( 0.0 );
|
|
319
|
-
float minWeight = min(boneMatrixValue.x,min(boneMatrixValue.y, min(boneMatrixValue.z, min(boneMatrixValue.w, boneMatrixValue0.x))));
|
|
320
|
-
|
|
321
|
-
if(boneMatrixValue.x > 0.0 && boneMatrixValue.x > minWeight)
|
|
322
|
-
skinned += boneMatX * skinVertex * boneMatrixValue.x;
|
|
323
|
-
|
|
324
|
-
if(boneMatrixValue.y > 0.0 && boneMatrixValue.y > minWeight)
|
|
325
|
-
skinned += boneMatY * skinVertex * boneMatrixValue.y;
|
|
326
|
-
|
|
327
|
-
if(boneMatrixValue.z > 0.0 && boneMatrixValue.z > minWeight)
|
|
328
|
-
skinned += boneMatZ * skinVertex * boneMatrixValue.z;
|
|
329
|
-
|
|
330
|
-
if(boneMatrixValue.w > 0.0 && boneMatrixValue.w > minWeight)
|
|
331
|
-
skinned += boneMatW * skinVertex * boneMatrixValue.w;
|
|
332
|
-
|
|
333
|
-
if(boneMatrixValue0.x > 0.0 && boneMatrixValue0.x > minWeight)
|
|
334
|
-
skinned += boneMat0 * skinVertex * boneMatrixValue0.x;
|
|
335
|
-
|
|
336
|
-
splatCenter = skinned.xyz;
|
|
337
|
-
#endif
|
|
338
|
-
|
|
339
|
-
uint sceneIndex = uint(0);
|
|
340
|
-
if (sceneCount > 1) {
|
|
341
|
-
sceneIndex = texture(sceneIndexesTexture, getDataUV(1, 0, sceneIndexesTextureSize)).r;
|
|
342
|
-
}
|
|
343
|
-
`;
|
|
344
|
-
|
|
345
|
-
if (enableOptionalEffects) {
|
|
346
|
-
vertexShaderSource += `
|
|
347
|
-
float splatOpacityFromScene = sceneOpacity[sceneIndex];
|
|
348
|
-
int sceneVisible = sceneVisibility[sceneIndex];
|
|
349
|
-
if (splatOpacityFromScene <= 0.01 || sceneVisible == 0) {
|
|
350
|
-
gl_Position = vec4(0.0, 0.0, 2.0, 1.0);
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
`;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (dynamicMode) {
|
|
357
|
-
vertexShaderSource += `
|
|
358
|
-
mat4 transform = transforms[sceneIndex];
|
|
359
|
-
mat4 transformModelViewMatrix = viewMatrix * transform;
|
|
360
|
-
#ifdef USE_SKINNING
|
|
361
|
-
transformModelViewMatrix = transformModelViewMatrix * skinMatrix;
|
|
362
|
-
#endif
|
|
363
|
-
`;
|
|
364
|
-
} else {
|
|
365
|
-
vertexShaderSource += `mat4 transformModelViewMatrix = modelViewMatrix;`;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
vertexShaderSource += `
|
|
369
|
-
float sh8BitCompressionRangeMinForScene = sphericalHarmonics8BitCompressionRangeMin[sceneIndex];
|
|
370
|
-
float sh8BitCompressionRangeMaxForScene = sphericalHarmonics8BitCompressionRangeMax[sceneIndex];
|
|
371
|
-
float sh8BitCompressionRangeForScene = sh8BitCompressionRangeMaxForScene - sh8BitCompressionRangeMinForScene;
|
|
372
|
-
float sh8BitCompressionHalfRangeForScene = sh8BitCompressionRangeForScene / 2.0;
|
|
373
|
-
vec3 vec8BitSHShift = vec3(sh8BitCompressionRangeMinForScene);
|
|
374
|
-
|
|
375
|
-
vec4 viewCenter = transformModelViewMatrix * vec4(splatCenter, 1.0);
|
|
376
|
-
|
|
377
|
-
vec4 clipCenter = projectionMatrix * viewCenter;
|
|
378
|
-
|
|
379
|
-
float clip = 1.2 * clipCenter.w;
|
|
380
|
-
if (clipCenter.z < -clip || clipCenter.x < -clip || clipCenter.x > clip || clipCenter.y < -clip || clipCenter.y > clip) {
|
|
381
|
-
gl_Position = vec4(0.0, 0.0, 2.0, 1.0);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
vec3 ndcCenter = clipCenter.xyz / clipCenter.w;
|
|
386
|
-
|
|
387
|
-
vPosition = position.xy;
|
|
388
|
-
vSplatIndex = vec2(splatIndex, splatIndex);
|
|
389
|
-
|
|
390
|
-
vColor = uintToRGBAVec(sampledCenterColor.r);
|
|
391
|
-
`;
|
|
392
|
-
|
|
393
|
-
// Proceed to sampling and rendering 1st degree spherical harmonics
|
|
394
|
-
if (maxSphericalHarmonicsDegree >= 1) {
|
|
395
|
-
|
|
396
|
-
vertexShaderSource += `
|
|
397
|
-
if (sphericalHarmonicsDegree >= 1) {
|
|
398
|
-
`;
|
|
399
|
-
|
|
400
|
-
if (dynamicMode) {
|
|
401
|
-
vertexShaderSource += `
|
|
402
|
-
vec3 worldViewDir = normalize(splatCenter - vec3(inverse(transform) * vec4(cameraPosition, 1.0)));
|
|
403
|
-
`;
|
|
404
|
-
} else {
|
|
405
|
-
vertexShaderSource += `
|
|
406
|
-
vec3 worldViewDir = normalize(splatCenter - cameraPosition);
|
|
407
|
-
`;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
vertexShaderSource += `
|
|
411
|
-
vec3 sh1;
|
|
412
|
-
vec3 sh2;
|
|
413
|
-
vec3 sh3;
|
|
414
|
-
`;
|
|
415
|
-
|
|
416
|
-
if (maxSphericalHarmonicsDegree >= 2) {
|
|
417
|
-
vertexShaderSource += `
|
|
418
|
-
vec3 sh4;
|
|
419
|
-
vec3 sh5;
|
|
420
|
-
vec3 sh6;
|
|
421
|
-
vec3 sh7;
|
|
422
|
-
vec3 sh8;
|
|
423
|
-
`;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// Determining how to sample spherical harmonics textures to get the coefficients for calculations for a given degree
|
|
427
|
-
// depends on how many total degrees (maxSphericalHarmonicsDegree) are present in the textures. This is because that
|
|
428
|
-
// number affects how they are packed in the textures, and therefore the offset & stride required to access them.
|
|
429
|
-
|
|
430
|
-
// Sample spherical harmonics textures with 1 degree worth of data for 1st degree calculations, and store in sh1, sh2, and sh3
|
|
431
|
-
if (maxSphericalHarmonicsDegree === 1) {
|
|
432
|
-
vertexShaderSource += `
|
|
433
|
-
if (sphericalHarmonicsMultiTextureMode == 0) {
|
|
434
|
-
vec2 shUV = getDataUVF(nearestEvenIndex, 2.5, doubleOddOffset, sphericalHarmonicsTextureSize);
|
|
435
|
-
vec4 sampledSH0123 = texture(sphericalHarmonicsTexture, shUV);
|
|
436
|
-
shUV = getDataUVF(nearestEvenIndex, 2.5, doubleOddOffset + uint(1), sphericalHarmonicsTextureSize);
|
|
437
|
-
vec4 sampledSH4567 = texture(sphericalHarmonicsTexture, shUV);
|
|
438
|
-
shUV = getDataUVF(nearestEvenIndex, 2.5, doubleOddOffset + uint(2), sphericalHarmonicsTextureSize);
|
|
439
|
-
vec4 sampledSH891011 = texture(sphericalHarmonicsTexture, shUV);
|
|
440
|
-
sh1 = vec3(sampledSH0123.rgb) * (1.0 - fOddOffset) + vec3(sampledSH0123.ba, sampledSH4567.r) * fOddOffset;
|
|
441
|
-
sh2 = vec3(sampledSH0123.a, sampledSH4567.rg) * (1.0 - fOddOffset) + vec3(sampledSH4567.gba) * fOddOffset;
|
|
442
|
-
sh3 = vec3(sampledSH4567.ba, sampledSH891011.r) * (1.0 - fOddOffset) + vec3(sampledSH891011.rgb) * fOddOffset;
|
|
443
|
-
} else {
|
|
444
|
-
vec2 sampledSH01R = texture(sphericalHarmonicsTextureR, getDataUV(2, 0, sphericalHarmonicsTextureSize)).rg;
|
|
445
|
-
vec2 sampledSH23R = texture(sphericalHarmonicsTextureR, getDataUV(2, 1, sphericalHarmonicsTextureSize)).rg;
|
|
446
|
-
vec2 sampledSH01G = texture(sphericalHarmonicsTextureG, getDataUV(2, 0, sphericalHarmonicsTextureSize)).rg;
|
|
447
|
-
vec2 sampledSH23G = texture(sphericalHarmonicsTextureG, getDataUV(2, 1, sphericalHarmonicsTextureSize)).rg;
|
|
448
|
-
vec2 sampledSH01B = texture(sphericalHarmonicsTextureB, getDataUV(2, 0, sphericalHarmonicsTextureSize)).rg;
|
|
449
|
-
vec2 sampledSH23B = texture(sphericalHarmonicsTextureB, getDataUV(2, 1, sphericalHarmonicsTextureSize)).rg;
|
|
450
|
-
sh1 = vec3(sampledSH01R.rg, sampledSH23R.r);
|
|
451
|
-
sh2 = vec3(sampledSH01G.rg, sampledSH23G.r);
|
|
452
|
-
sh3 = vec3(sampledSH01B.rg, sampledSH23B.r);
|
|
453
|
-
}
|
|
454
|
-
`;
|
|
455
|
-
// Sample spherical harmonics textures with 2 degrees worth of data for 1st degree calculations, and store in sh1, sh2, and sh3
|
|
456
|
-
} else if (maxSphericalHarmonicsDegree === 2) {
|
|
457
|
-
vertexShaderSource += `
|
|
458
|
-
vec4 sampledSH0123;
|
|
459
|
-
vec4 sampledSH4567;
|
|
460
|
-
vec4 sampledSH891011;
|
|
461
|
-
|
|
462
|
-
vec4 sampledSH0123R;
|
|
463
|
-
vec4 sampledSH0123G;
|
|
464
|
-
vec4 sampledSH0123B;
|
|
465
|
-
|
|
466
|
-
if (sphericalHarmonicsMultiTextureMode == 0) {
|
|
467
|
-
sampledSH0123 = texture(sphericalHarmonicsTexture, getDataUV(6, 0, sphericalHarmonicsTextureSize));
|
|
468
|
-
sampledSH4567 = texture(sphericalHarmonicsTexture, getDataUV(6, 1, sphericalHarmonicsTextureSize));
|
|
469
|
-
sampledSH891011 = texture(sphericalHarmonicsTexture, getDataUV(6, 2, sphericalHarmonicsTextureSize));
|
|
470
|
-
sh1 = sampledSH0123.rgb;
|
|
471
|
-
sh2 = vec3(sampledSH0123.a, sampledSH4567.rg);
|
|
472
|
-
sh3 = vec3(sampledSH4567.ba, sampledSH891011.r);
|
|
473
|
-
} else {
|
|
474
|
-
sampledSH0123R = texture(sphericalHarmonicsTextureR, getDataUV(2, 0, sphericalHarmonicsTextureSize));
|
|
475
|
-
sampledSH0123G = texture(sphericalHarmonicsTextureG, getDataUV(2, 0, sphericalHarmonicsTextureSize));
|
|
476
|
-
sampledSH0123B = texture(sphericalHarmonicsTextureB, getDataUV(2, 0, sphericalHarmonicsTextureSize));
|
|
477
|
-
sh1 = vec3(sampledSH0123R.rgb);
|
|
478
|
-
sh2 = vec3(sampledSH0123G.rgb);
|
|
479
|
-
sh3 = vec3(sampledSH0123B.rgb);
|
|
480
|
-
}
|
|
481
|
-
`;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Perform 1st degree spherical harmonics calculations
|
|
485
|
-
vertexShaderSource += `
|
|
486
|
-
if (sphericalHarmonics8BitMode == 1) {
|
|
487
|
-
sh1 = sh1 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
488
|
-
sh2 = sh2 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
489
|
-
sh3 = sh3 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
490
|
-
}
|
|
491
|
-
float x = worldViewDir.x;
|
|
492
|
-
float y = worldViewDir.y;
|
|
493
|
-
float z = worldViewDir.z;
|
|
494
|
-
vColor.rgb += SH_C1 * (-sh1 * y + sh2 * z - sh3 * x);
|
|
495
|
-
`;
|
|
496
|
-
|
|
497
|
-
// Proceed to sampling and rendering 2nd degree spherical harmonics
|
|
498
|
-
if (maxSphericalHarmonicsDegree >= 2) {
|
|
499
|
-
|
|
500
|
-
vertexShaderSource += `
|
|
501
|
-
if (sphericalHarmonicsDegree >= 2) {
|
|
502
|
-
float xx = x * x;
|
|
503
|
-
float yy = y * y;
|
|
504
|
-
float zz = z * z;
|
|
505
|
-
float xy = x * y;
|
|
506
|
-
float yz = y * z;
|
|
507
|
-
float xz = x * z;
|
|
508
|
-
`;
|
|
509
|
-
|
|
510
|
-
// Sample spherical harmonics textures with 2 degrees worth of data for 2nd degree calculations,
|
|
511
|
-
// and store in sh4, sh5, sh6, sh7, and sh8
|
|
512
|
-
if (maxSphericalHarmonicsDegree === 2) {
|
|
513
|
-
vertexShaderSource += `
|
|
514
|
-
if (sphericalHarmonicsMultiTextureMode == 0) {
|
|
515
|
-
vec4 sampledSH12131415 = texture(sphericalHarmonicsTexture, getDataUV(6, 3, sphericalHarmonicsTextureSize));
|
|
516
|
-
vec4 sampledSH16171819 = texture(sphericalHarmonicsTexture, getDataUV(6, 4, sphericalHarmonicsTextureSize));
|
|
517
|
-
vec4 sampledSH20212223 = texture(sphericalHarmonicsTexture, getDataUV(6, 5, sphericalHarmonicsTextureSize));
|
|
518
|
-
sh4 = sampledSH891011.gba;
|
|
519
|
-
sh5 = sampledSH12131415.rgb;
|
|
520
|
-
sh6 = vec3(sampledSH12131415.a, sampledSH16171819.rg);
|
|
521
|
-
sh7 = vec3(sampledSH16171819.ba, sampledSH20212223.r);
|
|
522
|
-
sh8 = sampledSH20212223.gba;
|
|
523
|
-
} else {
|
|
524
|
-
vec4 sampledSH4567R = texture(sphericalHarmonicsTextureR, getDataUV(2, 1, sphericalHarmonicsTextureSize));
|
|
525
|
-
vec4 sampledSH4567G = texture(sphericalHarmonicsTextureG, getDataUV(2, 1, sphericalHarmonicsTextureSize));
|
|
526
|
-
vec4 sampledSH4567B = texture(sphericalHarmonicsTextureB, getDataUV(2, 1, sphericalHarmonicsTextureSize));
|
|
527
|
-
sh4 = vec3(sampledSH0123R.a, sampledSH4567R.rg);
|
|
528
|
-
sh5 = vec3(sampledSH4567R.ba, sampledSH0123G.a);
|
|
529
|
-
sh6 = vec3(sampledSH4567G.rgb);
|
|
530
|
-
sh7 = vec3(sampledSH4567G.a, sampledSH0123B.a, sampledSH4567B.r);
|
|
531
|
-
sh8 = vec3(sampledSH4567B.gba);
|
|
532
|
-
}
|
|
533
|
-
`;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Perform 2nd degree spherical harmonics calculations
|
|
537
|
-
vertexShaderSource += `
|
|
538
|
-
if (sphericalHarmonics8BitMode == 1) {
|
|
539
|
-
sh4 = sh4 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
540
|
-
sh5 = sh5 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
541
|
-
sh6 = sh6 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
542
|
-
sh7 = sh7 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
543
|
-
sh8 = sh8 * sh8BitCompressionRangeForScene + vec8BitSHShift;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
vColor.rgb +=
|
|
547
|
-
(SH_C2[0] * xy) * sh4 +
|
|
548
|
-
(SH_C2[1] * yz) * sh5 +
|
|
549
|
-
(SH_C2[2] * (2.0 * zz - xx - yy)) * sh6 +
|
|
550
|
-
(SH_C2[3] * xz) * sh7 +
|
|
551
|
-
(SH_C2[4] * (xx - yy)) * sh8;
|
|
552
|
-
}
|
|
553
|
-
`;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
vertexShaderSource += `
|
|
557
|
-
|
|
558
|
-
vColor.rgb = clamp(vColor.rgb, vec3(0.), vec3(1.));
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
`;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return vertexShaderSource;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
static getVertexShaderFadeIn() {
|
|
569
|
-
return `
|
|
570
|
-
if (fadeInComplete == 0) {
|
|
571
|
-
float opacityAdjust = 1.0;
|
|
572
|
-
float centerDist = length(splatCenter - sceneCenter);
|
|
573
|
-
float renderTime = max(currentTime - firstRenderTime, 0.0);
|
|
574
|
-
|
|
575
|
-
float fadeDistance = 0.75;
|
|
576
|
-
float distanceLoadFadeInFactor = step(visibleRegionFadeStartRadius, centerDist);
|
|
577
|
-
distanceLoadFadeInFactor = (1.0 - distanceLoadFadeInFactor) +
|
|
578
|
-
(1.0 - clamp((centerDist - visibleRegionFadeStartRadius) / fadeDistance, 0.0, 1.0)) *
|
|
579
|
-
distanceLoadFadeInFactor;
|
|
580
|
-
opacityAdjust *= distanceLoadFadeInFactor;
|
|
581
|
-
vColor.a *= opacityAdjust;
|
|
582
|
-
}
|
|
583
|
-
`;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
static getUniforms(dynamicMode = false, enableOptionalEffects = false, maxSphericalHarmonicsDegree = 0,
|
|
587
|
-
splatScale = 1.0, pointCloudModeEnabled = false) {
|
|
588
|
-
|
|
589
|
-
const uniforms = {
|
|
590
|
-
'sceneCenter': {
|
|
591
|
-
'type': 'v3',
|
|
592
|
-
'value': new Vector3()
|
|
593
|
-
},
|
|
594
|
-
'fadeInComplete': {
|
|
595
|
-
'type': 'i',
|
|
596
|
-
'value': 0
|
|
597
|
-
},
|
|
598
|
-
'orthographicMode': {
|
|
599
|
-
'type': 'i',
|
|
600
|
-
'value': 0
|
|
601
|
-
},
|
|
602
|
-
'visibleRegionFadeStartRadius': {
|
|
603
|
-
'type': 'f',
|
|
604
|
-
'value': 0.0
|
|
605
|
-
},
|
|
606
|
-
'visibleRegionRadius': {
|
|
607
|
-
'type': 'f',
|
|
608
|
-
'value': 0.0
|
|
609
|
-
},
|
|
610
|
-
'bindMatrix': {
|
|
611
|
-
'type': 'm4',
|
|
612
|
-
'value': new Matrix4()
|
|
613
|
-
},
|
|
614
|
-
'bindMatrixInverse': {
|
|
615
|
-
'type': 'm4',
|
|
616
|
-
'value': new Matrix4()
|
|
617
|
-
},
|
|
618
|
-
'currentTime': {
|
|
619
|
-
'type': 'f',
|
|
620
|
-
'value': 0.0
|
|
621
|
-
},
|
|
622
|
-
'firstRenderTime': {
|
|
623
|
-
'type': 'f',
|
|
624
|
-
'value': 0.0
|
|
625
|
-
},
|
|
626
|
-
'centersColorsTexture': {
|
|
627
|
-
'type': 't',
|
|
628
|
-
'value': null
|
|
629
|
-
},
|
|
630
|
-
'flameModelTexture': {
|
|
631
|
-
'type': 't',
|
|
632
|
-
'value': null
|
|
633
|
-
},
|
|
634
|
-
'boneTexture': {
|
|
635
|
-
'type': 't',
|
|
636
|
-
'value': null
|
|
637
|
-
},
|
|
638
|
-
'boneTexture0': {
|
|
639
|
-
'type': 't',
|
|
640
|
-
'value': null
|
|
641
|
-
},
|
|
642
|
-
'boneWeightTexture': {
|
|
643
|
-
'type': 't',
|
|
644
|
-
'value': null
|
|
645
|
-
},
|
|
646
|
-
'sphericalHarmonicsTexture': {
|
|
647
|
-
'type': 't',
|
|
648
|
-
'value': null
|
|
649
|
-
},
|
|
650
|
-
'sphericalHarmonicsTextureR': {
|
|
651
|
-
'type': 't',
|
|
652
|
-
'value': null
|
|
653
|
-
},
|
|
654
|
-
'sphericalHarmonicsTextureG': {
|
|
655
|
-
'type': 't',
|
|
656
|
-
'value': null
|
|
657
|
-
},
|
|
658
|
-
'sphericalHarmonicsTextureB': {
|
|
659
|
-
'type': 't',
|
|
660
|
-
'value': null
|
|
661
|
-
},
|
|
662
|
-
'sphericalHarmonics8BitCompressionRangeMin': {
|
|
663
|
-
'type': 'f',
|
|
664
|
-
'value': []
|
|
665
|
-
},
|
|
666
|
-
'sphericalHarmonics8BitCompressionRangeMax': {
|
|
667
|
-
'type': 'f',
|
|
668
|
-
'value': []
|
|
669
|
-
},
|
|
670
|
-
'focal': {
|
|
671
|
-
'type': 'v2',
|
|
672
|
-
'value': new Vector2()
|
|
673
|
-
},
|
|
674
|
-
'orthoZoom': {
|
|
675
|
-
'type': 'f',
|
|
676
|
-
'value': 1.0
|
|
677
|
-
},
|
|
678
|
-
'inverseFocalAdjustment': {
|
|
679
|
-
'type': 'f',
|
|
680
|
-
'value': 1.0
|
|
681
|
-
},
|
|
682
|
-
'viewport': {
|
|
683
|
-
'type': 'v2',
|
|
684
|
-
'value': new Vector2()
|
|
685
|
-
},
|
|
686
|
-
'basisViewport': {
|
|
687
|
-
'type': 'v2',
|
|
688
|
-
'value': new Vector2()
|
|
689
|
-
},
|
|
690
|
-
'debugColor': {
|
|
691
|
-
'type': 'v3',
|
|
692
|
-
'value': new Color()
|
|
693
|
-
},
|
|
694
|
-
'centersColorsTextureSize': {
|
|
695
|
-
'type': 'v2',
|
|
696
|
-
'value': new Vector2(1024, 1024)
|
|
697
|
-
},
|
|
698
|
-
'flameModelTextureSize': {
|
|
699
|
-
'type': 'v2',
|
|
700
|
-
'value': new Vector2(4096, 2048)
|
|
701
|
-
},
|
|
702
|
-
'boneTextureSize': {
|
|
703
|
-
'type': 'v2',
|
|
704
|
-
'value': new Vector2(4, 32)
|
|
705
|
-
},
|
|
706
|
-
'boneWeightTextureSize': {
|
|
707
|
-
'type': 'v2',
|
|
708
|
-
'value': new Vector2(512, 512)
|
|
709
|
-
},
|
|
710
|
-
|
|
711
|
-
'sphericalHarmonicsDegree': {
|
|
712
|
-
'type': 'i',
|
|
713
|
-
'value': maxSphericalHarmonicsDegree
|
|
714
|
-
},
|
|
715
|
-
'sphericalHarmonicsTextureSize': {
|
|
716
|
-
'type': 'v2',
|
|
717
|
-
'value': new Vector2(1024, 1024)
|
|
718
|
-
},
|
|
719
|
-
'sphericalHarmonics8BitMode': {
|
|
720
|
-
'type': 'i',
|
|
721
|
-
'value': 0
|
|
722
|
-
},
|
|
723
|
-
'sphericalHarmonicsMultiTextureMode': {
|
|
724
|
-
'type': 'i',
|
|
725
|
-
'value': 0
|
|
726
|
-
},
|
|
727
|
-
'splatScale': {
|
|
728
|
-
'type': 'f',
|
|
729
|
-
'value': splatScale
|
|
730
|
-
},
|
|
731
|
-
'pointCloudModeEnabled': {
|
|
732
|
-
'type': 'i',
|
|
733
|
-
'value': pointCloudModeEnabled ? 1 : 0
|
|
734
|
-
},
|
|
735
|
-
'sceneIndexesTexture': {
|
|
736
|
-
'type': 't',
|
|
737
|
-
'value': null
|
|
738
|
-
},
|
|
739
|
-
'sceneIndexesTextureSize': {
|
|
740
|
-
'type': 'v2',
|
|
741
|
-
'value': new Vector2(1024, 1024)
|
|
742
|
-
},
|
|
743
|
-
'sceneCount': {
|
|
744
|
-
'type': 'i',
|
|
745
|
-
'value': 1
|
|
746
|
-
},
|
|
747
|
-
'gaussianSplatCount': {
|
|
748
|
-
'type': 'i',
|
|
749
|
-
'value': 1
|
|
750
|
-
},
|
|
751
|
-
'bsCount': {
|
|
752
|
-
'type': 'i',
|
|
753
|
-
'value': 1
|
|
754
|
-
},
|
|
755
|
-
'headBoneIndex': {
|
|
756
|
-
'type': 'f',
|
|
757
|
-
'value': -1.0
|
|
758
|
-
}
|
|
759
|
-
};
|
|
760
|
-
for (let i = 0; i < Constants.MaxScenes; i++) {
|
|
761
|
-
uniforms.sphericalHarmonics8BitCompressionRangeMin.value.push(-Constants.SphericalHarmonics8BitCompressionRange / 2.0);
|
|
762
|
-
uniforms.sphericalHarmonics8BitCompressionRangeMax.value.push(Constants.SphericalHarmonics8BitCompressionRange / 2.0);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
if (enableOptionalEffects) {
|
|
766
|
-
const sceneOpacity = [];
|
|
767
|
-
for (let i = 0; i < Constants.MaxScenes; i++) {
|
|
768
|
-
sceneOpacity.push(1.0);
|
|
769
|
-
}
|
|
770
|
-
uniforms['sceneOpacity'] ={
|
|
771
|
-
'type': 'f',
|
|
772
|
-
'value': sceneOpacity
|
|
773
|
-
};
|
|
774
|
-
|
|
775
|
-
const sceneVisibility = [];
|
|
776
|
-
for (let i = 0; i < Constants.MaxScenes; i++) {
|
|
777
|
-
sceneVisibility.push(1);
|
|
778
|
-
}
|
|
779
|
-
uniforms['sceneVisibility'] ={
|
|
780
|
-
'type': 'i',
|
|
781
|
-
'value': sceneVisibility
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
if (dynamicMode) {
|
|
786
|
-
const transformMatrices = [];
|
|
787
|
-
for (let i = 0; i < Constants.MaxScenes; i++) {
|
|
788
|
-
transformMatrices.push(new Matrix4());
|
|
789
|
-
}
|
|
790
|
-
uniforms['transforms'] = {
|
|
791
|
-
'type': 'mat4',
|
|
792
|
-
'value': transformMatrices
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
return uniforms;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
class SplatMaterial3D {
|
|
802
|
-
|
|
803
|
-
/**
|
|
804
|
-
* Build the Three.js material that is used to render the splats.
|
|
805
|
-
* @param {number} dynamicMode If true, it means the scene geometry represented by this splat mesh is not stationary or
|
|
806
|
-
* that the splat count might change
|
|
807
|
-
* @param {boolean} enableOptionalEffects When true, allows for usage of extra properties and attributes in the shader for effects
|
|
808
|
-
* such as opacity adjustment. Default is false for performance reasons.
|
|
809
|
-
* @param {boolean} antialiased If true, calculate compensation factor to deal with gaussians being rendered at a significantly
|
|
810
|
-
* different resolution than that of their training
|
|
811
|
-
* @param {number} maxScreenSpaceSplatSize The maximum clip space splat size
|
|
812
|
-
* @param {number} splatScale Value by which all splats are scaled in screen-space (default is 1.0)
|
|
813
|
-
* @param {number} pointCloudModeEnabled Render all splats as screen-space circles
|
|
814
|
-
* @param {number} maxSphericalHarmonicsDegree Degree of spherical harmonics to utilize in rendering splats
|
|
815
|
-
* @return {THREE.ShaderMaterial}
|
|
816
|
-
*/
|
|
817
|
-
static build(dynamicMode = false, enableOptionalEffects = false, antialiased = false, maxScreenSpaceSplatSize = 2048,
|
|
818
|
-
splatScale = 1.0, pointCloudModeEnabled = false, maxSphericalHarmonicsDegree = 0, kernel2DSize = 0.3, useFlame = true) {
|
|
819
|
-
|
|
820
|
-
const customVertexVars = `
|
|
821
|
-
uniform vec2 covariancesTextureSize;
|
|
822
|
-
uniform highp sampler2D covariancesTexture;
|
|
823
|
-
uniform highp usampler2D covariancesTextureHalfFloat;
|
|
824
|
-
uniform int covariancesAreHalfFloat;
|
|
825
|
-
|
|
826
|
-
void fromCovarianceHalfFloatV4(uvec4 val, out vec4 first, out vec4 second) {
|
|
827
|
-
vec2 r = unpackHalf2x16(val.r);
|
|
828
|
-
vec2 g = unpackHalf2x16(val.g);
|
|
829
|
-
vec2 b = unpackHalf2x16(val.b);
|
|
830
|
-
|
|
831
|
-
first = vec4(r.x, r.y, g.x, g.y);
|
|
832
|
-
second = vec4(b.x, b.y, 0.0, 0.0);
|
|
833
|
-
}
|
|
834
|
-
`;
|
|
835
|
-
|
|
836
|
-
let vertexShaderSource = SplatMaterial.buildVertexShaderBase(dynamicMode, enableOptionalEffects,
|
|
837
|
-
maxSphericalHarmonicsDegree, customVertexVars, useFlame);
|
|
838
|
-
vertexShaderSource += SplatMaterial3D.buildVertexShaderProjection(antialiased, enableOptionalEffects,
|
|
839
|
-
maxScreenSpaceSplatSize, kernel2DSize);
|
|
840
|
-
const fragmentShaderSource = SplatMaterial3D.buildFragmentShader();
|
|
841
|
-
|
|
842
|
-
const uniforms = SplatMaterial.getUniforms(dynamicMode, enableOptionalEffects,
|
|
843
|
-
maxSphericalHarmonicsDegree, splatScale, pointCloudModeEnabled);
|
|
844
|
-
|
|
845
|
-
uniforms['covariancesTextureSize'] = {
|
|
846
|
-
'type': 'v2',
|
|
847
|
-
'value': new Vector2(1024, 1024)
|
|
848
|
-
};
|
|
849
|
-
uniforms['covariancesTexture'] = {
|
|
850
|
-
'type': 't',
|
|
851
|
-
'value': null
|
|
852
|
-
};
|
|
853
|
-
uniforms['covariancesTextureHalfFloat'] = {
|
|
854
|
-
'type': 't',
|
|
855
|
-
'value': null
|
|
856
|
-
};
|
|
857
|
-
uniforms['covariancesAreHalfFloat'] = {
|
|
858
|
-
'type': 'i',
|
|
859
|
-
'value': 0
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
const material = new ShaderMaterial({
|
|
863
|
-
uniforms: uniforms,
|
|
864
|
-
vertexShader: vertexShaderSource,
|
|
865
|
-
fragmentShader: fragmentShaderSource,
|
|
866
|
-
transparent: true,
|
|
867
|
-
alphaTest: 1.0,
|
|
868
|
-
blending: NormalBlending,
|
|
869
|
-
depthTest: true,
|
|
870
|
-
depthWrite: false,
|
|
871
|
-
side: DoubleSide
|
|
872
|
-
});
|
|
873
|
-
|
|
874
|
-
return material;
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
static buildVertexShaderProjection(antialiased, enableOptionalEffects, maxScreenSpaceSplatSize, kernel2DSize) {
|
|
878
|
-
let vertexShaderSource = `
|
|
879
|
-
|
|
880
|
-
vec4 sampledCovarianceA;
|
|
881
|
-
vec4 sampledCovarianceB;
|
|
882
|
-
vec3 cov3D_M11_M12_M13;
|
|
883
|
-
vec3 cov3D_M22_M23_M33;
|
|
884
|
-
if (covariancesAreHalfFloat == 0) {
|
|
885
|
-
sampledCovarianceA = texture(covariancesTexture, getDataUVF(nearestEvenIndex, 1.5, oddOffset,
|
|
886
|
-
covariancesTextureSize));
|
|
887
|
-
sampledCovarianceB = texture(covariancesTexture, getDataUVF(nearestEvenIndex, 1.5, oddOffset + uint(1),
|
|
888
|
-
covariancesTextureSize));
|
|
889
|
-
|
|
890
|
-
cov3D_M11_M12_M13 = vec3(sampledCovarianceA.rgb) * (1.0 - fOddOffset) +
|
|
891
|
-
vec3(sampledCovarianceA.ba, sampledCovarianceB.r) * fOddOffset;
|
|
892
|
-
cov3D_M22_M23_M33 = vec3(sampledCovarianceA.a, sampledCovarianceB.rg) * (1.0 - fOddOffset) +
|
|
893
|
-
vec3(sampledCovarianceB.gba) * fOddOffset;
|
|
894
|
-
} else {
|
|
895
|
-
uvec4 sampledCovarianceU = texture(covariancesTextureHalfFloat, getDataUV(1, 0, covariancesTextureSize));
|
|
896
|
-
fromCovarianceHalfFloatV4(sampledCovarianceU, sampledCovarianceA, sampledCovarianceB);
|
|
897
|
-
cov3D_M11_M12_M13 = sampledCovarianceA.rgb;
|
|
898
|
-
cov3D_M22_M23_M33 = vec3(sampledCovarianceA.a, sampledCovarianceB.rg);
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
// Construct the 3D covariance matrix
|
|
902
|
-
mat3 Vrk = mat3(
|
|
903
|
-
cov3D_M11_M12_M13.x, cov3D_M11_M12_M13.y, cov3D_M11_M12_M13.z,
|
|
904
|
-
cov3D_M11_M12_M13.y, cov3D_M22_M23_M33.x, cov3D_M22_M23_M33.y,
|
|
905
|
-
cov3D_M11_M12_M13.z, cov3D_M22_M23_M33.y, cov3D_M22_M23_M33.z
|
|
906
|
-
);
|
|
907
|
-
|
|
908
|
-
mat3 J;
|
|
909
|
-
if (orthographicMode == 1) {
|
|
910
|
-
// Since the projection is linear, we don't need an approximation
|
|
911
|
-
J = transpose(mat3(orthoZoom, 0.0, 0.0,
|
|
912
|
-
0.0, orthoZoom, 0.0,
|
|
913
|
-
0.0, 0.0, 0.0));
|
|
914
|
-
} else {
|
|
915
|
-
// Construct the Jacobian of the affine approximation of the projection matrix. It will be used to transform the
|
|
916
|
-
// 3D covariance matrix instead of using the actual projection matrix because that transformation would
|
|
917
|
-
// require a non-linear component (perspective division) which would yield a non-gaussian result.
|
|
918
|
-
float s = 1.0 / (viewCenter.z * viewCenter.z);
|
|
919
|
-
J = mat3(
|
|
920
|
-
focal.x / viewCenter.z, 0., -(focal.x * viewCenter.x) * s,
|
|
921
|
-
0., focal.y / viewCenter.z, -(focal.y * viewCenter.y) * s,
|
|
922
|
-
0., 0., 0.
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
// Concatenate the projection approximation with the model-view transformation
|
|
927
|
-
mat3 W = transpose(mat3(transformModelViewMatrix));
|
|
928
|
-
mat3 T = W * J;
|
|
929
|
-
|
|
930
|
-
// Transform the 3D covariance matrix (Vrk) to compute the 2D covariance matrix
|
|
931
|
-
mat3 cov2Dm = transpose(T) * Vrk * T;
|
|
932
|
-
`;
|
|
933
|
-
|
|
934
|
-
if (antialiased) {
|
|
935
|
-
vertexShaderSource += `
|
|
936
|
-
float detOrig = cov2Dm[0][0] * cov2Dm[1][1] - cov2Dm[0][1] * cov2Dm[0][1];
|
|
937
|
-
cov2Dm[0][0] += ${kernel2DSize};
|
|
938
|
-
cov2Dm[1][1] += ${kernel2DSize};
|
|
939
|
-
float detBlur = cov2Dm[0][0] * cov2Dm[1][1] - cov2Dm[0][1] * cov2Dm[0][1];
|
|
940
|
-
vColor.a *= sqrt(max(detOrig / detBlur, 0.0));
|
|
941
|
-
if (vColor.a < minAlpha) return;
|
|
942
|
-
`;
|
|
943
|
-
} else {
|
|
944
|
-
vertexShaderSource += `
|
|
945
|
-
cov2Dm[0][0] += ${kernel2DSize};
|
|
946
|
-
cov2Dm[1][1] += ${kernel2DSize};
|
|
947
|
-
`;
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
vertexShaderSource += `
|
|
951
|
-
|
|
952
|
-
// We are interested in the upper-left 2x2 portion of the projected 3D covariance matrix because
|
|
953
|
-
// we only care about the X and Y values. We want the X-diagonal, cov2Dm[0][0],
|
|
954
|
-
// the Y-diagonal, cov2Dm[1][1], and the correlation between the two cov2Dm[0][1]. We don't
|
|
955
|
-
// need cov2Dm[1][0] because it is a symetric matrix.
|
|
956
|
-
vec3 cov2Dv = vec3(cov2Dm[0][0], cov2Dm[0][1], cov2Dm[1][1]);
|
|
957
|
-
|
|
958
|
-
// We now need to solve for the eigen-values and eigen vectors of the 2D covariance matrix
|
|
959
|
-
// so that we can determine the 2D basis for the splat. This is done using the method described
|
|
960
|
-
// here: https://people.math.harvard.edu/~knill/teaching/math21b2004/exhibits/2dmatrices/index.html
|
|
961
|
-
// After calculating the eigen-values and eigen-vectors, we calculate the basis for rendering the splat
|
|
962
|
-
// by normalizing the eigen-vectors and then multiplying them by (sqrt(8) * sqrt(eigen-value)), which is
|
|
963
|
-
// equal to scaling them by sqrt(8) standard deviations.
|
|
964
|
-
//
|
|
965
|
-
// This is a different approach than in the original work at INRIA. In that work they compute the
|
|
966
|
-
// max extents of the projected splat in screen space to form a screen-space aligned bounding rectangle
|
|
967
|
-
// which forms the geometry that is actually rasterized. The dimensions of that bounding box are 3.0
|
|
968
|
-
// times the square root of the maximum eigen-value, or 3 standard deviations. They then use the inverse
|
|
969
|
-
// 2D covariance matrix (called 'conic') in the CUDA rendering thread to determine fragment opacity by
|
|
970
|
-
// calculating the full gaussian: exp(-0.5 * (X - mean) * conic * (X - mean)) * splat opacity
|
|
971
|
-
float a = cov2Dv.x;
|
|
972
|
-
float d = cov2Dv.z;
|
|
973
|
-
float b = cov2Dv.y;
|
|
974
|
-
float D = a * d - b * b;
|
|
975
|
-
float trace = a + d;
|
|
976
|
-
float traceOver2 = 0.5 * trace;
|
|
977
|
-
float term2 = sqrt(max(0.1f, traceOver2 * traceOver2 - D));
|
|
978
|
-
float eigenValue1 = traceOver2 + term2;
|
|
979
|
-
float eigenValue2 = traceOver2 - term2;
|
|
980
|
-
|
|
981
|
-
if (pointCloudModeEnabled == 1) {
|
|
982
|
-
eigenValue1 = eigenValue2 = 0.2;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
if (eigenValue2 <= 0.0) return;
|
|
986
|
-
|
|
987
|
-
vec2 eigenVector1 = normalize(vec2(b, eigenValue1 - a));
|
|
988
|
-
// since the eigen vectors are orthogonal, we derive the second one from the first
|
|
989
|
-
vec2 eigenVector2 = vec2(eigenVector1.y, -eigenVector1.x);
|
|
990
|
-
|
|
991
|
-
// We use sqrt(8) standard deviations instead of 3 to eliminate more of the splat with a very low opacity.
|
|
992
|
-
vec2 basisVector1 = eigenVector1 * splatScale * min(sqrt8 * sqrt(eigenValue1), ${parseInt(maxScreenSpaceSplatSize)}.0);
|
|
993
|
-
vec2 basisVector2 = eigenVector2 * splatScale * min(sqrt8 * sqrt(eigenValue2), ${parseInt(maxScreenSpaceSplatSize)}.0);
|
|
994
|
-
`;
|
|
995
|
-
|
|
996
|
-
if (enableOptionalEffects) {
|
|
997
|
-
vertexShaderSource += `
|
|
998
|
-
vColor.a *= splatOpacityFromScene;
|
|
999
|
-
`;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
vertexShaderSource += `
|
|
1003
|
-
vec2 ndcOffset = vec2(vPosition.x * basisVector1 + vPosition.y * basisVector2) *
|
|
1004
|
-
basisViewport * 2.0 * inverseFocalAdjustment;
|
|
1005
|
-
|
|
1006
|
-
vec4 quadPos = vec4(ndcCenter.xy + ndcOffset, ndcCenter.z, 1.0);
|
|
1007
|
-
gl_Position = quadPos;
|
|
1008
|
-
|
|
1009
|
-
// Scale the position data we send to the fragment shader
|
|
1010
|
-
vPosition *= sqrt8;
|
|
1011
|
-
`;
|
|
1012
|
-
|
|
1013
|
-
vertexShaderSource += SplatMaterial.getVertexShaderFadeIn();
|
|
1014
|
-
vertexShaderSource += `}`;
|
|
1015
|
-
|
|
1016
|
-
return vertexShaderSource;
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
static buildFragmentShader() {
|
|
1020
|
-
let fragmentShaderSource = `
|
|
1021
|
-
precision highp float;
|
|
1022
|
-
#include <common>
|
|
1023
|
-
|
|
1024
|
-
uniform vec3 debugColor;
|
|
1025
|
-
|
|
1026
|
-
varying vec4 vColor;
|
|
1027
|
-
varying vec2 vUv;
|
|
1028
|
-
varying vec2 vPosition;
|
|
1029
|
-
varying vec2 vSplatIndex;
|
|
1030
|
-
|
|
1031
|
-
`;
|
|
1032
|
-
|
|
1033
|
-
fragmentShaderSource += `
|
|
1034
|
-
void main () {
|
|
1035
|
-
// Compute the positional squared distance from the center of the splat to the current fragment.
|
|
1036
|
-
float A = dot(vPosition, vPosition);
|
|
1037
|
-
// Since the positional data in vPosition has been scaled by sqrt(8), the squared result will be
|
|
1038
|
-
// scaled by a factor of 8. If the squared result is larger than 8, it means it is outside the ellipse
|
|
1039
|
-
// defined by the rectangle formed by vPosition. It also means it's farther
|
|
1040
|
-
// away than sqrt(8) standard deviations from the mean.
|
|
1041
|
-
|
|
1042
|
-
// if(vSplatIndex.x > 20000.0) discard;
|
|
1043
|
-
// if (A > 8.0) discard;
|
|
1044
|
-
vec3 color = vColor.rgb;
|
|
1045
|
-
|
|
1046
|
-
// Since the rendered splat is scaled by sqrt(8), the inverse covariance matrix that is part of
|
|
1047
|
-
// the gaussian formula becomes the identity matrix. We're then left with (X - mean) * (X - mean),
|
|
1048
|
-
// and since 'mean' is zero, we have X * X, which is the same as A:
|
|
1049
|
-
float opacity = exp( -0.5*A) * vColor.a;
|
|
1050
|
-
if(opacity < 1.0 / 255.0)
|
|
1051
|
-
discard;
|
|
1052
|
-
|
|
1053
|
-
// uint a = uint(255);
|
|
1054
|
-
// 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);
|
|
1055
|
-
// gl_FragColor = vec4(c, 1.0);
|
|
1056
|
-
gl_FragColor = vec4(color, opacity);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1060
|
-
`;
|
|
1061
|
-
|
|
1062
|
-
return fragmentShaderSource;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
}
|