按需渲染
产品查看器、配置器、检查工具等经常静止的场景无需每帧重绘。将 app.autoRender 设为 false,仅在变化时设置 app.renderNextFrame,即可按需渲染,让空闲场景的 GPU 也空闲。通用方式参见优化指南。
大型流式场景还会在后台加载和调整细节。这些工作与渲染解耦,无论是否绘制,每帧都会进行,因此空闲时也持续加载。产生值得绘制的新结果时,系统通过 frame:request 通知。
基本模式
关闭自动渲染,系统请求时绘制:
app.autoRender = false;
app.systems.gsplat.on('frame:request', () => {
app.renderNextFrame = true;
});
当流式加载产生新细节,或异步排序结果就绪时,frame:request 在渲染前每帧触发一次。处理它可让场景逐渐达到完整细节,无需中间每帧都绘制。
frame:request 的触发范围与自行请求
frame:request 覆盖数据本身的变化,引擎自动触发。只影响当前场景显示方式的变化不触发,需自行请求:
| 变化 | 是否触发 frame:request |
|---|---|
| LOD 细节加载进度 | 是,自动处理 |
| 预算、LOD 距离或范围 | 是,自动处理 |
| 相机移动 | 否,自行请求 |
| 画布或视口尺寸变化 | 否,自行请求 |
| 材质参数,例如 alpha 裁剪 | 否,自行请求 |
| 着色器动画,例如渐显 | 否,自行请求 |
| 增删 gsplat、相机或图层 | 否,自行请求 |
自行请求时,变化后设置 app.renderNextFrame = true。例如相机移动时渲染:
const lastPos = new pc.Vec3();
const lastRot = new pc.Quat();
app.on('update', () => {
const pos = camera.getPosition();
const rot = camera.getRotation();
if (!pos.equals(lastPos) || !rot.equals(lastRot)) {
app.renderNextFrame = true;
lastPos.copy(pos);
lastRot.copy(rot);
}
});
备注
相机、图层和 gsplat 组件的增删只有在实际渲染帧中才注册,因此变化后至少绘制一帧。这也是首次加载期间应正常连续渲染、运行就绪后再转按需的原因。
结合快速首帧
可与快速首帧结合。加载及入场动画期间逐帧绘制,完成后切换:
- 首帧加载时保持 autoRender = true。
- 等待 frame:ready 报告场景加载、排序和绘制完成。
- 切换 autoRender = false,由 frame:request 及相机、尺寸、设置变化驱动。
const gsplatSystem = app.systems.gsplat;
const onFrameReady = (camera, layer, ready, loadingCount) => {
if (ready && loadingCount === 0) {
gsplatSystem.off('frame:ready', onFrameReady);
// the scene is fully shown — switch to on-demand rendering from here
app.autoRender = false;
}
};
gsplatSystem.on('frame:ready', onFrameReady);
gsplatSystem.on('frame:request', () => {
app.renderNextFrame = true;
});
两个事件互补:frame:ready 表示某个已渲染帧完整且排序完成,适合首次显示和截图;frame:request 表示有新流式数据值得绘制。
实时示例
单个非流式数据按需渲染:显示后停止,仅在相机移动、窗口变化或设置更改时重绘。
基础按需渲染