使用WebGLRenderer
创建antialias = true
的实例会导致明显的性能问题,因为分辨率会增加,尤其是视网膜显示(window.devicePixelRatio === 2
)。
由于无法动态改变抗锯齿模式,问题是:如何自动调整像素比率以保持FPS高于某个阈值(例如30)?
答案 0 :(得分:0)
我们的想法是在渲染循环中监控FPS(通过测量requestAnimationFrame
次呼叫之间的间隔)并相应地减少或增加DPR。
“监控”是指将这些间隔记录到阵列中,删除最小值/最大值(以避免峰值),取平均值并将其与预定义阈值进行比较。
const highFrequencyThreshold = 20; // ~50 FPS
const lowFrequencyThreshold = 34; // ~30 FPS
const minDpr = 0.5;
const maxDpr = window.devicePixelRatio;
const deltaDpr = 0.1;
const relaxPeriod = 4000;
const accumulatorLength = 20;
const frameTimestamp = performance.now();
const frequencyAccumulator = [];
const lastUpdatedAt = null;
const renderer = new WebGLRenderer({
antialias: true,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
animate();
function animate(timestamp: number = performance.now()) {
requestAnimationFrame(animate);
monitor(frameTimestamp, timestamp);
frameTimestamp = timestamp;
// animation calculations and rendering
// ...
}
function monitor(frameTimestamp: number, now: number) {
collectFrequency(now - frameTimestamp);
// accumulator is not fully filled
if (frequencyAccumulator.length < accumulatorLength) {
return;
}
// an update happened recently
if (now - lastUpdatedAt < relaxPeriod) {
return;
}
const dpr = renderer.getPixelRatio();
const frequencyMedian = median(...frequencyAccumulator);
if (frequencyMedian > lowFrequencyThreshold && dpr > minDpr) {
updateDpr(dpr, -deltaDpr, now);
} else if (frequencyMedian < highFrequencyThreshold && dpr < maxDpr) {
updateDpr(dpr, deltaDpr, now);
}
}
function collectFrequency(frequency: number) {
if (frequency > 0) {
frequencyAccumulator.push(frequency);
}
if (frequencyAccumulator.length > accumulatorLength) {
frequencyAccumulator.shift();
}
}
function updateDpr(dpr: number, delta: number, now: number) {
renderer.setPixelRatio(dpr + delta);
frequencyAccumulator = [];
lastUpdatedAt = now;
}
function median(...elements: number[]): number {
const indexOfMin = elements.indexOf(Math.min(...elements));
const indexOfMax = elements.indexOf(Math.max(...elements));
const noMinMax = elements.filter((_, index) => index !== indexOfMin && index !== indexOfMax);
return average(...noMinMax);
}
function average(...elements: number[]): number {
return elements.reduce((sum, value) => sum + value, 0) / elements.length;
}
请注意,更新DPR可能会导致短时动画冻结。
此外,可以使用更聪明的东西来平衡DPR值(而不是用updateDpr()
的线性步骤调用0.1
,例如二分搜索。