跳到主要内容

SOG 格式(版本 2)

**SOG(Spatially Ordered Gaussians,空间有序高斯)**是紧凑的 3D 高斯泼溅容器。通过有损量化获得高压缩率,通常比等价 PLY 小约 15–20 倍。

可用 SplatTransform 创建,并在 PlayCanvas 查看器 预览。

本页为 SOG v2 规范。当前版本为 v3;所有 PlayCanvas 工具仍支持读取 v2,也可写出供早期查看器使用。

1. 文件集合​

数据集由一组图像和元数据文件组成:

文件用途通道(8 位)
meta.json场景元数据与文件名—
means_l.webp位置低 8 位R,G,B
means_u.webp位置高 8 位R,G,B
scales.webp通过码本表示的各轴尺寸R,G,B
quats.webp朝向,压缩四元数R,G,B,A
sh0.webp基础颜色(直流分量)与不透明度R,G,B,A
shN_centroids.webp球谐调色板系数(可选)R,G,B
shN_labels.webp球谐调色板索引,可选R,G
图像格式
  • 默认应使用无损 WebP,精确保留量化值。
  • meta.json 各属性指定文件名,因此可以使用其他支持 8 位 RGBA 的格式。
  • 不要使用有损编码,它会破坏数值,造成可见或结构性伪影。

1.1 图像尺寸与索引​

除 shN_centroids 外,所有属性图像的同一像素 x、y 都属于同一高斯单元。

  • 像素按行优先排列,原点在左上。
  • 宽 W、高 H 的图像可寻址 W*H 个单元。
  • meta.count 必须满足 <= W*H,末尾多余像素忽略。

从零开始的索引公式:

  • 索引转像素: x = i % W, y = floor(i / W)
  • 像素转索引: i = x + y * W

1.2 坐标系​

SOG 与 3DGS PLY 使用同一坐标系。放入 +Y 向上的右手世界(如 PlayCanvas)时,将场景整体绕 Z 旋转 180 度,即 (x, y, z) → (−x, −y, z),与 v3 相同。

1.3 打包变体​

打包 SOG 是上述文件的 ZIP。读取器应支持两种布局:

  • 多文件目录,推荐制作期间使用。
  • 单归档,例如 scene.sog,相同文件位于归档根目录。

读取器必须解压后,像多文件版本一样通过 meta.json 解析文件。


2. meta.json​

interface Meta {
version: 2; // File format version (integer)
asset?: { // Optional tool/version metadata
generator?: string; // e.g. "splat-transform v1.2.3"
};
count: number; // Number of gaussians (<= W*H of the images)
model?: "antialiased" | "2dgs"; // Optional. How the scene was trained; omitted for ordinary gaussians

means: {
// Ranges for decoding *log-transformed* positions (see §3.1).
mins: [number, number, number]; // min of nx,ny,nz (log-domain)
maxs: [number, number, number]; // max of nx,ny,nz (log-domain)
files: ["means_l.webp", "means_u.webp"];
};

scales: {
codebook: number[]; // 256 floats (log-domain); see §3.3
files: ["scales.webp"];
};

quats: {
files: ["quats.webp"]; // §3.2
};

sh0: {
codebook: number[]; // 256 floats; DC coefficients for gamma-space color (§3.4)
files: ["sh0.webp"];
};

// Present only if higher-order SH exist:
shN?: {
count: number; // Palette size (up to 65536)
bands: number; // Number of SH bands (1..3). DC (=band 0) lives in sh0.
codebook: number[]; // 256 floats; shared for all AC coefficients (§3.5)
files: [
"shN_centroids.webp",// Palette of AC coefficients as pixels (§3.5)
"shN_labels.webp" // Per-gaussian palette index (0..count-1)
];
};
}
备注
  • scales 码本保存对数值,需 exp() 恢复线性尺寸(§3.3)。sh0 码本为伽马空间 DC 系数,shN 为伽马空间 AC 系数。
  • 图像数据必须按原始 8 位整数处理,不做伽马转换。
  • 除非另有说明,未提到的通道忽略。
  • files 数组中文件名任意,但顺序有意义。

3. 属性编码​

3.1 位置​

means_l.webp、means_u.webp,RGB,每轴 16 位。

每轴通过两张图像量化为 16 位:

// 16-bit normalized value per axis (0..65535)
const qx = (means_u.r << 8) | means_l.r;
const qy = (means_u.g << 8) | means_l.g;
const qz = (means_u.b << 8) | means_l.b;

// Dequantize into *log-domain* nx,ny,nz using per-axis ranges from meta:
const nx = lerp(meta.means.mins[0], meta.means.maxs[0], qx / 65535);
const ny = lerp(meta.means.mins[1], meta.means.maxs[1], qy / 65535);
const nz = lerp(meta.means.mins[2], meta.means.maxs[2], qz / 65535);

// Undo the symmetric log transform used at encode time:
const unlog = (n: number) => Math.sign(n) * (Math.exp(Math.abs(n)) - 1);

const p = {
x: unlog(nx),
y: unlog(ny),
z: unlog(nz),
};

3.2 朝向​

quats.webp,RGBA,26 位“最小三分量”。

四元数使用标准最小三分量编码:3×8 位分量 + 2 位模式,总计 26 位。

四元数分量始终按 w、x、y、z 排列。

  • RGB 按 w、x、y、z 顺序保存三个保留的有符号分量,均匀量化到 [-√2/2, +√2/2]。编码器应舍入到最近值,而非截断。
  • A 保存 252–255 的模式,mode = A - 252,取 0、1、2、3,表示 w、x、y、z 中绝对值最大的分量;该分量被省略并重建。
  • 令 norm = Math.SQRT2,即 √2。
