# Why does OpenGL require all coordinates in [-1,1] (NDC)?

Why shouldn't the z coordinate be normalized to the [0,1] range instead of [-1,1]? As I understand it, that happens some time after anyways, so what's the catch with this?

The question could very easily be turned around: why the [0, 1] range? What makes that range special?

NDC space is a cube; every component is on the [-1, 1] range. So it's very nice, neat, and uniform. That's why OpenGL uses that space. It's simple, obvious, and very regular.

What you suggest is to turn that nice, neat, uniform cube into a rectangular prism. There is nothing to gain from such a space. Why be irregular when you can be regular?

As I understand it, that happens some time after anyways

No, it does not. The transform from NDC space to window space uses the current glDepthRange setting. That may be [0, 1], and that's what it is by default. But there's no rule that it must be. It can be anything you want, so long as both values are between 0 and 1. You can even reverse the near and far, using a range of [1, 0].

• Another good thing is that with a [-1, 1] range it's easier to figure out a negative/opposite direction compared to a [0, 1] range when you need to manipulate something. (At least I find it easier) – Darkwings May 13 '12 at 12:43
• I would add that you have an extra bit of information, so before depth value is saved into the 24 bit buffer it could have 1 more right bit in the pre-final calculation (does not matter that much :D) – CoffeDeveloper Feb 13 '15 at 11:43
• "why the [0, 1] range?" ... "It can be anything you want, so long as both values are between 0 and 1." lol so then the question is, why glDepthRange clamps the depth range between 0 to 1? ;) checkmate. maybe cause this range gets scaled to unsigned depth bits max value perhaps? ;) just like with color. lol you could've also tried explaining why directx doesn't do this unneeded mapping. obviously the reason was just to make it a symmetrical cube. there's no loss since they remap via glDepthRange anyway. there's nothing "irregular" about mapping directly to what's more useful. lol – Puddle Dec 3 '19 at 0:01
• @Puddle: "obviously the reason was just to make it a symmetrical cube." Isn't that what I said when I said that NDC space was a cube? I'm not sure I understand your comment here. – Nicol Bolas Dec 3 '19 at 2:23
• what someone wants to know is, was there any practical reason for it? or was it simply to have this neat (but unneeded) uniform space? we know it comes at no extra cost, since they assume someone may want to change the depth range. (which most probably don't. and could do manually at projection. hence glDepthRange could be deprecated) if they never implemented glDepthRange, it'd be more efficient to map directly to the needed range [0,1]. to answer "why the [0,1] range?" because that is the depth range. why [-1,+1]? in any practical sense? ;) – Puddle Dec 3 '19 at 9:16

There's one massive advantage to NDC's Z being defined as [0,1] and it has to do with floating point precision.

Since about 2006, the standard advice from GPU vendors to avoid z-fighting has been to use a projection matrix that maps the far plane to z=0 and the near plane to z=1 and to use a floating point depth buffer.

Perspective projection results in a hyperbolic precision density that favours the near plane (which results in terrible precision beyond about near*2). Floating point formats have logarithmic precision density that favours 0. Combining these two facts with the above mentioned "backwards" projection matrix results in those two curves almost cancelling out, giving approximate linear precision density across the entire Z range, without sacrificing performance or interpolation quality (unlike some other linear/logarithmic depth buffer schemes).

Unfortunately this trick (which is now standard practice) does not work with OpenGL's "nice, symmetrical" NDC coordinate system -- you have to enable a GL extension to opt in to the NDC definition used by every other sane graphics API.

• The GL extension Brooke's referring to is, I believe, glDepthRangedNV, which is currently supported by both NVidia and AMD GPU drivers, but not by Intel. – Trevor Powell Sep 30 '16 at 1:30
• It's ARB_clip_control; by the time of the depth range transformation, the precision would already have been lost. For more information see for example this NVidia blog entry. – Buster May 21 '17 at 8:15
• @Buster: It's in core OpenGL as of 4.5. – Tim Čas Jul 13 '17 at 12:50