【问题标题】:Quality differences between HTML, Canvas and WebGLHTML、Canvas 和 WebGL 之间的质量差异
【发布时间】:2020-05-24 08:29:16
【问题描述】:

我想在 WebGL 中绘制图像,但缩小了尺寸。当我不缩放时,图像质量很好,但如果我将其缩小,则图像质量很差。

我从这里读到了“在 WebGL 中处理高 DPI(视网膜)显示”:http://www.khronos.org/webgl/wiki/HandlingHighDPI,我也尝试过这样做。我在 WebGL 中的代码是:

        Initializations:

        var devicePixelRatio = window.devicePixelRatio || 1;

        gl.canvas.style.width = "800px";
        gl.canvas.style.height = "600px";

        canvas.width = Math.round(800 * devicePixelRatio);
        canvas.height = Math.round(600 * devicePixelRatio);


        For drawing:

        gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
        matrix = m3.scale(matrix, 28/800, 35/600); // matrix for scaling texture

        Textures:

        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);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

但这与 HTML 图像的质量不同。我的 HTML 代码是:

        setTimeout(function() {

            var imagine = new Image();
            imagine.src = 'Tank.png';
            imagine.width = '28';
            imagine.height = '35';
            document.body.appendChild(imagine);

        }, 1000);

我在画布中的代码是:

        var imagine = new Image();
        imagine.src = 'Tank.png';
        imagine.width = '28';
        imagine.height = '35';
        imagine.onload = function() {
            context.drawImage(imagine, 0, 0, 28, 35);
        }
        context = canvas.getContext('2d');



