性能模式高斯离屏 Pass 实现方案
本文说明告警中心 / STCM 编辑器在「性能」「均衡」档下,如何用 Babylon.js 做低分辨率离屏合成。主画布分辨率不降,只降低场景(含高斯点云)的填充率。
目标
近处拖镜头要流畅。不改 engine.setHardwareScalingLevel,画布仍按设备像素输出。
只把高斯缩小、墙留在主画布时,两边深度对不上,高斯会盖住近处模型或穿帮。因此约定:
- 主画布全分辨率: 广场地面贴图、地面网格、地面装饰
- 离屏低分辨率: 墙、楼板、GLB、高斯点云及其余场景物体
- 同一相机、同一投影、同一张带深度的离屏缓冲,先写实体深度,再画高斯测深度
地面留在主画布,走动和俯视时地面不发糊;墙和高斯一起缩小,才能既省填充又不破遮挡。
启用条件
同时满足才走离屏,否则走普通 scene.render():
- 点云已有可见 splat
- 流式粗底图已铺完
- 离屏比例小于 1(性能约 0.55,均衡约 0.80)
底图未就绪就 blit,空高斯会把整屏涂黑。Pass 失败则复原分层,主场景直出。
渲染循环
使用 Engine.runRenderLoop 接管出图,不用默认的单次 scene.render() 包办全场景。
每帧顺序:
- 业务步进(机器人、标记、漫游等)
- 可选:镜头运动时冻结高斯按距离换档
- 若离屏 Pass 成功则 return,不再调用
Scene.render() - 否则
scene.render()走原路径
每帧开始调用 Engine.restoreDefaultFramebuffer(),避免上一帧还绑在 RTT 上。画布像素尺寸用 Engine.getRenderWidth(true) / getRenderHeight(true)。
分层(layerMask)
Babylon 可见性规则:mesh.layerMask & camera.layerMask !== 0 才会被该相机渲染。不复制网格,只改 mask。
| 层 | 值 | 内容 |
|---|---|---|
| 离屏层 | 0x10000000 | 墙、模型、高斯等 |
| 地面层 | 相机原 mask 去掉离屏位(常见 0x0FFFFFFF) | 广场地面、网格、地面装饰 |
- 离屏那趟:
camera.layerMask = 离屏层,再渲 RTT - 主画布那趟:
camera.layerMask = 原 mask & ~离屏层,再scene.render(),只出地面
高斯 renderingGroupId = 1,墙/模型默认 group 0,保证离屏内先实体后高斯。
离屏缓冲
使用 RenderTargetTexture,不要用 PostProcess,也不要第二台相机复制场景。
创建参数:
new RenderTargetTexture(name, { width, height }, scene, options)generateMipMaps: falsegenerateDepthBuffer: true(墙和高斯必须同一套深度)samplingMode: Texture.BILINEAR_SAMPLINGMODEdoNotChangeAspectRatio: trueignoreCameraViewport = true(按 RTT 自身宽高画)wrapU/wrapV=Texture.CLAMP_ADDRESSMODEclearColor = new Color4(0, 0, 0, 0)(透明底,贴回时才能丢掉空像素)
分辨率:高度 ≈ 画布高 × 比例,宽度按画布宽高比,最小边 16。窗口变化时 rtt.resize({ width, height })。
必须从 scene.customRenderTargets 中移除该 RTT。 否则 scene.render() 会再自动渲一遍,与手动画的冲突。
绑定:
rtt.activeCamera= 当前场景相机(同一套 view / projection)rtt.renderList= 本趟进入离屏的 mesh- 调用
rtt.render()只画这张小图
深度
高斯在 rendering group 1。默认组切换会清深度,高斯就看不到墙,会整片盖上去。
离屏 rtt.render() 之前:
scene.setRenderingAutoClearDepthStencil(1, false, false, false)
三个 false:不清 color / depth / stencil。
高斯材质:forceDepthWrite = false,disableDepthWrite = true(只测不写)。
主画布 scene.render() 时:
scene.autoClear = truescene.autoClearDepthAndStencil = true
地面要在干净的 depth 上画;随后 blit 不再测深度,直接盖在地面之上。
切回画布
rtt.render() 之后:
Engine.restoreDefaultFramebuffer()Engine.setViewport(camera.viewport, canvasW, canvasH)恢复全画布 viewportcamera.getProjectionMatrix(true)scene.updateTransformMatrix(true)
后两步纠正离屏小尺寸带来的投影,避免主画面和拾取变形。
贴回全屏
不用场景里再挂一张平面 mesh,用全屏 effect:
new EffectRenderer(engine)new EffectWrapper({ engine, vertexShader, fragmentShader, attributeNames: ['position'], samplerNames: ['textureSampler'] })wrapper.onApplyObservable里effect.setTexture('textureSampler', rtt)
片元逻辑:颜色和 alpha 都接近 0 则 discard,否则输出不透明 RGB,避免透明黑底糊一层。
贴回时机:订阅 scene.onAfterRenderingGroupObservable,在 renderingGroupId === 0(地面画完)时立刻 effectRenderer.render(wrapper)。若观察者未触发,scene.render() 返回后再补一次 blit。
一帧流程
Engine.runRenderLoop
restoreDefaultFramebuffer
按比例准备 / resize RTT
地面 → 地面层,其余 + 高斯 → 离屏层
camera.layerMask = 离屏层
setRenderingAutoClearDepthStencil(1, false, false, false)
rtt.render() // 小图:墙写深度 → 高斯测深度
camera.layerMask = 仅地面
restoreDefaultFramebuffer
setViewport + 刷新投影矩阵
autoClear / autoClearDepthAndStencil = true
scene.render() // 全分辨率只画地面
onAfterRenderingGroup(0)
EffectRenderer 贴 RTT // 空像素 discard
Babylon API 一览
| 环节 | API |
|---|---|
| 自定义帧 | Engine.runRenderLoop / stopRenderLoop |
| 画布尺寸 | getRenderWidth(true) / getRenderHeight(true) |
| 离屏目标 | RenderTargetTexture、resize、renderList、activeCamera、render() |
| 采样 / 环绕 | Texture.BILINEAR_SAMPLINGMODE、CLAMP_ADDRESSMODE |
| 清色 | Color4、rtt.clearColor |
| 分层 | AbstractMesh.layerMask、Camera.layerMask |
| 绘制顺序 | renderingGroupId |
| 组间深度 | Scene.setRenderingAutoClearDepthStencil |
| 主场景清屏 | scene.autoClear、autoClearDepthAndStencil |
| 回画布 | restoreDefaultFramebuffer、setViewport、updateTransformMatrix |
| 全屏贴图 | EffectRenderer、EffectWrapper、Effect.setTexture |
| 贴回时机 | onAfterRenderingGroupObservable |
| 禁止自动渲 RTT | 从 scene.customRenderTargets 移除 |
未使用 PostProcess、未使用 hardwareScaling、未使用第二台相机复制场景。
约束
- 高斯与墙必须同缓冲、同深度,禁止只缩小高斯
- 底图未就绪禁止离屏
- 离屏内实体组与高斯组之间禁止清深度
- 主画布贴回前要清屏,只留地面
- 贴图时丢掉空像素,避免全屏黑雾
- Pass 失败则主场景直出,不要继续用坏掉的离屏结果
调参
| 意图 | 调整 | 风险 |
|---|---|---|
| 更流畅、更糊 | 降低离屏比例(如 0.4) | 墙沿、文字更锯 |
| 接近原清晰度 | 比例靠近 1 | 几乎不省填充,可直接关 Pass |
| 近处仍卡 | 优先再降比例,而不是再拆一层深度 | — |
档位保存在本机。切到性能/均衡且参数变化时,按新策略重新加载点云流。
已知问题
- 最小像素过大,或点云未 ready 就关深度排序 → 场景空(属显示调参,不是 Pass 本身)
- 底图未铺完就 blit → 黑屏或高斯消失
- 高斯与墙不在同一张深度图 → 高斯盖住近处模型
- 只降 LOD、不降填充 → 近处仍然卡;应用离屏比例解决

288

被折叠的 条评论
为什么被折叠?



