Varying 数据流
Varying 数据流从顶点阶段传递自定义逐单元值到片元阶段。值在 gsplatModifyVS 每单元计算一次,由 gsplatModifyPS 中每个片元读取。
典型用途是分类:先对单元判断一次,让逐像素阶段只在必要位置承担开销。
查看实时示例:动画盒裁剪,只有与盒表面相交的单元逐像素测试。
查看实时示例
添加数据流
通过 app.scene.gsplat.varyings 管理:
app.scene.gsplat.varyings.add([
{ name: 'clipState', type: pc.TYPE_UINT32, components: 1 }
]);
// later, to remove
app.scene.gsplat.varyings.remove(['clipState']);
支持 TYPE_FLOAT32、TYPE_INT32、TYPE_UINT32,每项 1–4 分量。
每条流生成两个供代码块使用的函数:
| 函数 | 使用位置 | 用途 |
|---|---|---|
set<Name>(value) | gsplatModifyVS | 写逐单元值,每单元一次 |
get<Name>() | gsplatModifyPS | 为当前片元读取值 |
增删数据流会重建着色器,应启动时配置,避免运行时切换。
示例
示例按动画世界空间盒分类:完全在盒内的整体裁掉;完全在外的设置标志,片元跳过工作;只有与表面相交的执行逐像素测试。
1. 顶点代码块写入逐单元值:
- GLSL
- WGSL
uniform vec3 uClipCenter;
uniform vec3 uClipHalf;
void modifySplatCenter(inout vec3 center) {
}
void modifySplatRotationScale(vec3 originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale) {
// signed distance of the splat center from the clipping box surface (negative inside)
vec3 d = abs(modifiedCenter - uClipCenter) - uClipHalf;
float sdf = length(max(d, vec3(0.0))) + min(max(d.x, max(d.y, d.z)), 0.0);
// conservative splat radius
float radius = 2.0 * gsplatGetSizeFromScale(scale);
if (sdf < -radius) {
// fully inside the box - clip the whole splat
scale = vec3(0.0);
setClipState(1u);
} else if (sdf > radius) {
// fully outside the box - no per-pixel clipping needed
setClipState(1u);
} else {
// intersects the box surface - clip per pixel in the fragment shader
setClipState(0u);
}
}
void modifySplatColor(vec3 center, inout vec4 color) {
}
uniform uClipCenter: vec3f;
uniform uClipHalf: vec3f;
fn modifySplatCenter(center: ptr<function, vec3f>) {
}
fn modifySplatRotationScale(originalCenter: vec3f, modifiedCenter: vec3f, rotation: ptr<function, vec4f>, scale: ptr<function, vec3f>) {
// signed distance of the splat center from the clipping box surface (negative inside)
let d = abs(modifiedCenter - uniform.uClipCenter) - uniform.uClipHalf;
let sdf = length(max(d, vec3f(0.0))) + min(max(d.x, max(d.y, d.z)), 0.0);
// conservative splat radius
let radius = 2.0 * gsplatGetSizeFromScale(*scale);
if (sdf < -radius) {
// fully inside the box - clip the whole splat
*scale = vec3f(0.0);
setClipState(1u);
} else if (sdf > radius) {
// fully outside the box - no per-pixel clipping needed
setClipState(1u);
} else {
// intersects the box surface - clip per pixel in the fragment shader
setClipState(0u);
}
}
fn modifySplatColor(center: vec3f, color: ptr<function, vec4f>) {
}
2. 片元读取并在昂贵操作前提前退出:
示例假设填满画布。内置 screen_size 提供尺寸,其他目标或局部视口需调整归一化。
- GLSL
- WGSL
uniform vec3 uClipCenter;
uniform vec3 uClipHalf;
uniform mat4 uInvViewProj;
uniform vec4 screen_size;
void modifySplatColor(vec2 gaussianUV, inout vec4 color) {
// splats fully inside or outside the box were already resolved per splat in the vertex stage
if (getClipState() == 1u) return;
// reconstruct the world position of this fragment (on the splat's depth plane)
vec3 ndc = vec3(gl_FragCoord.xy * screen_size.zw, gl_FragCoord.z) * 2.0 - 1.0;
vec4 world = uInvViewProj * vec4(ndc, 1.0);
vec3 worldPos = world.xyz / world.w;
// clip fragments inside the box
vec3 d = abs(worldPos - uClipCenter) - uClipHalf;
if (max(d.x, max(d.y, d.z)) < 0.0) {
color.a = 0.0;
}
}
uniform uClipCenter: vec3f;
uniform uClipHalf: vec3f;
uniform uInvViewProj: mat4x4f;
uniform screen_size: vec4f;
fn modifySplatColor(gaussianUV: vec2f, color: ptr<function, vec4f>) {
// splats fully inside or outside the box were already resolved per splat in the vertex stage
if (getClipState() == 1u) {
return;
}
// reconstruct the world position of this fragment (on the splat's depth plane)
let uv = pcPosition.xy * uniform.screen_size.zw;
let ndc = vec3f(uv.x * 2.0 - 1.0, (1.0 - uv.y) * 2.0 - 1.0, pcPosition.z * 2.0 - 1.0);
let world = uniform.uInvViewProj * vec4f(ndc, 1.0);
let worldPos = world.xyz / world.w;
// clip fragments inside the box
let d = abs(worldPos - uniform.uClipCenter) - uniform.uClipHalf;
if (max(d.x, max(d.y, d.z)) < 0.0) {
*color = vec4f((*color).rgb, 0.0);
}
}
按常规方式将两个代码块通过 gsplatModifyVS 和 gsplatModifyPS 应用到全局材质。
内存考虑
部分平台将每分量保存在逐单元显存中,大小随渲染数量增长。应尽量紧凑,减少分量,将多个小值按位打包到一个 uint,避免独立数据流。