分别绘制每个像素WebGL

时间:2015-08-08 11:02:38

标签: javascript canvas webgl pixels

首先,让我谈谈我的目标。我正在创建另一个像素艺术在线编辑器,并决定只使用WebGL(根本没有Canvas2D)。我很愚蠢,因为我对这项技术一无所知,但我正在努力!那么......问题:如何改变唯一一个像素的颜色?我一直在努力解决另一个“误解”,并用粒子系统找到了答案:

https://stackoverflow.com/a/16465343/4458277

我重写了它,使它看起来接近我的情况:

https://jsfiddle.net/kurz/rt2n7hmb/1/

正如您所看到的,当您在画布上绘制时,有10个像素具有相同的颜色。 这里主要绘制方法:

function mouseMoveEvent(e) {
    /*... detect x and y...*/
    gl.bindBuffer(gl.ARRAY_BUFFER, particleBuffer);
    var data = new Float32Array(2);
    data[0] = x;
    data[1] = y;
    gl.uniform4f(
        gl.getUniformLocation(shaderProgram, "color"),
        Math.random(),
        Math.random(),
        Math.random(),
        1.0
    );
    gl.bufferSubData(gl.ARRAY_BUFFER, particleId * particleSize * sizeOfFloat, data);
    particleId = (particleId + 1) % vertexBufferSize;
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0, vertexBufferSize);
}

那么如何将此示例更改为使用唯一颜色绘制的像素?

有些人可能会说:“使用canvas2d!”。 - 不!我认为未来不是canvas2d。 “所以使用库!”。 - 不,再次!对于那种“简单”的东西,比如绘制正方形,我不想要包含大型库。

谢谢!

2 个答案:

答案 0 :(得分:4)

您需要了解的是,您发布的代码是将变量传递给片段着色器,它没有进行任何绘制。片段着色器正在进行绘制。

OpenGL是一种在现代图形卡上操纵渲染过程的不同阶段的工具。它不会尝试将blitting像素的用例覆盖到屏幕缓冲区。 (glDrawPixels已弃用,在ES 2.0或WebGL中不可用)

但是,您可以在应用程序中使用它。

我建议these两个articles来了解先决条件,但您应该知道它适用于OpenGL 3.2和C ++,而不是WebGL和Javascript。因此,您可以忽略诸如上下文创建,加载纹理的方式以及您不必创建VertexArrays等内容。

WebGL解决方案1:

  • 创建一个空纹理。
  • 每一帧,在显示此纹理的屏幕上绘制一个四边形。
  • 点击鼠标后,调用glTexSubImage2D修改纹理中的1个像素。

WebGL解决方案2:

  • 更改gl_PointSize(顶点着色器第8行)
  • 只画1点
  • 不要清除屏幕(javascript第45行)
  • 保留webGL屏幕缓冲区(javascript行86或89)

See modified source

此解决方案未修改1个精确像素,我认为解决方案1是更好的方法

为什么要使用Canvas2D:

考虑到Canvas2D不如WebGL是一个错误,一个是螺丝刀,另一个是锤子。一个非常适合用螺丝固定,另一个非常适合用钉子固定物品。

当您想要操纵3d渲染管道时使用WebGL,当您想要操作光栅图像时使用Canvas2D。

<强> TL; DR:

如果您使用Canvas2D,您可以开始制作您想要的应用程序并获得良好的性能。如果您想使用WebGL,您应该努力了解渲染管道的各个阶段。

答案 1 :(得分:2)

您问题的直接答案是gl.uniform4f是一个全局变量。所以你实际上是这样做的:

var color = randomColor(); // uniform4f call
for each point:
    drawPoint(p.x, p.y,color);

您需要将其更改为:

for each point:
    var color = gl.uniform4f(...);
    drawPoint(p.x, p.y, color);

但是,我想要注意,使用粒子系统进行像素编辑可能不是可行的方法。想象一下,如果用户反复在同一区域移动会发生什么,那么您将拥有大量冗余数据。其次,你如何(有效地)支持擦除操作?或者填写?

最终,我想你想继续使用纹理。纹理基本上是一个数据数组的奇特名称,但其格式和它可以包含的内容有限制。但是,GPU可以非常快速地访问它们。所以你要做的是绘制一个相同尺寸的四边形的四边形,并通过纹理给它单独的像素数据,告诉它要绘制什么。然后在用户执行操作并重绘时直接操作纹理数据。

在webgl中包含如何实现这一点真的太长了,所以我建议在网上学习一些教程。 webgl最好的一个atm是webgl fundamentals系列。我建议从那里开始。