What a machine computes

Along a line of constant depth the page is affine

Stepping a texture by a constant amount per pixel is wrong across a receding floor and exactly right along any line of it that stays at one depth — and on every plane those lines run parallel to its own vanishing line. Turn a 120 px span 1° away from that direction and it is 0.79 px out; roll the camera a hundredth of a degree and a floor drawn to 30 m is out by 0.69 px on its worst scanline.

Worth reading first: The divide is postponed, not avoided · Dividing depth by eye.

A texture does not interpolate on the page found that stepping a surface’s attributes by a constant amount per pixel is wrong by a closed form, (k1)/(k+1)(\sqrt{k}-1)/(\sqrt{k}+1) of the span with kk the depth ratio between its ends, and that the only thing it depends on is that ratio. Its control was a wall square to the camera, where the ratio is one and the error vanishes, and it called that case the reason the bug survives testing.

The control is larger than a wall. A ratio of one needs the two ends of a span at the same depth, not the whole surface. Every plane in front of a camera, however it is turned, is crossed by a family of lines whose points all share a depth, and along any of them a constant step per pixel is exactly right. That changes the question from whether a renderer must divide to which way it walks.

Along the horizon's direction the page is affine; across it, it is notSpans 120 px long on a floor seen from 1.5 m up with a 60° field, centred 80 and 160 px below the horizon and turned away from the horizon's direction. Parallel to the horizon every point of a span is at one depth, and stepping linearly along it is exact: 0 px. Turned by 1° the worst texel is 0.79 and 0.39 px out; by 10°, 7.8 and 3.9 px; across the horizon, at 90°, 54.2 and 23.4 px. Each is measured along its span and matches (√k − 1)/(√k + 1) of the span's length, for the depth ratio k at its ends, to a part in a thousand.0.25°0.5°10°20°45°90°0.1110angle of the span from the horizon's direction (log scale)worst texel along a 120 px span (px, log scale)80 px below the horizon160 px below the horizonparallel to the horizon: 0 pxfloor · eye 1.5 m · 60° field
Fig. 1 Spans 120 px long on a floor seen from 1.5 m up with a 60° field, centred 80 and 160 px below the horizon and turned away from the horizon’s direction. Parallel to it, stepping linearly is exact, 0 px. Turned by 1° the worst texel is 0.79 and 0.39 px out, by 10° 7.8 and 3.9 px, and at 90° 54.2 and 23.4 px.

Why one depth means no division

A point at camera depth zz lands at u=cx+fx/zu = c_x + f\,x/z across the page and v=cy+fy/zv = c_y + f\,y/z down it. Hold zz fixed and both are affine in the point’s position: walking the world line at a constant rate walks its image at a constant rate. The division by depth is still there, but it is division by one number for the whole line, and a single number can be divided out once before the walk begins.

That is the whole of the exactness. The postponed divide the divide is postponed described exists because a varying depth turns equal steps in the world into unequal steps on the page; where the depth does not vary, there is nothing to postpone.

So the question is where those lines are. A plane meets each plane of constant depth — each plane parallel to the picture — in a straight line, and that line is parallel to the picture too. A line parallel to the picture has no vanishing point; its image is parallel to itself. And every such line on one plane is parallel to every other, so their images are all parallel on the page, in the direction of the plane’s own vanishing line — the image of the plane’s line at infinity, which passes through the vanishing point of every direction the plane contains, where parallel lines meet.

For a floor seen by a level camera that direction is horizontal, and the lines of constant depth are the page’s rows. Every row of a floor is at one depth; the depth changes only from row to row, as the eye’s height times the focal length over the row’s distance below a horizon that sits, for a level camera, at eye level.

How fast a turned span fails

The first figure holds a span’s length at 120 px, centres it on the floor, and turns it. Parallel to the horizon the worst texel is out by nothing at all. Turn it by a quarter of a degree and it is not nothing.

At 80 px below the horizon, 1° of turn puts the worst texel 0.79 px out; 10°, 7.8 px; a span standing straight across the rows, 54.2 px. At 160 px below, each is about half: 0.39, 3.9 and 23.4. On logarithmic axes the small-angle end is a straight line of slope one: the error grows in proportion to the turn.

The proportion has a simple form. A span of length LL turned by a small angle α\alpha reaches Lα/2L\alpha/2 above and below its centre, and at a distance hh below the horizon that is a depth ratio of about 1+Lα/h1 + L\alpha/h. Put that into the closed form and the worst texel is close to

L2α4h.\frac{L^2\,\alpha}{4h}.

For 120 px at 80 px below the horizon and 1° — 0.01745 of a radian — that is 0.785 px, and the figure measures 0.79. The error grows with the square of the span, in proportion to the turn, and in inverse proportion to the distance below the horizon, which is to say in proportion to the depth. A long span near the horizon is the one that cannot afford to be turned.

