首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何不模糊地拉伸WebGL画布?样式“图像渲染”不起作用。

如何不模糊地拉伸WebGL画布?样式“图像渲染”不起作用。
EN

Stack Overflow用户
提问于 2019-01-27 23:22:04
回答 2查看 2.1K关注 0票数 7

我用width=16height=16创建了一个画布。然后,我使用WebGL向它呈现一个图像。看上去是这样的:

之后,我使用width: 256pxheight: 256px对画布进行缩放。我还将image-rendering设置为pixelated

代码语言:javascript
复制
      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;
      }

其结果是:

图像模糊了。为什么?我在上使用Safari 12.0.2。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-01-28 03:58:51

Safari还不支持image-rendering: pixelated; on WebGL。提出了一个错误

另外,crisp-edges不!= pixelatedcrisp-edges可以是任意数量的算法。这并不意味着pixelated。它意味着应用一些保持清晰边缘的算法,其中有大量的算法。

规范本身展示了一些例子:

鉴于这一形象:

我是pixelated

重要内容:请参阅底部的更新,其中允许浏览器对crisp-edges使用各种算法,因此,例如,结果可能是

因此,换句话说,您的CSS可能不会产生您期望的结果。如果浏览器不支持像素化,但是支持清晰的边缘,如果它们使用像上面这样的算法,那么就不会有像素化的外观了。

在不使用image-rendering: pixelated的情况下绘制像素化图形的最有效方法是绘制到一个小的纹理,然后使用NEAREST过滤将该纹理绘制到画布上。

代码语言:javascript
复制
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);
}
代码语言:javascript
复制
<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的意思是“保持它看起来像像素化的”,这可以翻译为“如果您想做比最近的邻居更好的事情保持图像像素化”。请参阅这个答案

票数 6
EN

Stack Overflow用户

发布于 2019-01-28 01:58:09

这是一个非常古老的Webkit bug,从眨眼叉发生之前。从那以后,眨眼修好了,Webkit仍然没有。

你可能想让他们知道这仍然是一个问题,通过评论仍然悬而未决的问题。

至于解决办法,有几个,但不是完美的。

  • 第一种方法是以正确的尺寸直接画出你的场景,然后自己画像素。
  • 另一种方法是在2d画布上呈现webgl画布(使用CSS技巧调整大小,或者使用2d上下文imageSmoothingEnabled属性以正确的大小直接呈现。
  • CSS可能会让我们自己解决这个问题。

但真正的问题是找出你是否需要这个解决办法。我看不出有任何方法来测试这个案例(至少没有Houdini),所以这意味着你要么要做丑陋的用户代理检测,要么把解决办法应用到每个人身上。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54393847

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档