程序化高斯泼溅
GSplatContainer 可通过程序创建数据而非从文件加载,适合动态可视化、程序化效果和其他数据转换。
备注
此功能基于渲染架构。
概览
GSplatContainer 是由 CPU 填充的容器,搭配定义纹理流与属性读取方式的 GSplatFormat。GSplatProcessor 也可从 GPU 写入。
主要特点:
- 固定大小:创建时设容量,不能更改。
- CPU 填充:JavaScript 写入纹理数据。
- GPU 填充:可通过 GSplatProcessor 写入。
- 格式驱动:GSplatFormat 定义布局和代码。
数据格式
文件资源自动确定格式,程序化数据则需显式创建。引擎为常见用途提供内置格式。
内置格式
默认格式
GSplatFormat.createDefaultFormat(device) 包含完整数据:
| 数据流 | 格式 | 内容 |
|---|---|---|
dataColor | RGBA16F | 半浮点 r、g、b、a |
dataCenter | RGBA32F | 浮点 x、y、z 位置 |
dataScale | RGBA16F | 半浮点 x、y、z 缩放 |
dataRotation | RGBA16F | 半浮点 x、y、z、w 四元数 |
const format = pc.GSplatFormat.createDefaultFormat(device);
简化格式
GSplatFormat.createSimpleFormat(device) 创建无旋转、统一缩放的轻量格式:
| 数据流 | 格式 | 内容 |
|---|---|---|
dataCenter | RGBA32F | xyz 位置,w 保存统一尺寸 |
dataColor | RGBA16F | 半浮点 r、g、b、a |
const format = pc.GSplatFormat.createSimpleFormat(device);
适合 GsplatMesh、GsplatImage、GsplatLines 等辅助脚本。
自定义格式
高级用途可自行定义数据流和代码。
构造函数
const format = new pc.GSplatFormat(device, streams, options);
参数:
- device:图形设备。
- streams:
{ name: string, format: number }描述符数组。 - options.readGLSL:WebGL 所需 GLSL。
- options.readWGSL:WebGPU 所需 WGSL。
必需着色器函数
读取代码必须定义:
| 函数 | 返回类型 | 说明 |
|---|---|---|
getCenter() | vec3 | 局部空间位置 |
getColor() | vec4 | r、g、b、a 颜色 |
getScale() | vec3 | x、y、z 缩放 |
getRotation() | vec4 | x、y、z、w 四元数 |
getCenter() 始终最先执行,可在其中采样纹理、保存共享全局值,供其他函数使用。
加载函数
每条流生成 load{StreamName}(),首字母大写。例如 data 生成 loadData()。
示例:带染色 uniform 的自定义格式
用单张 RGBA8 纹理和自定义 uniform 创建逐实例颜色渐变:
const format = new pc.GSplatFormat(device, [
{ name: 'data', format: pc.PIXELFORMAT_RGBA8 }
], {
readGLSL: `
uniform vec3 uTint;
uniform vec3 uTint2;
vec4 splatData; // Global variable to avoid sampling texture twice
vec3 getCenter() {
// getCenter always executes first - sample texture here
splatData = loadData();
return (splatData.rgb - 0.5) * 5.0;
}
vec4 getColor() {
vec3 tint = mix(uTint2, uTint, splatData.a);
return vec4(tint, 1.0);
}
vec3 getScale() { return vec3(0.15); }
vec4 getRotation() { return vec4(0.0, 0.0, 0.0, 1.0); }
`,
readWGSL: `
uniform uTint: vec3f;
uniform uTint2: vec3f;
var<private> splatData: vec4f; // Global variable to avoid sampling texture twice
fn getCenter() -> vec3f {
// getCenter always executes first - sample texture here
splatData = loadData();
return (splatData.rgb - 0.5) * 5.0;
}
fn getColor() -> vec4f {
let tint = mix(uniform.uTint2, uniform.uTint, splatData.a);
return vec4f(tint, 1.0);
}
fn getScale() -> vec3f { return vec3f(0.15); }
fn getRotation() -> vec4f { return vec4f(0.0, 0.0, 0.0, 1.0); }
`
});
基本用法
1. 创建格式
使用内置或自定义格式:
// Simple format for uniform-scale splats (no rotation)
const format = pc.GSplatFormat.createSimpleFormat(device);
// Or default format with full splat data
const format = pc.GSplatFormat.createDefaultFormat(device);
2. 创建容器
const maxSplats = 1000;
const container = new pc.GSplatContainer(device, maxSplats, format);
3. 填充纹理
锁定、填数据、解锁:
// Get textures by stream name
const centerTex = container.getTexture('dataCenter');
const colorTex = container.getTexture('dataColor');
// Lock for writing
const centerData = centerTex.lock(); // Float32Array for RGBA32F
const colorData = colorTex.lock(); // Uint16Array for RGBA16F
// Fill data for each splat
for (let i = 0; i < numSplats; i++) {
// Center position (x, y, z) + size in w
centerData[i * 4 + 0] = x;
centerData[i * 4 + 1] = y;
centerData[i * 4 + 2] = z;
centerData[i * 4 + 3] = size;
// Color as half-floats (use FloatPacking helper)
colorData[i * 4 + 0] = pc.FloatPacking.float2Half(r);
colorData[i * 4 + 1] = pc.FloatPacking.float2Half(g);
colorData[i * 4 + 2] = pc.FloatPacking.float2Half(b);
colorData[i * 4 + 3] = pc.FloatPacking.float2Half(a);
}
// Unlock when done
centerTex.unlock();
colorTex.unlock();
4. 设置中心与包围盒
中心用于排序,包围盒用于剔除:
// Fill centers array (xyz per splat, Float32Array)
const centers = container.centers;
for (let i = 0; i < numSplats; i++) {
centers[i * 3 + 0] = x;
centers[i * 3 + 1] = y;
centers[i * 3 + 2] = z;
}
// Set bounding box
container.aabb = new pc.BoundingBox(
new pc.Vec3(centerX, centerY, centerZ),
new pc.Vec3(halfExtentX, halfExtentY, halfExtentZ)
);
5. 更新并加入场景
// Update container with number of splats to render (can be less than maxSplats)
container.update(numSplats);
// Add to scene via gsplat component
entity.addComponent('gsplat', {
resource: container
});
numSplats 可小于等于 maxSplats,只渲染容器一部分。
更新数据
创建后更新:
- 锁定纹理,修改,解锁。
- 中心变化时更新 container.centers。
- 调用
container.update(numSplats, centersUpdated)。
// Update color texture
const colorTex = container.getTexture('dataColor');
const colorData = colorTex.lock();
// ... modify colorData ...
colorTex.unlock();
// Update count (centersUpdated=false if centers didn't change)
container.update(numSplats, false);
辅助脚本
以下现成脚本封装容器,覆盖常见用途:
| 脚本 | 说明 |
|---|---|
| GsplatMesh | 转换网格 |
| GsplatImage | 每可见像素一个单元 |
| GsplatLines | 线、箭头、包围盒 |
| GsplatText | 渲染文本 |
脚本负责容器创建和数据填充。
实时示例
Procedural Instanced 演示自定义容器、格式及逐实例 uniform。
程序化实例示例