图片质量的区别如下(质量:WebGL

我知道这不是一个很大的区别,但我真的想展示高质量的图片。在 HTML 中,图片更平滑。我观察到,如果我放大 Chrome 浏览器,来自 HTML 的图片质量会提高,但 WebGL 图片保持相同的分辨率并且会降低质量。如果我刷新页面,为 WebGL 更新 devicePixelRatio,那么图片的质量会更好,但我的浏览器在 500% 缩放时运行速度较慢,我认为是因为它使画布更大并且必须绘制更多。但是在 HTML 中,如果我以 500% 的缩放比例移动图像,则没有问题,图像移动得很好,质量也很好。


在这种情况下 - WebGL image rendering bad quality - 图像未缩放,但我需要缩小图片。

与这种情况相比 - canvas drawImage quality - 我在所有三个程序中都将值作为整数。



最后一个问题:

如何在 webGL 中以与在 HTML 中相同的质量(看不到这些轨道中的线条)绘制图像,并且即使浏览器放大也能获得良好的质量?我还有什么其他的可能性来画它们?我应该使用什么技术?我想使用 WebGL,因为它有一些 HTML 没有的功能,我想从头开始绘制一些东西,比如线条或点。


编辑 1:这是正常分辨率的图片。



在这张图片中,轨道的线条似乎不是笔直的,而是交叉的。如果您可以近距离观察 Canvas 和 WebGL 中的图像,轨道的第一行,首先在左侧,然后在右侧,它们是笔直的。更多的是,带有 Canvas 的图片有更多的直线轨道,甚至是最后一条。 HTML的最后一个图像具有相同形状的所有轨道。


对不起,因为我放了非常小的图片。这是因为我需要这个比例,我需要那些图片不要更大。如果我要画更大的图片,那么 devicePixelRatio 会使图片看起来更好,但我不使用正常尺寸的图片。这样,问题就可以通过 devicePixelRatio 解决。对于较小的尺寸,这种带有 devicePixelRatio 的方法对我没有帮助。这就是我寻找其他解决方案的原因。

【问题讨论】:

  • 如果这些图像是根据您在它们上面建立的顺序排列的:第一张图像看起来清晰而完美,其他两张看起来模糊到无用的地步。所以 webgl 赢了。如果您的图像排序与代码排序不同:请解决此问题。
  • 是的,第一张照片可能看起来很完美,但轨道画得不好。也许有更多的细节试图融入其中,但看起来并不好。当然,第二个和第三个是模糊的,但是因为它们不强制绘制像素,它们看起来更好。第三个看起来比其他更好,因为它没有第二个模糊。我不喜欢那种清脆的感觉,这就是为什么我说那个图像画得有点差。如果你仔细看,第一张和第二张图片的第一条线是笔直的,但第三张没有。第三个所有线条都有点弯曲
  • 请提供完整的原始图像(原始分辨率),因为无法知道创意质量如何。

标签: javascript html image canvas webgl


【解决方案1】:

如果你想让你的图片画得更流畅,那么你可以使用mipmaps!尝试使图片的两个维度都具有 2 的幂,然后可以生成 mipmap,如下所示:

gl.generateMipmap(gl.TEXTURE_2D);

使用 mipmap,您可以通过为每个纹理设置纹理过滤来选择 WebGL 的功能。如果你想让你的图像平滑,那么你必须将纹理过滤更改为 LINEAR_MIPMAP_LINEAR。因此,在生成 mipmap 之后,您必须编写:

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

有差异的图片如下(LINEAR vs LINEAR_MIPMAP_LINEAR):

在第一张图片中,轨道是不对称的,但第二张图片是平滑的。我认为第二张图片看起来更像您提供的 HTML 图片。


有一个很棒的关于 WebGL 纹理的教程:https://webglfundamentals.org/webgl/lessons/webgl-3d-textures.html。上面写着这种类型的纹理过滤是最慢的,它需要多 33% 的内存,所以要注意这一点!

【讨论】:

    【解决方案2】:

    随机缩放、定位和旋转

    如果图像经过变换和动画处理,您最好使用线性插值,原始图像至少比渲染结果大 2 倍。

    确保画布(2D 或 WebGL)与设备像素对齐,并且与物理设备像素一对一匹配。

    如果devicePixelRatio 是 1 或 2 以外的任何其他值(HDPI 或 Retina 为 2),则不要使用它,因为其他值意味着页面已被缩放,并且您无法获得一对一的像素与设备像素匹配.尝试缩放画布(2D 或 WebGL)只会降低质量。

    原图

    这最适用于绘制到像素边界的轴对齐渲染图像。但是,对于缩放、旋转和/或未对齐的渲染,它仍然有一些好处。

    优质画布(2d 或 webGL)图像渲染最重要的部分是原始图像大小与渲染结果之间的关系。

    如果您将图像缩小到 28 x 35 像素,则原始图像必须是 2* (68, 92) 4* (136, 184) 等...最终渲染大小的 2 倍结果。 注意您必须考虑较大图像的 GPU RAM 成本。

    与渲染像素对齐的原始图像细节

    图像中必须尽可能清晰的重要细节必须与渲染的像素边缘对齐,否则最终会出现细节模糊。

    下图是最终尺寸的 16*。它上面有一个棋盘格(每个正方形 16 × 16 像素)来显示渲染像素的大小。

    注意轨道、车身、枪管、炮塔的边缘是如何与最终缩小的像素对齐的。

    渲染

    下一张图片显示渲染结果,像素对齐(WebGL 顶部的两行在 2D 中具有相同的结果)

    从左到右是原始源图片的大小,是最终大小的2倍到16倍。

    每一行使用不同的像素查找

    • 最近的。最快的渲染每个像素最靠近左上角的像素最终渲染

    • 线性。默认为 2d (smoothing = true), webGL (gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR))。像素颜色是最终像素附近(下、右、下、右下)的 4 个像素的线性插值。

    • 对数均值。仅限自定义 WebGL。片段着色器使用渲染像素下所有像素的总光子计数来计算最终的 RGB 像素颜色。对于原始大小的 2 倍 GPU 成本大约与 Linear 相同,对于 16 倍仅适用于高端设备,或者图像渲染计数低(每像素比线性慢 64 倍)

    • 对数均值 +。使用与 Log Mean 相同的片段着色器进行渲染,但使用修改后的锐化卷积过滤器对原始图像进行预处理(及时)以在2/3 * invScale(对数计算)处锐化,以增加低对比度边界处的视觉对比度。成本是一次性初始成本。

    对于实时项目,我个人使用第二行第一列 2 * 线性,当在 HDPI 或高渲染计数上渲染时,我使用最简单的 1 * 线性(未显示)。

    大多数人无法区分第 2 行、第 3 行和最后一行。可以不放大图片吗?

    旋转

    下一张图显示了相同的方法,从左到右从 2 次到 16 次,然后是线性行、最近行和对数均值,+。

    注意事项

    • Lo​​g RGB 图像处理仅对具有高色彩对比度的高饱和图像有显着改进。坦克图像具有非常低的颜色对比度,因此无法从对数均值中获得太多好处。下一张图片从左到右显示了高颜色对比度版本的最接近、线性和对数平均值。

    • 此答案中的所有图像都是在 GPU 加速的 Chrome 80 Win 10 x64 上创建的。 OP的原始坦克参考图像。

    • 我刚刚注意到,当我完成时,Nearest 旋转图像的外边缘都具有抗锯齿功能。这是我的错误,因为我忘记给图像一些透明填充。抗锯齿是由于多边形边缘而不是像素gl.TEXTURE_MIN_FILTER, gl.LINEAR 查找过程的一部分。有很多工作需要修复,抱歉。

    【讨论】:

    • 很好的答案!确实,我看不出最后几排坦克之间的区别。你说了很多有趣的事情,但我是初学者,不像你知道的那么多。例如,我在 google 上进行了搜索,但没有找到关于 Log Mean 的明确信息。请您提供这些解决方案的一些代码或文章吗?
    • @Niqusor31 有关 sRGB 的详细信息 en.wikipedia.org/wiki/SRGB 我所说的 Log RGB 和 Log Mean 是针对性能优化的 sRGB 模型的变体,这是我自己的发明。显示更复杂的处理的重点是它不值得 CPU/GPU 周期。就渲染质量而言,确保一对一的像素与设备像素比率以及尺寸合适的高质量源图像,其对比/重要细节与渲染像素边界对齐是最重要的。
    • “确保画布(2D 或 WebGL)与设备像素对齐,并且与物理设备像素一对一匹配。”。仅供参考:目前这是不可能的。 They are adding APIs to kind of make to possible。此外,考虑到lots of devices have devicePixel ratios of other values,“如果 devicePixelRatio 的值不是 1 或 2,则不要使用它”似乎很可疑。
    • @gman "impossible" 不。我现在坐在画布前,它具有完美的对齐方式,并且可以可靠地将其标记为 true(显然并非不可能)。当我立即缩放时,我知道不再可能对齐。当所有指示都显示对齐但不正确时,存在一些边缘情况,例如当窗口跨越两个或多个具有不同分辨率的显示设备时。
    • @gman 来自 MDN devicePixelRatio“...以物理像素为单位的分辨率与以 CSS 像素为单位的分辨率之比。”。那么screenX"...水平距离,以 CSS 像素为单位。" 还有 screen.widthscreen.availWidth"...(以像素为单位)..."(不是 CSS 像素!!)。然而screenX + screen.availWidth === screen.widthtruedevicePixelRatio !== 1 因此当比率不是1 或2 时不要使用devicePixelRatio 你不能确定对齐。不是 1 或最大 2 的值,你就会开始弄乱设备设计的演示参数,并不是每个人都有完美的视力。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    • 2017-01-09
    • 2015-05-03
    • 2010-09-14
    • 1970-01-01
    • 2022-09-29
    • 1970-01-01
    相关资源
    最近更新 更多