// Dequantize the stored three components:
const toComp = (c: number) => (c / 255 - 0.5) * 2.0 / Math.SQRT2;

const a = toComp(quats.r);
const b = toComp(quats.g);
const c = toComp(quats.b);

const mode = quats.a - 252; // 0..3 → omitted component is w, x, y or z respectively

// Reconstruct the omitted component so that ||q|| = 1 and w.l.o.g. the omitted one is non-negative
const t = a*a + b*b + c*c;
const d = Math.sqrt(Math.max(0, 1 - t));

// Place components according to mode; q is ordered [w, x, y, z]
let q: [number, number, number, number];
switch (mode) {
case 0: q = [d, a, b, c]; break; // omitted = w
case 1: q = [a, d, b, c]; break; // omitted = x
case 2: q = [a, b, d, c]; break; // omitted = y
case 3: q = [a, b, c, d]; break; // omitted = z
default: throw new Error("Invalid quaternion mode");
}

有效性限制​

  • quats.a 必须为 252、253、254、255,其他值保留。

3.3 缩放​

scales.webp,RGB,使用码本。

各轴尺寸存为码本索引,码本是对数域数值,需指数运算解码:

const sx = Math.exp(meta.scales.codebook[scales.r]); // r,g,b are 0..255
const sy = Math.exp(meta.scales.codebook[scales.g]);
const sz = Math.exp(meta.scales.codebook[scales.b]);

尺寸含义(例如主轴标准差或完整范围)遵循源训练设置;exp 后的解码值单位为场景单位。

3.4 基础颜色与不透明度(DC)​

sh0.webp (RGBA)

sh0 保存各颜色通道的 **DC(l=0)**球谐系数和 alpha:

  • RGB 是 sh0.codebook 中 0–255 的索引。
  • A 是 [0,1] 不透明度,即 sh0.a / 255。编码器应舍入到最近量化值,而非截断。

将 DC 系数转换为伽马空间 RGB:

// SH_C0 = Y_0^0 = 1 / (2 * sqrt(pi))
const SH_C0 = 0.28209479177387814;

const r = 0.5 + meta.sh0.codebook[sh0.r] * SH_C0;
const g = 0.5 + meta.sh0.codebook[sh0.g] * SH_C0;
const b = 0.5 + meta.sh0.codebook[sh0.b] * SH_C0;
const a = sh0.a / 255;

3.5 高阶球谐(可选)​

shN_centroids.webp, shN_labels.webp

存在高阶 AC 系数时,通过调色板存储:

  • shN.count 为条目数,范围 [1,64k]。
  • shN.bands 为每条阶数,范围 [1,3]。

质心(调色板)​

  • shN_centroids.webp 是保存球谐系数调色板的 RGB 图像。
  • 每行固定 64 个条目,从左上开始按行优先打包。

纹理宽度取决于阶数:

阶数系数数纹理宽度(像素)
1364 * 3 = 192
2864 * 8 = 512
31564 * 15 = 960

计算球谐条目 n 的系数 c 对应像素位置:

const coeffs = [3, 8, 15];
const u = (n % 64) * coeffs[bands - 1] + c;
const v = Math.floor(n / 64);

标签​

  • shN_labels.webp 为每单元保存 16 位索引,范围 0 到 count-1。
const index = shN_labels.r + (shN_labels.g << 8);

解码​

质心像素的 RGB 是共享 shN.codebook 的 0–255 码本索引,分别对应球谐系数三个颜色通道。

解码单个单元的完整 AC 球谐系数:

const coeffs = [3, 8, 15];
const shCoeffs = coeffs[meta.shN.bands - 1]; // coefficients per color channel
const codebook = meta.shN.codebook; // 256 shared float entries

// 1. Look up this gaussian's palette entry
const label = shN_labels.r + (shN_labels.g << 8);

// 2. For each coefficient, read RGB from the centroids pixel and map through the codebook
for (let c = 0; c < shCoeffs; c++) {
const u = (label % 64) * shCoeffs + c;
const v = Math.floor(label / 64);

// pixel (u, v) in shN_centroids.webp
const r = centroidsPixel(u, v).r;
const g = centroidsPixel(u, v).g;
const b = centroidsPixel(u, v).b;

sh_channel0[c] = codebook[r]; // SH AC coefficient for color channel 0
sh_channel1[c] = codebook[g]; // SH AC coefficient for color channel 1
sh_channel2[c] = codebook[b]; // SH AC coefficient for color channel 2
}

一至三阶的基函数、系数顺序和求值与 v3 相同。


4. meta.json 示例​

{
"version": 2,
"asset": { "generator": "splat-transform v1.2.3" },
"count": 187543,
"model": "antialiased",
"means": {
"mins": [-2.10, -1.75, -2.40],
"maxs": [ 2.05, 2.25, 1.90],
"files": ["means_l.webp", "means_u.webp"]
},
"scales": {
"codebook": [/* 256 floats */],
"files": ["scales.webp"]
},
"quats": { "files": ["quats.webp"] },
"sh0": {
"codebook": [/* 256 floats */],
"files": ["sh0.webp"]
},
"shN": {
"count": 65536,
"bands": 3,
"codebook": [/* 256 floats */],
"files": ["shN_centroids.webp", "shN_labels.webp"]
}
}

5. 版本与兼容​

  • 读取器必须检查 version。本页是 v2,当前规范见 v3。
  • 未来可能增加可选属性,读取器应忽略未知字段。