Before I get into the main part of my answer, and in an effort to answer the last part of your question, it may help to understand how the numbers received by WebGL actually become colors on your screen. I've already written up a detailed answer on exactly that, if you don't mind clicking through to " How WebGL works? ".
You can't use a near plane depth of 0 for perspective matrices because doing so produces a division by 0, which leads to NaN values in the matrix:
// fovy, aspect, znear, zfar
m = mat4.perspective( 45, 1, 0, 100);
//=> [NaN, 0, 0, 0, 0, NaN, 0, 0, NaN, NaN, -1, -1, 0, 0, 0, 0]
Think of the near plane as the eyepiece of a camera lens. It's very close to your eye, but it's not actually in your eye, which is what a near plane of 0 would imply.
However, using a nonzero near value is only half of the answer. You should become familiar with orthographic projection, which is sort of the opposite of perspective.
Perspective means that as objects become more distant, they appear to grow smaller. This is what gives you a sense of depth. With orthographic projection matrices, objects are drawn the same size no matter how close or far they are to the camera, which makes them perfect for all sorts of 2D operations, even in 3D engines (such as drawing a scope lens or HUD).
It's worth noting that even with orthographic projections, there must still be a near and far plane, and objects must still lie between them to be visible.
Most matrix libraries have a function for producing an orthographic projection, and actually making use of the ortho matrix once created is identical to making use of a perspective matrix. Here's an example of instantiating an ortho matrix using gl-matrix
:
m = mat4.ortho(left, right, bottom, top, near, far);
Most people get hung up on exactly what the values should be. Truth is, you can use whatever values you want. You can use the range -1..1
if you prefer to keep things in unit space, which is useful if you want to scale your objects to the size of the window, or you can use the pixel coordinates of the canvas, which is sometimes useful for laying out user interfaces and the like. (And yes, you can use a near value of 0.)
Using a negative near plane value works, but can be a bit unwieldy since it technically means to allow objects behind the camera to be drawn. In a 2D environment, this may matter less. Doing this has its uses but is not common.
I'm not entirely sure whether objects will start getting culled out at the near plane or past it, so you should be cautious about using values directly on the near plane. To be safe, I try to always set the near plane at least 0.01 closer than the closest object will be.
Another thing regarding near and far values: they directly and drastically affect the accuracy of your depth buffer. Keep them as close together as possible. Obviously, this matters a whole lot more in 3D, but can come into play if you're doing a pseudo-2D scene, such as offsetting sprites from one another in layers.
Finally, one last note about doing 2D graphics in WebGL. There are some functions you'll want to call:
// Disable depth testing. This way, the last object drawn is always in "front".
// WebGL will not attempt to determine whether one object lies behind another.
gl.disable(gl.DEPTH_TEST);
// Alternatively, keep depth testing, but allow objects that share a plane with
// one another to overwrite each other. Your mileage may vary but the idea here
// is to be able to draw the scene in distinct layers, where objects in a given
// layer share the same plane but objects from one layer to the next can still
// take advantage of depth testing.
gl.depthFunc(gl.LEQUAL);