【问题标题】:WebGL image rendering bad qualityWebGL 图像渲染质量差
【发布时间】:2017-01-09 14:43:02
【问题描述】:

我在 WebGL 中的图像渲染有问题:我有一个画布,一个四边形占据整个画布,一个纹理应该映射到整个四边形上(我已经设置了正确的纹理坐标)。

图像是一个包含 RGB 数据(按此顺序)的 Uint8array,图像为 1024*768 像素。我有正确的缓冲区。这是图片的链接。

问题如下:当渲染到 WebGL 时,我的图片变得模糊不清,即使我有一个图像大小的画布。看下面的结果:

现在是代码:这是我用来创建纹理句柄的代码:

//texture
that.Texture = that.gl.createTexture();         
that.gl.bindTexture(that.gl.TEXTURE_2D, that.Texture);                          

// Set the parameters so we can render any size image.
that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_WRAP_S, that.gl.CLAMP_TO_EDGE);
that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_WRAP_T, that.gl.CLAMP_TO_EDGE);
that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_MIN_FILTER, that.gl.NEAREST);
that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_MAG_FILTER, that.gl.NEAREST);

数据按如下方式加载到纹理上:

this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGB, this.m_iImageWidth, this.m_iImageHeight,
0, this.gl.RGB, this.gl.UNSIGNED_BYTE, this.m_aImage);

最后,这是我使用的片段着色器:

precision mediump float;
uniform sampler2D u_image;
varying vec2 v_texCoord;
void main() 
{
   gl_FragColor = texture2D(u_image, v_texCoord);
}

我尝试了很多选项,从过滤到设置样式选项图像渲染像素化,将图像转换为 RGBA 并为其赋予 RGBA 值,结果总是相同的糟糕纹理渲染。即使画布与纹理的大小完全相同,WebGL 似乎也无法正确插入数据。

有人有提示吗?

提前致谢。

【问题讨论】:

    标签: javascript canvas webgl image-rendering


    【解决方案1】:

    确保画布的宽度和高度与像素显示大小相匹配。显示大小通过样式宽度和高度设置。

    分辨率是图像中的像素数。显示大小是它在页面上占据的空间量。两者应该匹配以获得最佳结果。

    canvas = document.createElement("canvas");
    canvas.width = 1024; // set the canvas resolution
    canvas.height = 784;
    
    canvas.style.width = "1024px"; // set the display size.
    canvas.style.height = "784px";
    

    显示尺寸大于分辨率会拉长画布并造成模糊

    【讨论】:

    • 感谢您的评论。但是,这不会改变画布显示区域吗?如果是这样,那是我想避免的。另外,我不明白为什么必须这样做。我获得的纹理渲染质量比我预期的要差得多……我明天回去工作时会做一个完整的试验。再次感谢!
    • @DerickThePoney 您需要将画布分辨率与显示尺寸相匹配。这并不意味着您需要更改显示区域,只需更改画布的分辨率。您应该期待高质量的渲染,您使用的是gl.NEARESTgl.LINEAR 如果图像与画布 res 或 Mip 映射图像不匹配,或使用 TEXTURE_MAX_ANISOTROPY_EXT 的扩展名,将在一定程度上提高质量
    【解决方案2】:

    让我们试试吧。

    var vs = `
    attribute vec4 position;
    attribute vec2 texcoord;
    varying vec2 v_texCoord;
    void main() {
      gl_Position = vec4(position.xy * vec2(1, -1), 0, 1);
      v_texCoord = texcoord;
    }
    `;
    var fs = `
    precision mediump float;
    uniform sampler2D u_image;
    varying vec2 v_texCoord;
    void main() 
    {
       gl_FragColor = texture2D(u_image, v_texCoord);
    }`
    ;
    
    var gl = document.querySelector("canvas").getContext("webgl");
    var that = {
        gl: gl,
    };
    var img = new Image();
    img.crossOrigin = "";
    img.onload = function() {
      that.m_aImage = img;
      that.m_iImageWidth = img.width;
      that.m_iImageHeight = img.height;
      gl.canvas.width = img.width;
      gl.canvas.height = img.height;
      console.log("size: ", img.width, "x ", img.height);
      render.call(that);
    }
    img.src = "https://i.imgur.com/ZCfccZh.png";
    
    function render() {
      //texture
      that.Texture = that.gl.createTexture();         
      that.gl.bindTexture(that.gl.TEXTURE_2D, that.Texture);                          
    
      // Set the parameters so we can render any size image.
      that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_WRAP_S, that.gl.CLAMP_TO_EDGE);
      that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_WRAP_T, that.gl.CLAMP_TO_EDGE);
      that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_MIN_FILTER, that.gl.NEAREST);
      that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_MAG_FILTER, that.gl.NEAREST);
    
      // upload the image directly.
      //  this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGB, this.gl.RGB, this.gl.UNSIGNED_BYTE, img);
      // upload the image indirectly
      var ctx = document.createElement("canvas").getContext("2d");
      ctx.canvas.width = this.m_iImageWidth;
      ctx.canvas.height = this.m_iImageHeight;
      ctx.drawImage(this.m_aImage, 0, 0);
      var imageData = ctx.getImageData(0, 0, this.m_iImageWidth, this.m_iImageHeight);
      var numPixels = this.m_iImageWidth * this.m_iImageHeight;
      var pixels = new Uint8Array(numPixels * 3);
      var src = 0;
      var dst = 0;
      for (var i = 0; i < numPixels; ++i) {
        pixels[src++] = imageData.data[dst++];
        pixels[src++] = imageData.data[dst++];
        pixels[src++] = imageData.data[dst++];
        ++dst;
      }  
      
      this.m_aImage = pixels;
      this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGB, this.m_iImageWidth, this.m_iImageHeight, 0, this.gl.RGB, this.gl.UNSIGNED_BYTE, this.m_aImage);
      
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
      
      var programInfo = twgl.createProgramInfo(gl, [vs, fs]);
      var bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
      gl.useProgram(programInfo.program);
      twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
      twgl.drawBufferInfo(gl, bufferInfo);
     }
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    <canvas></canvas>

    为我工作。虽然图像显然是 800x600 而不是 1024x768。如果您的图像与画布大小不同,那么您可能希望使用LINEAR 过滤而不是NEAREST

    此外,您可以直接上传图像,而不是通过数组缓冲区(结果相同,但可能更小、更快且内存更少)

    【讨论】:

    • 您好,感谢您的回答!正如@Blindman67 所说,我认为问题在于画布的宽度和高度。至于图像大小,我不得不手动减小它以将其放到stackoverflow :-) 并且对于图像上传,我无法使用它。此图像来自文件,但大多数时候图像不会来自 bmp 或 png,而是来自相机。图像缓冲区通过 websocket 从自定义 c++ 服务器传送到网站。
    • 那么您是否尝试切换到LINEAR 过滤?
    猜你喜欢
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 2019-10-11
    • 2010-11-19
    • 1970-01-01
    • 1970-01-01
    • 2020-10-08
    • 1970-01-01
    相关资源
    最近更新 更多