Every plane has its own direction

Horizontal is the floor’s answer, not a general one.

Each plane is exact along its own vanishing line, and only thereThe same 120 px spans, centred 100 px below the horizon, on a floor and on a floor banked 20° about the line of sight, turned through every direction on the page. Each has exactly one direction in which stepping is exact — the direction of its own vanishing line, 0.0° for the floor and 20.0° for the banked floor, where the worst texel is 7e-14 px. Along the page's rows the floor is exact and the banked floor is 13.26 px out; along the banked floor's direction the level floor is 12.45 px out.0.010.1110100-90-4504590direction of the span on the page (degrees from a row)worst texel along a 120 px span (px, log scale)exact at 0.0°exact at 20.0°floorfloor banked 20°exact along 0.0° and 20.0°eye 1.5 m · 60° field
Fig. 2 The same 120 px spans, centred 100 px below the horizon, on a floor and on a floor banked 20° about the line of sight, turned through every direction. Each is exact only along its own vanishing line — 0.0° and 20.0° — where the worst texel is 7e-14 px. Along a row the banked floor is 13.26 px out; along 20.0° the level floor is 12.45 px out.

Bank the floor 20° about the line of sight, as a road’s camber or a tilted deck would be, and its vanishing line tips by the same 20° on the page. The figure turns the same 120 px span through every direction on both surfaces. Each has exactly one direction in which the span is exact, to 7×10147 \times 10^{-14} px, and it is that surface’s own vanishing line: 0.0° for the level floor, 20.0° for the banked one. The page’s rows, exact on the level floor, are 13.26 px out on the banked floor. A span along the banked floor’s direction, exact there, is 12.45 px out on the level floor.

The same statement covers walls. A wall running away from a level camera contains the vertical direction, which is parallel to the picture, so its vanishing line is vertical and its lines of constant depth are the page’s columns. A floor is exact in rows; a wall beside it is exact in columns; a sloping ramp is exact along lines tipped by its own slope. Nothing about the page’s grid is privileged. What is privileged is each surface’s vanishing line, which is a property of the surface and the camera together.

This is not new to rendering. Alberti draws a pavement by dividing a baseline evenly and running orthogonals to the centric point, then placing the transversals — the lines across the pavement, parallel to the picture. Each transversal is a line of constant depth, and the orthogonals cross it at equal intervals, because along it the page is affine. The painter never divides a transversal by eye: equal tiles along it are equal on the page. Every error dividing depth by eye measured lives in the other direction, across the transversals.

A rolled camera has nothing to walk along

A renderer that draws floors row by row has made an assumption about the camera, and the next figure prices it.

A rolled camera's scanlines cross the floor's lines of constant depthEvery second scanline of a 690 × 400 frame, stepped linearly across the floor out to 30 m, from a camera 1.5 m up with a 60° field rolled about its axis. Level, the worst texel on any scanline is 0 px. Rolled 0.01°, 0.03°, 0.1°, 0.3°, 1°, 3°, 10°, it is 0.69 px, 1.95 px, 6.49 px, 19 px, 58 px, 135 px, 261 px. A pixel of error is passed between 0.01° and 0.03°.0.01°0.03°0.1°0.3°10°131030100300roll of the camera about its axis (log scale)worst texel on any scanline, floor to 30 m (px, log scale)1 pxlevel: 0 px · 1°: 58 pxfloor to 30 m · eye 1.5 m · 60° field
Fig. 3 Every second scanline of a 690 × 400 frame stepped across the floor out to 30 m, the camera 1.5 m up with a 60° field and rolled about its axis. Level, the worst texel is 0 px; rolled 0.01° it is 0.69 px, 0.03° 1.95 px, 1° 58 px and 10° 261 px.

Level, every scanline across the floor is a line of constant depth, and the worst texel on the frame is out by nothing. Roll the camera about its axis and the floor’s vanishing line tips while the scanlines stay horizontal, so every scanline now crosses the lines of constant depth. At a hundredth of a degree the worst scanline is out by 0.69 px; at three hundredths, 1.95 px; at a tenth of a degree, 6.49; at one degree, 58; at ten, 261.

The sensitivity is extreme because the worst scanline is the worst case of the formula above at once. It is the whole frame wide — 690 px, not 120 — and it runs into the far limit of the floor, where the distance below the horizon is smallest. The span is squared and the distance is in the denominator, and both have been taken to their limits by the frame itself.

How little roll a floor survives

The tolerance can be written down, and it has no free constants.

