簡體   English   中英

如何在不模糊的情況下拉伸 WebGL 畫布? “圖像渲染”樣式不起作用

[英]How to stretch a WebGL canvas without blurring? The style "image-rendering" doesn't work

我創建了一個帶有width=16height=16的畫布。 然后我使用 WebGL 向它渲染圖像。 這是它的樣子:

在此處輸入圖像描述

之后,我使用width: 256pxheight: 256px縮放畫布。 我還將image-rendering設置為pixelated

      canvas {
        image-rendering: optimizeSpeed;             /* STOP SMOOTHING, GIVE ME SPEED  */
        image-rendering: -moz-crisp-edges;          /* Firefox                        */
        image-rendering: -o-crisp-edges;            /* Opera                          */
        image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */
        image-rendering: pixelated; /* Chrome */
        image-rendering: optimize-contrast;         /* CSS3 Proposed                  */
        -ms-interpolation-mode: nearest-neighbor;   /* IE8+                           */
        width: 256px;
        height: 256px;
      }

這是結果:

在此處輸入圖像描述

圖像模糊。 為什么? 我在 OSX Mojave 上使用 Safari 12.0.2。

Safari 尚不支持image-rendering: pixelated; 在 WebGL 上。 提交了一個錯誤

crisp-edges也不 != pixelated crisp-edges可以是任意數量的算法。 這並不意味着pixelated 這意味着應用一些算法來保持清晰的邊緣,其中有大量的算法。

規范本身顯示了示例:

鑒於此圖像:

在此處輸入圖像描述

這是pixelated的:

在此處輸入圖像描述

重要提示:請參閱底部的更新,因為允許瀏覽器使用各種算法來處理crisp-edges ,例如結果可能是

在此處輸入圖像描述

所以換句話說,你的 CSS 可能不會產生你期望的結果。 如果瀏覽器不支持像素化但支持清晰邊緣,並且如果它們使用上述算法,那么您將不會看到像素化外觀。

在沒有image-rendering: pixelated是繪制到一個小紋理,然后使用NEAREST過濾將該紋理繪制到畫布上。

 const vs = ` attribute vec4 position; void main() { gl_Position = position; } `; const fs = ` precision mediump float; void main() { gl_FragColor = vec4(1, 0, 0, 1); } `; const screenVS = ` attribute vec4 position; varying vec2 v_texcoord; void main() { gl_Position = position; // because we know position goes from -1 to 1 v_texcoord = position.xy * 0.5 + 0.5; } `; const screenFS = ` precision mediump float; varying vec2 v_texcoord; uniform sampler2D u_tex; void main() { gl_FragColor = texture2D(u_tex, v_texcoord); } `; const gl = document.querySelector('canvas').getContext('webgl', {antialias: false}); // compile shaders, link programs, look up locations const programInfo = twgl.createProgramInfo(gl, [vs, fs]); const screenProgramInfo = twgl.createProgramInfo(gl, [screenVS, screenFS]); const width = 16; const height = 16; const tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); const fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fb); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); // create buffers and put data in const quadBufferInfo = twgl.createBufferInfoFromArrays(gl, { position: { numComponents: 2, data: [ -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, ], } }); render(); function render() { // draw at 16x16 to texture gl.bindFramebuffer(gl.FRAMEBUFFER, fb); gl.viewport(0, 0, width, height); gl.useProgram(programInfo.program); // bind buffers and set attributes twgl.setBuffersAndAttributes(gl, programInfo, quadBufferInfo); gl.drawArrays(gl.TRIANGLES, 0, 3); // only draw the first triangle // draw texture to canvas gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.useProgram(screenProgramInfo.program); // bind buffers and set attributes twgl.setBuffersAndAttributes(gl, screenProgramInfo, quadBufferInfo); // uniforms default to 0 so in this simple case // no need to bind texture or set uniforms since // we only have 1 texture, it's on texture unit 0 // and the uniform defaults to 0 gl.drawArrays(gl.TRIANGLES, 0, 6); }
 <canvas width="256" height="256"></canvas> <script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

注意:如果您正在渲染 3D 或出於其他原因需要深度緩沖區,則需要將深度渲染緩沖區附件添加到幀緩沖區。

請注意, optimizeSpeed也不是一個真正的選擇。 它已經被棄用很久了,就像crisp-edges一樣,由瀏覽器來解釋。

更新

規格於 2021 年 2 月更改 crisp-edges意思是“使用最近的鄰居”,而pixelated的意思是“讓它看起來像素化”,可以翻譯為“如果你想做比最近的鄰居更好的事情來保持圖像像素化”。 看到這個答案

這是一個非常古老的Webkit 錯誤,發生在 Blink 分叉之前。 從那以后,Blink修復了它,Webkit 仍然沒有。
您可能希望通過評論尚未解決的問題讓他們知道這仍然是一個問題。

至於解決方法,有幾種,但沒有完美的。

  • 第一個是直接以正確的大小繪制場景並自己進行像素化。
  • 另一種方法是在 2d 畫布上渲染 webgl 畫布(您可以使用 CSS 技巧調整大小,或者使用 2d 上下文imageSmoothingEnabled屬性直接以正確的大小渲染。
  • CSS-Houdini 可能會讓我們自己解決這個問題。

但這里真正的問題是確定您是否需要這種解決方法。 我認為對這種情況進行功能測試沒有任何意義(至少沒有 Houdini),因此這意味着您要么必須進行丑陋的用戶代理檢測,要么將解決方法應用於每個人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM