降低 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);
其中 width
和 height
来自计算元素的大小。
放大示例:
这是一个演示,展示了同一形状的不同渲染分辨率。您会在第一张图片中看到锯齿,但第二张图片应该清晰。
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>
我在将 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);
其中 width
和 height
来自计算元素的大小。
放大示例:
这是一个演示,展示了同一形状的不同渲染分辨率。您会在第一张图片中看到锯齿,但第二张图片应该清晰。
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>