@d5techs/3dgs-lib 1.4.22 → 1.4.23
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/dist/3dgs-lib.cjs +261 -84
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +261 -84
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/gs/SOGEncoder.d.ts +3 -6
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/3dgs-lib.cjs
CHANGED
|
@@ -6303,91 +6303,231 @@ function findNearest(sortedCB, value) {
|
|
|
6303
6303
|
}
|
|
6304
6304
|
return lo;
|
|
6305
6305
|
}
|
|
6306
|
-
function
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6306
|
+
function getGpuClusterShader(dim) {
|
|
6307
|
+
return (
|
|
6308
|
+
/* wgsl */
|
|
6309
|
+
`
|
|
6310
|
+
struct Uniforms {
|
|
6311
|
+
numPoints: u32,
|
|
6312
|
+
numCentroids: u32
|
|
6313
|
+
};
|
|
6314
|
+
|
|
6315
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
6316
|
+
@group(0) @binding(1) var<storage, read> points: array<f32>;
|
|
6317
|
+
@group(0) @binding(2) var<storage, read> centroids: array<f32>;
|
|
6318
|
+
@group(0) @binding(3) var<storage, read_write> results: array<u32>;
|
|
6319
|
+
|
|
6320
|
+
const DIM = ${dim}u;
|
|
6321
|
+
const CHUNK_SIZE = 128u;
|
|
6322
|
+
var<workgroup> sharedChunk: array<f32, ${dim * 128}>;
|
|
6323
|
+
|
|
6324
|
+
fn calcDistSqr(point: array<f32, ${dim}>, cIdx: u32) -> f32 {
|
|
6325
|
+
var result = 0.0;
|
|
6326
|
+
var ci = cIdx * DIM;
|
|
6327
|
+
for (var i = 0u; i < DIM; i++) {
|
|
6328
|
+
let v = point[i] - sharedChunk[ci + i];
|
|
6329
|
+
result += v * v;
|
|
6330
|
+
}
|
|
6331
|
+
return result;
|
|
6332
|
+
}
|
|
6333
|
+
|
|
6334
|
+
@compute @workgroup_size(64)
|
|
6335
|
+
fn main(
|
|
6336
|
+
@builtin(local_invocation_index) localId: u32,
|
|
6337
|
+
@builtin(global_invocation_id) globalId: vec3u,
|
|
6338
|
+
@builtin(num_workgroups) numWgs: vec3u
|
|
6339
|
+
) {
|
|
6340
|
+
let pointIdx = globalId.x + globalId.y * numWgs.x * 64u;
|
|
6341
|
+
|
|
6342
|
+
var point: array<f32, ${dim}>;
|
|
6343
|
+
if (pointIdx < uniforms.numPoints) {
|
|
6344
|
+
for (var i = 0u; i < DIM; i++) {
|
|
6345
|
+
point[i] = points[pointIdx * DIM + i];
|
|
6314
6346
|
}
|
|
6315
|
-
while (centroids2.length < k) centroids2.push(new Float32Array(dim));
|
|
6316
|
-
return { centroids: centroids2, labels };
|
|
6317
6347
|
}
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6348
|
+
|
|
6349
|
+
var mind = 1e20;
|
|
6350
|
+
var mini = 0u;
|
|
6351
|
+
|
|
6352
|
+
let numChunks = (uniforms.numCentroids + CHUNK_SIZE - 1u) / CHUNK_SIZE;
|
|
6353
|
+
for (var chunk = 0u; chunk < numChunks; chunk++) {
|
|
6354
|
+
|
|
6355
|
+
let rowsPerThread = CHUNK_SIZE / 64u;
|
|
6356
|
+
let dstRow = localId * rowsPerThread;
|
|
6357
|
+
let srcRow = min(uniforms.numCentroids, chunk * CHUNK_SIZE + dstRow);
|
|
6358
|
+
let numRows = min(uniforms.numCentroids, srcRow + rowsPerThread) - srcRow;
|
|
6359
|
+
|
|
6360
|
+
var dst = dstRow * DIM;
|
|
6361
|
+
var src = srcRow * DIM;
|
|
6362
|
+
for (var c = 0u; c < numRows * DIM; c++) {
|
|
6363
|
+
sharedChunk[dst + c] = centroids[src + c];
|
|
6333
6364
|
}
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6365
|
+
|
|
6366
|
+
workgroupBarrier();
|
|
6367
|
+
|
|
6368
|
+
if (pointIdx < uniforms.numPoints) {
|
|
6369
|
+
let thisChunkSize = min(CHUNK_SIZE, uniforms.numCentroids - chunk * CHUNK_SIZE);
|
|
6370
|
+
for (var c = 0u; c < thisChunkSize; c++) {
|
|
6371
|
+
let d = calcDistSqr(point, c);
|
|
6372
|
+
if (d < mind) {
|
|
6373
|
+
mind = d;
|
|
6374
|
+
mini = chunk * CHUNK_SIZE + c;
|
|
6375
|
+
}
|
|
6341
6376
|
}
|
|
6342
6377
|
}
|
|
6343
|
-
|
|
6378
|
+
|
|
6379
|
+
workgroupBarrier();
|
|
6344
6380
|
}
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6381
|
+
|
|
6382
|
+
if (pointIdx < uniforms.numPoints) {
|
|
6383
|
+
results[pointIdx] = mini;
|
|
6384
|
+
}
|
|
6385
|
+
}`
|
|
6386
|
+
);
|
|
6387
|
+
}
|
|
6388
|
+
let _cachedDevice = null;
|
|
6389
|
+
let _deviceFailed = false;
|
|
6390
|
+
async function getGpuDevice() {
|
|
6391
|
+
if (_cachedDevice) return _cachedDevice;
|
|
6392
|
+
if (_deviceFailed) return null;
|
|
6393
|
+
try {
|
|
6394
|
+
if (typeof navigator === "undefined" || !navigator.gpu) {
|
|
6395
|
+
_deviceFailed = true;
|
|
6396
|
+
return null;
|
|
6361
6397
|
}
|
|
6362
|
-
const
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
const l = labels[i];
|
|
6367
|
-
counts[l]++;
|
|
6368
|
-
for (let d = 0; d < dim; d++) sums[l][d] += vectors[i][d];
|
|
6398
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
6399
|
+
if (!adapter) {
|
|
6400
|
+
_deviceFailed = true;
|
|
6401
|
+
return null;
|
|
6369
6402
|
}
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
const v = sums[c][d] / counts[c];
|
|
6375
|
-
if (v !== centroids[c][d]) {
|
|
6376
|
-
centroids[c][d] = v;
|
|
6377
|
-
changed = true;
|
|
6378
|
-
}
|
|
6379
|
-
}
|
|
6403
|
+
_cachedDevice = await adapter.requestDevice({
|
|
6404
|
+
requiredLimits: {
|
|
6405
|
+
maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
|
|
6406
|
+
maxBufferSize: adapter.limits.maxBufferSize
|
|
6380
6407
|
}
|
|
6408
|
+
});
|
|
6409
|
+
_cachedDevice.lost.then(() => {
|
|
6410
|
+
_cachedDevice = null;
|
|
6411
|
+
});
|
|
6412
|
+
return _cachedDevice;
|
|
6413
|
+
} catch {
|
|
6414
|
+
_deviceFailed = true;
|
|
6415
|
+
return null;
|
|
6416
|
+
}
|
|
6417
|
+
}
|
|
6418
|
+
async function gpuAssignLabels(device, pointsFlat, centroidsFlat, numPoints, numCentroids, dim) {
|
|
6419
|
+
const WORKGROUP_SIZE2 = 64;
|
|
6420
|
+
const BATCH_SIZE = 1024 * WORKGROUP_SIZE2;
|
|
6421
|
+
const numBatches = Math.ceil(numPoints / BATCH_SIZE);
|
|
6422
|
+
const shaderModule = device.createShaderModule({ code: getGpuClusterShader(dim) });
|
|
6423
|
+
const bindGroupLayout = device.createBindGroupLayout({
|
|
6424
|
+
entries: [
|
|
6425
|
+
{ binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: "uniform" } },
|
|
6426
|
+
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
6427
|
+
{ binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
6428
|
+
{ binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: "storage" } }
|
|
6429
|
+
]
|
|
6430
|
+
});
|
|
6431
|
+
const pipeline = device.createComputePipeline({
|
|
6432
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] }),
|
|
6433
|
+
compute: { module: shaderModule, entryPoint: "main" }
|
|
6434
|
+
});
|
|
6435
|
+
const uniformBuf = device.createBuffer({ size: 8, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
6436
|
+
const centroidsBuf = device.createBuffer({ size: centroidsFlat.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
|
6437
|
+
device.queue.writeBuffer(centroidsBuf, 0, centroidsFlat.buffer);
|
|
6438
|
+
const pointsBufSize = BATCH_SIZE * dim * 4;
|
|
6439
|
+
const pointsBuf = device.createBuffer({ size: pointsBufSize, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
|
6440
|
+
const resultsBufSize = BATCH_SIZE * 4;
|
|
6441
|
+
const resultsBuf = device.createBuffer({ size: resultsBufSize, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });
|
|
6442
|
+
const readbackBuf = device.createBuffer({ size: resultsBufSize, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
|
|
6443
|
+
const labels = new Uint32Array(numPoints);
|
|
6444
|
+
const bindGroup = device.createBindGroup({
|
|
6445
|
+
layout: bindGroupLayout,
|
|
6446
|
+
entries: [
|
|
6447
|
+
{ binding: 0, resource: { buffer: uniformBuf } },
|
|
6448
|
+
{ binding: 1, resource: { buffer: pointsBuf } },
|
|
6449
|
+
{ binding: 2, resource: { buffer: centroidsBuf } },
|
|
6450
|
+
{ binding: 3, resource: { buffer: resultsBuf } }
|
|
6451
|
+
]
|
|
6452
|
+
});
|
|
6453
|
+
for (let batch = 0; batch < numBatches; batch++) {
|
|
6454
|
+
const offset = batch * BATCH_SIZE;
|
|
6455
|
+
const currentBatchSize = Math.min(numPoints - offset, BATCH_SIZE);
|
|
6456
|
+
const groups = Math.ceil(currentBatchSize / WORKGROUP_SIZE2);
|
|
6457
|
+
device.queue.writeBuffer(uniformBuf, 0, new Uint32Array([currentBatchSize, numCentroids]));
|
|
6458
|
+
device.queue.writeBuffer(pointsBuf, 0, pointsFlat.buffer, offset * dim * 4, currentBatchSize * dim * 4);
|
|
6459
|
+
const encoder = device.createCommandEncoder();
|
|
6460
|
+
const pass = encoder.beginComputePass();
|
|
6461
|
+
pass.setPipeline(pipeline);
|
|
6462
|
+
pass.setBindGroup(0, bindGroup);
|
|
6463
|
+
pass.dispatchWorkgroups(groups);
|
|
6464
|
+
pass.end();
|
|
6465
|
+
encoder.copyBufferToBuffer(resultsBuf, 0, readbackBuf, 0, currentBatchSize * 4);
|
|
6466
|
+
device.queue.submit([encoder.finish()]);
|
|
6467
|
+
await readbackBuf.mapAsync(GPUMapMode.READ);
|
|
6468
|
+
const mapped = new Uint32Array(readbackBuf.getMappedRange(0, currentBatchSize * 4));
|
|
6469
|
+
labels.set(mapped, offset);
|
|
6470
|
+
readbackBuf.unmap();
|
|
6471
|
+
}
|
|
6472
|
+
uniformBuf.destroy();
|
|
6473
|
+
centroidsBuf.destroy();
|
|
6474
|
+
pointsBuf.destroy();
|
|
6475
|
+
resultsBuf.destroy();
|
|
6476
|
+
readbackBuf.destroy();
|
|
6477
|
+
return labels;
|
|
6478
|
+
}
|
|
6479
|
+
async function kmeansGpu(pointsFlat, n, dim, k, iterations, onProgress) {
|
|
6480
|
+
const labels = new Uint32Array(n);
|
|
6481
|
+
if (n <= k) {
|
|
6482
|
+
const centroids2 = new Float32Array(k * dim);
|
|
6483
|
+
for (let i = 0; i < n; i++) {
|
|
6484
|
+
centroids2.set(pointsFlat.subarray(i * dim, i * dim + dim), i * dim);
|
|
6485
|
+
labels[i] = i;
|
|
6381
6486
|
}
|
|
6382
|
-
|
|
6487
|
+
return { centroids: centroids2, labels };
|
|
6383
6488
|
}
|
|
6489
|
+
const centroids = new Float32Array(k * dim);
|
|
6490
|
+
const indices = pickRandomIndices(n, k);
|
|
6491
|
+
for (let i = 0; i < k; i++) {
|
|
6492
|
+
centroids.set(pointsFlat.subarray(indices[i] * dim, indices[i] * dim + dim), i * dim);
|
|
6493
|
+
}
|
|
6494
|
+
const device = await getGpuDevice();
|
|
6495
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
6496
|
+
onProgress == null ? void 0 : onProgress(iter, iterations);
|
|
6497
|
+
if (device) {
|
|
6498
|
+
const newLabels = await gpuAssignLabels(device, pointsFlat, centroids, n, k, dim);
|
|
6499
|
+
labels.set(newLabels);
|
|
6500
|
+
} else {
|
|
6501
|
+
assignLabelsCpu(pointsFlat, centroids, labels, n, k, dim);
|
|
6502
|
+
}
|
|
6503
|
+
updateCentroids(pointsFlat, centroids, labels, n, k, dim);
|
|
6504
|
+
}
|
|
6505
|
+
onProgress == null ? void 0 : onProgress(iterations, iterations);
|
|
6506
|
+
if (device) {
|
|
6507
|
+
const finalLabels = await gpuAssignLabels(device, pointsFlat, centroids, n, k, dim);
|
|
6508
|
+
labels.set(finalLabels);
|
|
6509
|
+
} else {
|
|
6510
|
+
assignLabelsCpu(pointsFlat, centroids, labels, n, k, dim);
|
|
6511
|
+
}
|
|
6512
|
+
return { centroids, labels };
|
|
6513
|
+
}
|
|
6514
|
+
function pickRandomIndices(n, m) {
|
|
6515
|
+
const chosen = /* @__PURE__ */ new Set();
|
|
6516
|
+
for (let j = n - m; j < n; j++) {
|
|
6517
|
+
const t = Math.floor(Math.random() * (j + 1));
|
|
6518
|
+
chosen.add(chosen.has(t) ? j : t);
|
|
6519
|
+
}
|
|
6520
|
+
return [...chosen];
|
|
6521
|
+
}
|
|
6522
|
+
function assignLabelsCpu(points, centroids, labels, n, k, dim) {
|
|
6384
6523
|
for (let i = 0; i < n; i++) {
|
|
6385
|
-
|
|
6386
|
-
let bestD = Infinity;
|
|
6524
|
+
const pOff = i * dim;
|
|
6525
|
+
let bestK = 0, bestD = Infinity;
|
|
6387
6526
|
for (let c = 0; c < k; c++) {
|
|
6527
|
+
const cOff = c * dim;
|
|
6388
6528
|
let d2 = 0;
|
|
6389
6529
|
for (let d = 0; d < dim; d++) {
|
|
6390
|
-
const diff =
|
|
6530
|
+
const diff = points[pOff + d] - centroids[cOff + d];
|
|
6391
6531
|
d2 += diff * diff;
|
|
6392
6532
|
}
|
|
6393
6533
|
if (d2 < bestD) {
|
|
@@ -6397,7 +6537,25 @@ function kmeansVectors(vectors, dim, k, iterations) {
|
|
|
6397
6537
|
}
|
|
6398
6538
|
labels[i] = bestK;
|
|
6399
6539
|
}
|
|
6400
|
-
|
|
6540
|
+
}
|
|
6541
|
+
function updateCentroids(points, centroids, labels, n, k, dim) {
|
|
6542
|
+
const sums = new Float64Array(k * dim);
|
|
6543
|
+
const counts = new Uint32Array(k);
|
|
6544
|
+
for (let i = 0; i < n; i++) {
|
|
6545
|
+
const l = labels[i];
|
|
6546
|
+
counts[l]++;
|
|
6547
|
+
const pOff = i * dim, sOff = l * dim;
|
|
6548
|
+
for (let d = 0; d < dim; d++) sums[sOff + d] += points[pOff + d];
|
|
6549
|
+
}
|
|
6550
|
+
for (let c = 0; c < k; c++) {
|
|
6551
|
+
if (counts[c] > 0) {
|
|
6552
|
+
const off = c * dim;
|
|
6553
|
+
for (let d = 0; d < dim; d++) centroids[off + d] = sums[off + d] / counts[c];
|
|
6554
|
+
} else {
|
|
6555
|
+
const idx = Math.floor(Math.random() * n);
|
|
6556
|
+
centroids.set(points.subarray(idx * dim, idx * dim + dim), c * dim);
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6401
6559
|
}
|
|
6402
6560
|
function mortonSort(positions, count) {
|
|
6403
6561
|
let minx = Infinity, miny = Infinity, minz = Infinity;
|
|
@@ -6468,11 +6626,12 @@ async function encodeWebP(width, height, pixelData) {
|
|
|
6468
6626
|
}
|
|
6469
6627
|
return new Uint8Array(await blob.arrayBuffer());
|
|
6470
6628
|
}
|
|
6471
|
-
async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
6629
|
+
async function serializeSOG(data, coordinateSystem = "blender", options = {}, onProgress) {
|
|
6472
6630
|
const { maxSHBands = 3, iterations = 10, paletteSize = 1024 } = options;
|
|
6473
6631
|
const { count, colors, opacities } = data;
|
|
6474
6632
|
let { positions, scales, rotations } = data;
|
|
6475
6633
|
let shCoeffs = data.shCoeffs ? new Float32Array(data.shCoeffs) : void 0;
|
|
6634
|
+
onProgress == null ? void 0 : onProgress("坐标系变换", 0);
|
|
6476
6635
|
if (coordinateSystem === "blender") {
|
|
6477
6636
|
positions = new Float32Array(positions);
|
|
6478
6637
|
scales = new Float32Array(scales);
|
|
@@ -6495,6 +6654,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6495
6654
|
}
|
|
6496
6655
|
}
|
|
6497
6656
|
}
|
|
6657
|
+
onProgress == null ? void 0 : onProgress("Morton 排序", 0.05);
|
|
6498
6658
|
const sortOrder = mortonSort(positions, count);
|
|
6499
6659
|
const sPos = new Float32Array(count * 3);
|
|
6500
6660
|
const sSca = new Float32Array(count * 3);
|
|
@@ -6527,6 +6687,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6527
6687
|
}
|
|
6528
6688
|
const { width, height } = calcImageDimensions(count);
|
|
6529
6689
|
const totalPixels = width * height;
|
|
6690
|
+
onProgress == null ? void 0 : onProgress("编码位置", 0.1);
|
|
6530
6691
|
const logPos = new Float32Array(count * 3);
|
|
6531
6692
|
for (let i = 0; i < count * 3; i++) logPos[i] = symLog(sPos[i]);
|
|
6532
6693
|
const mins = [Infinity, Infinity, Infinity];
|
|
@@ -6554,6 +6715,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6554
6715
|
meansLData[off + 3] = 255;
|
|
6555
6716
|
meansUData[off + 3] = 255;
|
|
6556
6717
|
}
|
|
6718
|
+
onProgress == null ? void 0 : onProgress("编码缩放", 0.2);
|
|
6557
6719
|
const logScales = new Float32Array(count * 3);
|
|
6558
6720
|
for (let i = 0; i < count * 3; i++) logScales[i] = Math.log(Math.max(1e-8, sSca[i]));
|
|
6559
6721
|
const scaleCodebook = buildCodebook(logScales, 256);
|
|
@@ -6566,6 +6728,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6566
6728
|
scalesData[off + 2] = findNearest(scaleCodebook, logScales[i * 3 + 2]);
|
|
6567
6729
|
scalesData[off + 3] = 255;
|
|
6568
6730
|
}
|
|
6731
|
+
onProgress == null ? void 0 : onProgress("编码四元数", 0.3);
|
|
6569
6732
|
const quatsData = new Uint8ClampedArray(totalPixels * 4);
|
|
6570
6733
|
quatsData.fill(255);
|
|
6571
6734
|
const SQRT2 = Math.SQRT2;
|
|
@@ -6618,6 +6781,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6618
6781
|
quatsData[off + 2] = Math.round(Math.max(0, Math.min(255, (r2 / SQRT2 + 0.5) * 255)));
|
|
6619
6782
|
quatsData[off + 3] = largest + 252;
|
|
6620
6783
|
}
|
|
6784
|
+
onProgress == null ? void 0 : onProgress("编码颜色", 0.4);
|
|
6621
6785
|
const dcValues = new Float32Array(count * 3);
|
|
6622
6786
|
for (let i = 0; i < count; i++) {
|
|
6623
6787
|
dcValues[i * 3] = (sCol[i * 3] - 0.5) / SH_C0;
|
|
@@ -6646,18 +6810,28 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6646
6810
|
encodeWebP(width, height, sh0Data)
|
|
6647
6811
|
];
|
|
6648
6812
|
if (actualBands > 0 && sSH) {
|
|
6649
|
-
|
|
6813
|
+
onProgress == null ? void 0 : onProgress("压缩球谐系数 (GPU)", 0.5);
|
|
6814
|
+
const pointsFlat = new Float32Array(count * shDim);
|
|
6650
6815
|
for (let i = 0; i < count; i++) {
|
|
6651
|
-
const v = new Float32Array(shDim);
|
|
6652
6816
|
const b = i * 45;
|
|
6653
|
-
for (let j = 0; j < shDim; j++)
|
|
6654
|
-
vectors[i] = v;
|
|
6817
|
+
for (let j = 0; j < shDim; j++) pointsFlat[i * shDim + j] = sSH[b + j];
|
|
6655
6818
|
}
|
|
6656
6819
|
const effectivePaletteSize = Math.min(paletteSize, count);
|
|
6657
|
-
const { centroids, labels } =
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6820
|
+
const { centroids, labels } = await kmeansGpu(
|
|
6821
|
+
pointsFlat,
|
|
6822
|
+
count,
|
|
6823
|
+
shDim,
|
|
6824
|
+
effectivePaletteSize,
|
|
6825
|
+
iterations,
|
|
6826
|
+
(iter, total) => {
|
|
6827
|
+
const p = 0.5 + 0.35 * (iter / total);
|
|
6828
|
+
onProgress == null ? void 0 : onProgress(`球谐聚类 ${iter}/${total}`, p);
|
|
6829
|
+
}
|
|
6830
|
+
);
|
|
6831
|
+
onProgress == null ? void 0 : onProgress("量化球谐 codebook", 0.88);
|
|
6832
|
+
const allCoeffValues = new Float32Array(effectivePaletteSize * shDim);
|
|
6833
|
+
for (let c = 0; c < effectivePaletteSize; c++) {
|
|
6834
|
+
for (let d = 0; d < shDim; d++) allCoeffValues[c * shDim + d] = centroids[c * shDim + d];
|
|
6661
6835
|
}
|
|
6662
6836
|
const shCodebook = buildCodebook(allCoeffValues, 256);
|
|
6663
6837
|
const centW = 64 * numCoeffs;
|
|
@@ -6665,14 +6839,14 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6665
6839
|
const centData = new Uint8ClampedArray(centW * centH * 4);
|
|
6666
6840
|
centData.fill(255);
|
|
6667
6841
|
for (let n = 0; n < effectivePaletteSize; n++) {
|
|
6668
|
-
const entry = centroids[n];
|
|
6669
6842
|
for (let c = 0; c < numCoeffs; c++) {
|
|
6670
6843
|
const u = n % 64 * numCoeffs + c;
|
|
6671
6844
|
const v = Math.floor(n / 64);
|
|
6672
6845
|
const off = (v * centW + u) * 4;
|
|
6673
|
-
|
|
6674
|
-
centData[off
|
|
6675
|
-
centData[off +
|
|
6846
|
+
const cOff = n * shDim;
|
|
6847
|
+
centData[off] = findNearest(shCodebook, centroids[cOff + c * 3]);
|
|
6848
|
+
centData[off + 1] = findNearest(shCodebook, centroids[cOff + c * 3 + 1]);
|
|
6849
|
+
centData[off + 2] = findNearest(shCodebook, centroids[cOff + c * 3 + 2]);
|
|
6676
6850
|
centData[off + 3] = 255;
|
|
6677
6851
|
}
|
|
6678
6852
|
}
|
|
@@ -6696,6 +6870,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6696
6870
|
files: ["sh_centroids.webp", "sh_labels.webp"]
|
|
6697
6871
|
};
|
|
6698
6872
|
}
|
|
6873
|
+
onProgress == null ? void 0 : onProgress("编码 WebP 图像", 0.92);
|
|
6699
6874
|
const webps = await Promise.all(webpPromises);
|
|
6700
6875
|
const [meansLWebP, meansUWebP, scalesWebP, quatsWebP, sh0WebP] = webps;
|
|
6701
6876
|
const meta = {
|
|
@@ -6721,7 +6896,9 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
|
6721
6896
|
zipEntries["sh_centroids.webp"] = webps[5];
|
|
6722
6897
|
zipEntries["sh_labels.webp"] = webps[6];
|
|
6723
6898
|
}
|
|
6899
|
+
onProgress == null ? void 0 : onProgress("打包 ZIP", 0.98);
|
|
6724
6900
|
const zipData = zipSync(zipEntries, { level: 0 });
|
|
6901
|
+
onProgress == null ? void 0 : onProgress("完成", 1);
|
|
6725
6902
|
return zipData.buffer;
|
|
6726
6903
|
}
|
|
6727
6904
|
const WORKGROUP_SIZE$1 = 256;
|