降低 WebGL 渲染分辨率,提高 FPS

Lower WebGL Render Resolution, and Increase FPS

我在将 2d 精灵渲染到 GL 时遇到问题 canvas。当我的浏览器在较低分辨率的显示器上呈现时,它以比在高分辨率显示器上呈现更高的 FPS。

Mac 视网膜显示屏:2880x1800 = ~20 FPS

外接显示器:1920x1080 = ~60 FPS

WebGL 在我的 1080 分辨率显示器上以比在 Mac 视网膜显示器上更快的 FPS 渲染。

强制 WebGL 以较低分辨率呈现的最佳做法是什么?我曾尝试寻找资源来帮助回答这个问题,但在网上找不到任何东西。

我试过如下降低分辨率:

var ratio = 1
var width = $(window).width();
var height = $(window).height();
if (window.screen.height * window.devicePixelRatio > 1080) {
   ratio = 1080/(window.screen.height * window.devicePixelRatio);
}
width = width*ratio;
height = height*ratio;
gl.canvas.width = width;
gl.canvas.height = height;

然后像这样设置我的视口:

gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);

代码更新

var width = $(window).width();
var height = $(window).height();
if (window.screen.height * window.devicePixelRatio > 1080) {
   glTools.ratio = 1080/(window.screen.height * window.devicePixelRatio);
} else {
   glTools.ratio = 1;
}
width = Math.round(width*glTools.ratio);
height = Math.round(height*glTools.ratio);
glTools.gl.canvas.width = width;
glTools.gl.canvas.height = height;
glTools.gl.viewport(0, 0, width, height);

性能更新 虽然这个问题的答案是正确的。我只是想在我的视频游戏中指出我的 FPS 的另一个罪魁祸首。在我的代码中使用计时器并在我的游戏中执行非硬件加速 css 动画设法阻止线程,因此导致渲染被安排在以后进行。 您可以看到游戏在 Obsidio

处以 60FPS 渲染

根据 MDN WebGL best practices:

Rendering to a canvas can be done at a different resolution than the style sheet will eventually force the canvas to appear at. If struggling with performance you should consider rendering to a low resolution WebGL context and using CSS to upscale its canvas to the size you intend.


WebGL 将根据您的 canvas' 宽度和高度属性进行渲染。 canvas 看起来像这样:<canvas width="256" height="256"> 将以 256x256 呈现。但是,您随后可以通过使用 CSS 设置 canvas 样式来放大绘图:canvas {width: 512px;height: 512px;} 将以放大的 512x512 显示渲染图像。

运行 下面的代码片段,您将看到输出:

[Canvas internal rendering] W: 256 | H: 256
[Actual canvas size] W: 512 | H: 512

const canvas = document.querySelector("canvas"),
  ctx = canvas.getContext("webgl"),
  {
    width,
    height
  } = canvas.getBoundingClientRect();

console.log(`[Canvas internal rendering] W: ${ctx.drawingBufferWidth} | H: ${ctx.drawingBufferHeight}`);
console.log(`[Actual canvas size] W: ${width} | H: ${height}`);
canvas {
  width: 512px;
  height: 512px;
}
<canvas width="256" height="256">


查看您的代码后,您的游戏似乎 "zoom in" 的原因是您将 viewport 与渲染大小联系在一起。不要耦合这些。您的视口设置绝对应该反映您的宽高比,但应该独立于您的绘图大小。 WebGL 会将其默认为您的 canvas 大小,但由于您想要放大,请尝试将其设置为放大后的 canvas 大小,看看您是否仍然可以缩放。从我上面的例子来看,代码看起来像:

ctx.viewport(0, 0, width, height);

其中 widthheight 来自计算元素的大小。


放大示例:

这是一个演示,展示了同一形状的不同渲染分辨率。您会在第一张图片中看到锯齿,但第二张图片应该清晰。

const vertSource = `
attribute vec3 a_position;
void main(void) {
    gl_Position = vec4((a_position * 2.0) - 1.0, 1.0);
}`;

const fragSource = `
void main(void) {
    gl_FragColor = vec4(1.0);
}`;

const verts = [
  0.1, 0.1, 0,
  0.9, 0.1, 0,
  0.5, 0.9, 0
];

function setupCanvas(canvas) {
  const gl = canvas.getContext("webgl");

  const {
    width,
    height
  } = canvas.getBoundingClientRect();

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);
  gl.viewport(0.0, 0.0, gl.drawingBufferWidth, gl.drawingBufferHeight);

  console.log(`[Canvas internal rendering] W: ${gl.drawingBufferWidth} | H: ${gl.drawingBufferHeight}`);
  console.log(`[Actual canvas size] W: ${width} | H: ${height}`);

  const b = gl.createBuffer(),
    p = gl.createProgram(),
    v = gl.createShader(gl.VERTEX_SHADER),
    f = gl.createShader(gl.FRAGMENT_SHADER);

  gl.bindBuffer(gl.ARRAY_BUFFER, b);
  gl.shaderSource(v, vertSource);
  gl.compileShader(v);
  gl.shaderSource(f, fragSource);
  gl.compileShader(f);
  gl.attachShader(p, v);
  gl.attachShader(p, f);
  gl.linkProgram(p);
  gl.useProgram(p);

  const a = gl.getAttribLocation(p, "a_position");
  gl.vertexAttribPointer(a, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(a);

  function draw() {
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.DYNAMIC_DRAW);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLES, 0, verts.length / 3);
  }
  draw();
}

Array.from(document.querySelectorAll("canvas"))
  .forEach(setupCanvas);
canvas {
  width: 512px;
  height: 512px;
}
<canvas width="256" height="256"></canvas>
<canvas width="512" height="512"></canvas>