Draw each pixel separately with WebGL

First of all, let me tell you about my goal. I am creating another one pixel image editor and decided to make it with WebGL only (no Canvas2D at all). Quite stupid from me because I have no knowledge of this technology, but I'm trying! So ... the question is: how to change the color of just one pixel? I was struggling with another "misunderstanding" and found this answer with a particle system:

stackoverflow question

and I rewrote it to be close to my case:

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

As you can see, there are 10 pixels with the same color when painting on canvas. Here's the main drawing method:

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);
}

      

So how to change this example to pixels, each drawn with a unique color?

Some of you are probably saying "use canvas2d!" Not! I think the future won't be canvas2d. "So use the library!" - No, again! For this "simple" thing like rice squares, I don't want to include a large library.

Thank!

+3


source to share


2 answers


What you need to understand is that you specified that it is passing the variable to the fragment shader, it does not do any drawing. The shader fragment performs the drawing.

OpenGL is a tool for managing the various stages of the rendering process on modern graphics cards. It doesn't try to cover the blitting use-case of pixels in the screen buffer. ( glDrawPixels

deprecated and not available in ES 2.0 or WebGL)

However, there are ways that you could use it in your application.

I recommend these two articles to understand the prerequisites, although you should know that it is for openGL 3.2 and C ++ and not WebGL and Javascript. This way you can ignore things like context creation, how textures are loaded, and you don't need to create VertexArrays.

WebGL solution 1:

  • Create an empty texture.
  • Each frame, draw a square on the screen that displays this texture.
  • On mouse clicks, call glTexSubImage2D

    to change 1 pixel inside the texture.

WebGL 2 solution:

  • Edit gl_PointSize

    (vertex shader line 8)
  • Draw only 1 point
  • Don't clear screen (javascript line 45)
  • Save the webGL screen buffer (javascript lines 86 or 89).


See modified source

This solution does not modify 1 exact pixel, I think solution 1 is the best way to do it

Why you should use Canvas2D:

It is a mistake to think that Canvas2D is inferior to WebGL, One is a screwdriver and the other is a hammer. One is great for securing things with screws, which is great for securing things with nails.

Use WebGL when you want to manipulate the 3D rendering pipeline, use Canvas2D when you want to manipulate bitmaps.

TL; DR:

If you are using Canvas2D, you can start making the application you want today with good performance. If you want to use WebGL, you must make an effort to understand the rendering steps.

+3


source


The direct answer to your question is what gl.uniform4f

is a global variable. So you do it effectively:

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

      

You need to change it to this:

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

      



However, I want to point out that using a particle system to create a pixel editor is probably not the way to go. Imagine what happens if the user navigates the same area many times, you have a lot of redundant data. Second, how do you (efficiently) support the erase operation? Or fill?

At the end of the day, I think you want to move on to using textures. Textures are basically a fancy name for a dataset, but with restrictions on its format and what it can contain. However, GPUs can access them very quickly. So what you want to do is draw a square the same screen size and give it individual pixel data through a texture to tell it what to draw. Then manipulate the texture data directly when the user does something and redraws.

It's actually too time consuming to include how to implement this in webgl, so I suggest following some tutorials on the web. The best website for webgl is webgl basics . I suggest starting from there.

+2


source







All Articles