单独绘制每个像素WebGL

问题描述 投票:0回答:3

首先谈谈我的目标。我正在创建另一个像素艺术在线编辑器,并决定仅使用 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。 “所以使用图书馆!”。 - 不,再来一次!对于像绘制正方形这样的“简单”事情,我不想包含大型库。

谢谢!

javascript canvas webgl pixel
3个回答
3
投票

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

OpenGL 是一种在现代显卡上操纵渲染过程不同阶段的工具。它不会尝试涵盖将像素传输到屏幕缓冲区的用例。 (

glDrawPixels
已弃用,并且在 ES 2.0 或 WebGL 中不可用)

但是,您可以通过多种方式在应用程序中使用它。

我推荐这些两篇文章来了解先决条件,尽管您应该知道它适用于 openGL 3.2 和 C++,而不是 WebGL 和 Javascript。因此,您可以忽略诸如上下文创建、纹理加载方式之类的事情,并且不必创建 VertexArray。

WebGL解决方案1:

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

WebGL解决方案2:

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

查看修改后的源代码

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

为什么你应该使用Canvas2D:

认为Canvas2D不如WebGL是错误的,一个是螺丝刀,另一个是锤子。一个非常适合用螺丝固定东西,另一种非常适合用钉子固定东西。

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

TL;博士:

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


3
投票

您问题的直接答案是

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 基础知识 系列。我建议从那里开始。


0
投票

要使用 WebGL 修改画布上的单个像素,您可以使用

gl.drawArrays()
方法。首先,您需要创建一个具有覆盖整个画布的单个顶点的缓冲区。然后,您可以使用
gl.uniform4f()
方法设置要修改的像素的颜色。最后,您可以使用
gl.drawArrays()
参数调用
gl.POINTS
在画布上绘制单个点,该点将对应于您要修改的像素。

这是一个示例代码片段,演示如何修改画布上位置 (10, 10) 处的单个像素:

const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');

// Create a buffer with a single vertex that covers the entire canvas
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);

// Set the color of the pixel to red
const colorLocation = gl.getUniformLocation(program, 'u_color');
gl.uniform4f(colorLocation, 1, 0, 0, 1);

// Draw a single point at position (10, 10)
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLocation);
gl.drawArrays(gl.POINTS, 10, 10);

请注意,此代码假设您已经创建了一个带有顶点着色器和片段着色器的 WebGL 程序,并且您已经获取了

u_color
制服和
position
属性的位置。

© www.soinside.com 2019 - 2024. All rights reserved.