I am very new to WebGL, but not Javascript or Canvas. Essentially, I need a very fast way to be able to change the color of a pixel on a canvas at any coordinate. I feel like this could be done using WebGL fragment shaders. Is this possible, and is there a better way? How would I go about doing this?
相关问题
- Views base64 encoded blob in HTML with PHP
- Is there a limit to how many levels you can nest i
- How to toggle on Order in ReactJS
- void before promise syntax
- Keeping track of variable instances
This isn't an exhaustive guide to rendering because it doesn't include the bits about shader management; instead it focuses on how drawing of WebGL primitives is actually performed, and how to use orthographic projection to precisely position vertices on the screen.
For more information, I highly recommend these sources:
WebGL does all of its drawing with Vertex Buffer Objects (VBOs), so you need to compile all your data into 3D vertices and send them down to the video card in as few VBOs as possible (to maximize performance).
A VBO can be created in WebGL like so:
Then you need to send the data down to the buffer, usually in the form of a
Float32Array
. If you already have your data in the form of a regular JavaScript array, then you can usenew Float32Array(jsArray)
to initialize theFloat32Array
from thejsArray
's contents. Try to do this rarely though. Typed arrays are very fast, but initialize very slowly. It's best to create them once and reuse them whenever possible.Once you have a
Float32Array
, you can pass the data down to the buffer like so:You'll need to perform a
bufferData
orbufferSubData
call every time the data changes.By this time the data is on the graphics card, so you only need to actually draw it:
Note that although the graphics card can hold quite a few VBOs, you should use as few of them as possible, because the more
drawArrays
calls you have to perform, the slower things are going to get. Indeed, if you render only a single pixel at a time in a given VBO, it's going to be too slow to run.The reason the length of the
Float32Array
is divided by 3 is because each single data element is a 3D (X, Y, Z) coordinate, so each data element consists of 3 float components. Note that the first argument togl.drawArrays
wasgl.POINTS
. This instructs the graphics card to draw a single point (by default, a single pixel in size) for each item in the array. There are other ways to draw, and if you need to fill a group of pixels, one of the other draw modes (e.g.gl.TRIANGLES
) may be more to your liking.As for lighting up specific pixels, it depends on how the shader is written, but most likely you're making use of a modelview matrix and a projection matrix. The modelview matrix represents the orientation of the camera relative to the points being drawn, and the projection matrix represents the camera dimensions (width and height, field of view, nearest and furthest visible ranges, etc). So if you want to light specific pixels, your best bet is to apply an orthographic projection matrix with a width and height equal to the width and height of the canvas; and a modelview matrix set to the identity (no transformation). In an orthographic projection, objects don't shrink as they get further away from the camera, so they're very helpful for specifying exact positions relative to the screen. Also, if you give them the proper dimensions, you can position vertices very precisely -- at specific pixels, if you wish.
Different matrix libraries work in different ways, but for example, to set up an orthographic matrix in gl-matrix, you can do so like this:
The exact numbers depend on your preference; if you'd like to place the origin (0, 0) at the bottom-left of the canvas, you'd do so like this:
Note that the Z value of each point still has to lie between
near
andfar
in order to be rendered, however, and thenear
value cannot be set to0
.Let me know if any of this needs clarifying.
You could use GL_POINTS when drawing. Basically you would pass an array of points to the GPU, together with the colors and the positions. You then call drawArrays with the right data.
This might be too tied to other things, but this might help you:
https://github.com/funkaster/ChesterGL/blob/master/chesterGL/primitivesBlock.js#L126
https://github.com/funkaster/ChesterGL/blob/master/chesterGL/primitivesBlock.js#L260
That's how I render several points in the primitive block for chesterGL.
If you just want to draw single pixels you're probably better off using canvas 2d.
Otherwise you can probably figure it out from this tutorial which draws rectangles in pixel units so set the width and height to 1x1 and you'll get single pixels.
http://games.greggman.com/game/webgl-fundamentals/