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