WebGLRenderer.setPixelRatio drawing buffer not reflecting new ratio after first render
0 reputation · 23 Dec 2020, 03:41 UTC
0 reputation · 23 Dec 2020, 03:41 UTC
Three.js provides WebGLRenderer.setPixelRatio to control the drawing buffer size relative to CSS pixel dimensions. When this method is invoked after the renderer has already produced a frame, the updated ratio may not be reflected in the framebuffer allocation unless the canvas is reset or renderer.resetGLState() is called. This behavior creates ambiguity for applications that need to adjust pixel ratio dynamically, such as when responding to display density changes or memory pressure.
The uncertainty is amplified by WebGL1 and WebGL2 differences, where WebGL2 supports separate color and depth buffer scaling while WebGL1 may clamp the ratio. On mobile devices, excessively high ratios can lead to out-of-memory errors, yet the precise boundary depends on the GPU and browser implementation.
Because renderer.getPixelRatio() can return the newly assigned value while the underlying drawing buffer remains unchanged, what is the documented procedure for applying a pixel ratio change mid-session? Does the renderer require an explicit state reset, or does the change take effect automatically on the next frame? Are there version-specific constraints introduced around r152 that alter this behavior?
No explicit state reset is required. In standard Three.js, renderer.setPixelRatio(value) stores the new ratio and resizes the canvas drawing buffer using the renderer's current logical size. The new buffer is used on the next render. renderer.resetGLState() clears cached WebGL state; it does not reallocate the drawing buffer.
getPixelRatio() returns the stored value, not proof that the framebuffer changed.width and height attributes control the WebGL drawing buffer. When Three.js updates them, the browser reallocates the buffer.setPixelRatio. Render targets and post-processing passes have their own sizing.The most common cause is not the renderer's canvas buffer; it is an offscreen target that kept the old size. If you use EffectComposer, a WebGLRenderTarget, or a custom framebuffer, resize those after changing the ratio. A CSS rule or external resize handler that later writes canvas.width or canvas.height can also overwrite the renderer's size.
const next = Math.min(window.devicePixelRatio, 2);renderer.setPixelRatio(next);renderer.setSize(width, height, false); or use true if Three.js should update the CSS size too.composer.setSize(width, height); Some builds also expose composer.setPixelRatio(next);Check the canvas attributes and WebGL drawing buffer, not just getPixelRatio():
const next = Math.min(window.devicePixelRatio, 2);
renderer.setPixelRatio(next);
const size = renderer.getSize(new THREE.Vector2());
console.log(renderer.getPixelRatio(), size.x, size.y, renderer.domElement.width, renderer.domElement.height);
const gl = renderer.getContext();
renderer.render(scene, camera);
console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
Expected: domElement.width and gl.drawingBufferWidth equal Math.floor(size.x * next); height likewise.
I am not aware of a documented r152-specific change that requires resetGLState() for setPixelRatio. Treat any r152 or WebGL1-specific claim as unverified until checked against your exact Three.js build and changelog. High ratios can exhaust GPU memory on mobile, but the limit is device-dependent and not a Three.js clamp.
If the canvas attributes match but output is still low-resolution, the one detail that changes the fix is whether you are rendering directly to the canvas or through EffectComposer or custom render targets.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.