WebGL 图像渲染质量差

4
我在WebGL中渲染图像时遇到了问题:我有一个画布,上面有一个占据整个画布的四边形和一张纹理贴图(我已经设置了正确的纹理坐标),这张图片是一个包含RGB数据(按照此顺序)的Uint8array数组,尺寸为1024*768像素,我有正确的缓冲区。这是链接: https://istack.dev59.com/DrWGL.webp。
问题是:当渲染到WebGL中时,我的图片变得模糊和不清晰,即使我有一个与图像大小相同的画布。请看下面的结果: https://istack.dev59.com/k3rhr.webp。
现在是代码部分:以下是我用来创建纹理句柄的代码:
//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);
}

我尝试了很多选项,从过滤到设置样式选项image-rendering pixelated,将图像转换为RGBA并给出RGBA值,结果始终是相同的糟糕纹理渲染。看起来WebGL没有正确地插值数据,即使画布与纹理大小完全相同。
有人有提示吗?
提前致谢。
2个回答

8

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

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

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
@DerickThePoney 你需要将画布分辨率与显示大小匹配。这并不意味着您需要更改显示区域,只需更改画布的分辨率即可。您应该期望高质量的渲染,因为您正在使用 gl.NEAREST。如果图像与画布分辨率不匹配,则gl.LINEAR会在一定程度上提高质量,或者对图像进行Mip映射,或使用TEXTURE_MAX_ANISOTROPY_EXT扩展。 - Blindman67

4

让我们来试一下。

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++服务器传输到网站上。 - DerickThePoney
所以你尝试切换到“LINEAR”过滤器了吗? - gman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接