The deeper the floor is drawn, the less roll its scanlines surviveThe largest roll at which every scanline of the frame stays within 1 px, for a floor drawn to 10, 30, 100, 300 m from a camera 1.5 m up with a 60° field: 0.043°, 0.014°, 0.0043°, 0.0014°. The small-angle estimate 4fH/(L²Z), with f the focal length in pixels, H the eye's height, L the frame's width and Z the far limit, gives 0.043°, 0.014°, 0.0043°, 0.0014°. The far limit lies 90, 30, 9, 3 px below the horizon, and the measured tolerance is 1.00, 1.00, 1.00, 1.00 times the estimate.floor to 10 m, measured0.043°floor to 10 m, 4fH/(L²Z)0.043°floor to 30 m, measured0.014°floor to 30 m, 4fH/(L²Z)0.014°floor to 100 m, measured0.0043°floor to 100 m, 4fH/(L²Z)0.0043°floor to 300 m, measured0.0014°floor to 300 m, 4fH/(L²Z)0.0014°within 1 px on every scanlineeye 1.5 m · 60° field · 690 px wide
Fig. 4 The largest roll at which every scanline stays within 1 px, for a floor drawn to 10, 30, 100 and 300 m: 0.043°, 0.014°, 0.0043° and 0.0014°. The estimate 4fH/(L²Z) gives the same, 1.00 times the measured tolerance at every depth, with the far limit 90, 30, 9 and 3 px below the horizon.

A floor drawn to a far limit ZZ has its last row fH/ZfH/Z below the horizon, with ff the focal length in pixels and HH the eye’s height. A roll θ\theta tips a full-width scanline of length LL across LθL\theta of page height beside it, and setting L2θ/4hL^2\theta/4h to one pixel gives

θ=4fHL2Z.\theta = \frac{4fH}{L^2 Z}.

With a 597.6 px focal length, an eye 1.5 m up and a 690 px frame, a floor drawn to 10 m tolerates 0.043° of roll; to 30 m, 0.014°; to 100 m, 0.0043°; to 300 m, 0.0014°. The figure finds each by bisecting on the roll over every scanline of the frame, and the measured tolerance is the estimate at every depth to the second decimal — even at 300 m, where the far limit is only 3 px below the horizon and the frame’s last floor row is nearly on it.

The formula says what the tolerance is sensitive to. It falls with the square of the frame’s width, so a frame twice as wide tolerates a quarter of the roll. It falls in proportion to how far the floor is drawn. And it rises with the eye’s height, because a higher eye puts the far floor further below the horizon. None of these moves the tolerance out of the hundredths of a degree for any frame a person would look at. A renderer that walks floors in rows does not need its camera nearly level; it needs it level.

The formula also says the tolerance is not really about degrees. Multiply it by the frame’s width and it becomes the rise of one end of the frame over the other that a roll produces: Lθ=4Hf/(LZ)L\theta = 4H f/(LZ). For a fixed field of view f/Lf/L is fixed — 0.866 for a 60° field — so the rise depends only on the eye’s height over the far limit, and not on how many pixels the frame has. A floor drawn to 10 m survives a roll that lifts one side of the frame 0.52 px above the other; to 30 m, 0.17 px; to 300 m, under a fiftieth of a pixel. A frame of four thousand pixels tolerates a smaller angle than one of 690, and exactly the same fraction of a pixel of rise. The condition is not that the camera be nearly level to some precision of angle; it is that the scanlines not be seen to climb, by less than a pixel, from one side of the picture to the other.

Looking up and down without tipping the camera

Roll is not the only way to spoil a line of constant depth. Tipping the camera to look down spoils a different one.

Tipping the camera spoils a wall's columns; moving the principal point spoils nothingA wall 2 m to one side, stepped down each column from eye height to the floor at 5, 7, 10, 14, 20 m ahead, by a camera 1.5 m up with a 60° field looking down. Tipping the axis down by 1, 2, 5, 10° puts the worst texel 0.23, 0.46, 1.14, 2.27 px out. Looking down by the same angles by moving the principal point up the frame leaves every column exact, 0 px. The floor's rows are exact either way, 0 px.0°, axis tipped0 px0°, principal point moved0 px1°, axis tipped0.23 px1°, principal point moved0 px2°, axis tipped0.46 px2°, principal point moved0 px5°, axis tipped1.14 px5°, principal point moved0 px10°, axis tipped2.27 px10°, principal point moved0 pxthe floor's rows: 0 px either waywall columns · eye 1.5 m · 60° field
Fig. 5 Columns of a wall 2 m to one side, from eye height to the floor at 5 to 20 m ahead. Tipping the camera’s axis down 1, 2, 5 and 10° puts the worst texel 0.23, 0.46, 1.14 and 2.27 px out; looking down by moving the principal point leaves every column at 0 px, and the floor’s rows are 0 px either way.

