A sky is carried as a direction
Worth reading first: The divide is postponed, not avoided · A texture does not interpolate on the page.
A texture reaches the horizon as a rate solved the problem of a ground drawn to infinity. A repeating texture has no coordinate at a vertex that is a direction, but it has a rate — so many checks per metre along the direction — and a second number, a weight, that is one at points and zero at directions. Carried over the clip like every attribute and divided once at the end, that pair gave the ground’s own texture coordinate to three parts in half a pixel from the horizon.
The solution needed one vertex that was a point. The weight is interpolated across the triangle, and the division is by the interpolated weight; a triangle with one point in it has a weight that is positive everywhere except on the horizon itself. That essay ended on the case it could not reach. A sky drawn as a dome of triangles has every vertex at infinity. Each vertex is a direction with weight zero, the interpolated weight is zero at every pixel, and the rate-and-weight pair has nothing to divide by. The triangle is a piece of the plane at infinity, and on that plane there are no distances for a rate to be measured along.
It suggested an answer, and the answer holds exactly.
The attribute that belongs to a direction
What a sky texture is looked up by is a direction — which way the pixel looks — and the vertices of a sky triangle are directions. So the natural attribute is the direction itself, three numbers at each vertex.
The reason it works is the same linearity the rate used. A direction at a vertex is that vertex’s homogeneous coordinates with the fourth set to zero; the clip-space position is a linear function of them; and the rasteriser’s perspective-correct interpolation — divide each attribute by the vertex’s clip , interpolate straight across the page, and divide by the interpolated one-over- — is exact for any attribute that is linear in the vertex’s homogeneous coordinates. A texture does not interpolate on the page derived that recipe, and a direction is as linear as a coordinate can be.
The point on the sky a pixel sees is some combination of the three vertex directions — a direction too, with weight zero. Its projection is the pixel. So the interpolated vector, before any final division, is that combination scaled by the interpolated one-over-: it points exactly along the pixel’s own ray and has some length. The final division has nothing to divide by, and does not need to happen. The length is simply thrown away.
The figure draws the sky’s grid of azimuth and elevation both ways across a triangle spanning sixty degrees. Through the carried direction, every grid line lands where the pixel’s ray says it should, to degrees. Through the vertices’ own azimuth and elevation carried as values — the obvious alternative, if the sky is stored as an equirectangular map — the grid lines are bent and displaced, up to 7.7 degrees in azimuth near the top of the triangle.
Exact at every span, to the format’s digits
A claim of exactness is only worth its measurement, and here the measurement is simple: at every pixel inside the triangle, the angle between the interpolated direction and the ray that pixel looks along.
In double precision the direction is off by between one and five parts in of a degree, at every span from ten degrees to a hundred. That is rounding and nothing else: the error does not grow with the triangle, because nothing in the interpolation approximates. In single precision — the format a graphics processor interpolates in — it is four to six millionths of a degree, which is single precision’s own resolution on a unit vector, about radians.
That is the same answer the ground triangle gave. A point at infinity is an ordinary vertex found that the projection matrix draws a direction through the same multiplication as a point, and the rate essay found that a vertex at infinity is the best-conditioned vertex in the triangle, because its is a depth rate of order one rather than a depth of thousands of metres. A sky triangle is made of nothing but such vertices, and it is correspondingly well behaved.
The length that is thrown away
The one thing the direction does not give for free is a unit length, and it is worth seeing how far from one it is.
Across an eighty-degree triangle the interpolated vector’s length runs from 1.00 to 1.64, shaded dark to light: one at the vertex looking straight ahead, larger toward the vertices the camera sees obliquely, whose clip — their depth rate, the component along the view — is smaller. The length is exactly the quantity a texture’s rate would have been divided by, if the weight had not been zero; on a sky triangle it carries no information about the sky and has to be removed.
That is the practical warning. A sky looked up by the normalised direction — a cube map, which is indexed by direction and never by length — is exact. A sky looked up by some coordinate computed from the raw vector before normalising inherits the shading in the figure as an error: a lookup that takes the vector’s components as if they were unit, or a fog term computed from its length, is off by up to two-thirds across a wide triangle. Normalise first, and everything that follows is exact.
Angles are not linear in anything the pipeline carries
The obvious alternative — carrying each vertex’s azimuth and elevation and looking the sky up by the interpolated angles — fails for a reason that is the direction’s success turned inside out.
An angle is a nonlinear function of a direction — an arctangent of two of its components — and perspective-correct interpolation reproduces exactly only the functions that are linear. The angles at the three vertices are right, and the angles in between are the interpolation of three right values through the wrong function. The error is small near the vertices and largest in the interior, and it grows fast with the triangle’s size: 0.018 degrees for a triangle five degrees across, 0.098 at ten, 2.7 at forty and nearly twenty at eighty. Over the middle of that range the error grows as about the 2.4 power of the span — faster than the square, which is what the second-order part of an angle would give on its own, because the wider triangles reach higher toward the zenith, where azimuth changes fastest for a given change of direction.
In the currency a sky is stored in, that is a budget for tessellation. One texel of an equirectangular sky 4096 texels round is 0.088 degrees. To keep the angle lookup within a texel, the sky must be cut into triangles no wider than 9.6 degrees; at 16,384 texels, 5.4 degrees. A dome of such triangles is a few hundred to a few thousand of them for a sky that the direction attribute draws exactly with a handful.
The seam where azimuth wraps
A stored azimuth has a seam, and the angle lookup meets it.
Move the same forty-degree triangle from azimuth 150 degrees to straddle 180, where an equirectangular map’s azimuth runs from 180 back to minus 180. A vertex stored at 160 degrees and a vertex stored at minus 160 are twenty degrees apart on the sky, and three hundred and forty apart as numbers. Interpolated as numbers, the azimuth between them runs through zero — the long way round — and the triangle is painted with the sky from behind the viewer, 178 degrees out. The direction does not notice: degrees on both sides of the seam.
Renderers that store skies as angles handle the seam by duplicating vertices along it, each copy carrying the azimuth that suits its triangle, and handle the pole — where every azimuth meets — by splitting the triangles that touch it. Neither repair is needed for a direction. The seam and the pole are places in a parametrisation of the sky, and a direction is not a parametrisation; it is the sky.
The pole is the seam’s companion and fails the same way for the same reason. A triangle with a vertex at the zenith gives that vertex an azimuth, and any azimuth is as good as any other there: the stored value is arbitrary, and whatever is chosen is interpolated toward the other two vertices as if it meant something. The triangle’s interior near the pole is then painted with a fan of azimuths that belongs to no direction the pixels see. The widest triangles in the span figure are the ones that climb toward the zenith, which is where their extra error beyond the square comes from; a triangle that reaches the pole itself has an error set by an arbitrary choice rather than by its size.
Why the storage decides the error
The two lookups differ in where the sky’s parametrisation is applied, and the difference is the whole of the error. The direction lookup interpolates the one quantity the pipeline interpolates exactly and applies the parametrisation afterwards, at each pixel, to a direction that is already right. The angle lookup applies the parametrisation first, at the vertices, and then asks the interpolator to carry the result across the triangle — which it can do exactly only if the parametrisation is linear, and no map from directions to a flat rectangle is.
That is a statement about every stored sky, not only the equirectangular one. Six flat pictures of everything describes the cube map: six pinhole pictures on the faces of a cube, each a projective map from directions to its face. Within one face that map is linear in homogeneous coordinates, which is why a cube map can be indexed by a direction with no approximation — the lookup divides by the largest component and reads the face. What a 360-degree photograph actually is describes the equirectangular map, whose two coordinates are angles, and the kink at a seam the price a panorama pays where one face or strip hands over to the next. A sky carried as a direction is looked up the same way whatever its storage, and each storage’s own seams and poles are met pixel by pixel, where they cost nothing, rather than vertex by vertex, where they cost a triangle.
It also says what tessellation is for. A dome cut into triangles five degrees wide is not needed to draw the sky’s shape — a sky has no shape, only directions — but only to keep the angle lookup’s error within a texel. Carried as a direction, the sky can be a single enormous triangle, or the far plane of a frustum, or a quadrilateral covering the screen with its four corner rays as attributes; the last is how most renderers draw it, and it is exact for the reason measured here.
The far plane was never needed
A sky triangle is drawn through the same matrix the ground triangle was, the one with no far plane. One plane is nearly free found that removing the far plane costs the depth buffer almost nothing, and the divide is postponed, not avoided that the pipeline’s division by depth happens once, late. Both matter more for a sky than for a ground.
A renderer with a finite far plane cannot draw a vertex at infinity at all — every finite far plane clips it — so a sky is usually drawn as a large sphere at some finite radius, just inside the far plane. That sphere’s triangles have finite depths, a texture coordinate at each vertex, and every one of the problems measured here: a sphere drawn with per-vertex angles is a sphere looked up by the angle lookup. Drawn at infinity, the same sky needs no radius, no depth range, and no angles; its depth after the divide is exactly one, the far end of the buffer, and every opaque surface in the scene is in front of it by construction.
Where the rate and the direction meet
The two attributes, the ground’s rate and the sky’s direction, are one idea applied at two places on the plane at infinity.
The ground triangle had one vertex that was a point, and its texture needed the point’s value, the directions’ rates and a weight to divide by. The sky triangle has none, and its attribute needs no division. In both, the attribute is linear in the vertex’s homogeneous coordinates, and that is the entire condition for exactness. What differs is what the attribute is: on the ground, a coordinate that grows without bound toward the horizon, whose value at infinity is a rate; on the sky, a direction, whose value at infinity is itself.
A scene with both is a ground of rate triangles and a dome of direction triangles, and the two meet along the horizon. A direction lying exactly on the horizon is a vertex of both: as a ground vertex it carries a rate and a weight of zero, as a sky vertex it carries itself. Where parallel lines meet is the reminder that the horizon is a line in the picture and not an edge in the scene, and nothing about either triangle changes there except which attribute has meaning.
What was assumed
The sky is looked up by direction. A cube map, or a direction-indexed function, is exact under the direction attribute. An equirectangular sky can still be used — the angles are computed from the normalised direction at each pixel rather than interpolated — and that per-pixel computation is exact; what fails is interpolating angles computed only at the vertices.
No vertex lies behind the eye. A sky triangle wider than the field of view, with a vertex behind the camera, has a negative clip at that vertex and must be clipped first, in clip space, with its direction attribute carried through the clipper’s interpolation like any other. The rate essay found that a clipper which drops the weight breaks the ground’s texture; a clipper that carries the direction as three plain attributes is correct automatically, since the direction needs no weight.
The camera does not move between vertices. Every direction here is seen from one eye. A sky seen from an eye that has moved is the same sky — directions do not have parallax — which is why a sky is drawn without translation, and why a direction attribute needs no knowledge of where the eye is.
Still open: whether the ground and the sky meet without a crack
The meeting described in the section before last has not been measured. A ground triangle and a sky triangle that share an edge along the horizon share two vertices, both directions with equal to their depth rates, and a rasteriser decides which pixels along that edge belong to which triangle by the same edge function computed from the same vertex positions. In exact arithmetic the two triangles tile the edge with no gap and no overlap. In single precision the vertices’ pixel positions are rounded, and a vertex at infinity reaches the page through a division by its depth rate like any other.
The measurement that settles it draws a ground of rate triangles and a dome of direction triangles sharing the horizon, rasterises both with the vertex positions rounded as a graphics processor rounds them, and counts the pixels along the horizon covered by neither triangle or by both — and, for the pixels the ground triangle claims within a fraction of a pixel of the horizon, how large the texture coordinate becomes as the interpolated weight approaches zero. If no pixel is lost or doubled and the ground’s coordinate stays finite on every pixel it owns, the horizon is only a place where one attribute hands over to another; if pixels fall into a crack, the horizon is a seam of its own that a renderer must close.
Shares its objects with
Essays that name at least two of the same things, and that neither author linked.
- What happens behind the eye — both name clip space, homogeneous coordinates, point at infinity, projective map
- A point and a line are one object — both name homogeneous coordinates, point at infinity, projective map
- A line is a space of its own — both name homogeneous coordinates, point at infinity
- An angle is a cross-ratio — both name homogeneous coordinates, point at infinity
- Desargues read the other way — both name homogeneous coordinates, point at infinity
- Five marks and the sixth — both name point at infinity, projective map
Named objects
A flat tag is an object no other essay names yet.
Clip spaceCube mapEquirectangularHomogeneous coordinatesPlane at infinitypoint at infinityProjective map