Tip the axis down and the floor’s rows survive. A tilt turns the camera about its own horizontal axis, and a line on the floor parallel to that axis keeps every point at one depth whatever the tilt, so its image is still a line of constant depth, still a row. The figure confirms it: the floor’s rows are exact at every tilt.

A wall’s columns do not survive. A vertical line on the wall has its top and bottom at different depths once the axis is tipped, and its image is no longer a column but a line leaning toward a vertical vanishing point below the frame. Stepping down it linearly puts the worst texel 0.23 px out at 1°, 0.46 at 2°, 1.14 at 5° and 2.27 at 10°.

There is a second way to look down, and it costs nothing. Keep the camera’s axis level and move the principal point up the frame instead, so that the frame shows more of what is below the horizon and less of what is above. Every line of constant depth is exactly where it was, because nothing about the camera’s orientation has changed — the frame is simply a different window onto a level camera’s picture plane. The figure measures every column at 0 px at every angle, and the floor’s rows likewise.

For a drawing, that is the separation the centre of the picture is not the centre of the paper makes between the point where the eye’s axis meets the picture and the middle of the sheet: move the sheet and not the axis, and a tall building drawn from its foot keeps its verticals parallel. For a camera it is a shifted lens, and the principal point is not the centre is the same fact read back from a photograph. It is not the same picture as a tipped camera’s. Straightening does not move the eye found that a tilt is a homography of the level picture; that homography keeps the floor’s rows as rows but carries a wall’s columns into lines that lean. A shifted principal point is a translation of the picture, and a translation carries every row to a row and every column to a column.

What each choice buys

Put the three pieces together and the picture a renderer can draw without dividing per pixel is exactly described.

Floors and ceilings, level, stepped in rows — and a ceiling costs nothing extra, because a plane parallel to the floor has the floor’s line at infinity and so the same vanishing line, the horizon, however high above the eye it is. Its rows run above the horizon rather than below it, at depths set by its own height over the eye instead of the eye’s height over the floor, and they are lines of constant depth for the same reason. Walls, vertical, stepped in columns. A camera that turns about the vertical freely, because that turn keeps floors’ rows at constant depth and walls’ columns vertical; that never rolls, because a hundredth of a degree of roll costs the far floor a pixel; and that looks up and down by moving the principal point rather than by tipping, because a tip spoils the columns and a shift does not. The price of that last choice is visible in the picture: verticals never converge, and a view straight down a tall wall looks like an architect’s elevation rather than a photograph.

Outside that description every surface needs its depth at every pixel. The division a texture does not interpolate on the page derived — interpolate u/zu/z and 1/z1/z and divide at the end — is exact for every surface in every direction, and it is what a renderer does once a division per pixel is cheap. The constant-depth walk is exact only on its own lines. But on them it is exact in exactly the same sense, to the rounding of a double, and its cost is one division per line rather than one per pixel — a 690-pixel scanline divided once instead of 690 times.

What the two routes share is the reciprocal of depth. The per-pixel route interpolates it because it is what the page is linear in, the fact depth is a reciprocal stated for a pair of cameras. The constant-depth route holds it fixed along a line so that it does not need interpolating. Both are ways of never stepping the depth itself across the page.

What this does not settle

Planes only. A curved surface has lines of constant depth, but they are curves on the page, and stepping along a curve is not stepping along a scanline or a column. Nothing here measures how a curved surface fares.

Continuous spans. Every span is a line segment with exact endpoints. A renderer draws pixels, and a span along a tilted direction visits them in a staircase; the error of snapping such a span to the pixel grid is a different error from the one measured here, and was not drawn.

Position, not filtering. The texel error is where a texel lands. Which texels are blended to colour a pixel, and how the blend changes along a span as the texture’s footprint shrinks with depth, is a sampling question outside this geometry.

Still open: a banked floor stepped along tilted spans

The banked floor is exact along lines tipped by its bank, so a renderer can walk it without division by stepping along those tilted lines instead of along scanlines. The page, though, is made of rows. A span at 20° crosses a new row every three pixels, and neighbouring spans must tile the surface between them without gaps or double-drawn pixels.

The question that leaves is what that staircase costs: whether snapping tilted constant-depth spans to the pixel grid leaves a texel error smaller than the 13.26 px a row-walk makes on the same banked floor, how that error depends on the bank angle and the depth, and at what bank a per-pixel division is cheaper than the bookkeeping of tilted spans.

Shares its objects with

Essays that name at least two of the same things, and that neither author linked.

Named objects

A flat tag is an object no other essay names yet.

Depth divisionForeshorteningHomogeneous coordinatesHorizonPrincipal pointProjective invariantShift lensTransversalVanishing line