What a machine computes

A tile is an off-centre frustum

Rendering a picture in tiles is exact, and the way to do it is one line of arithmetic: a tile's sides are the whole frustum's sides read at the tile's own pixel bounds. Aiming the camera at each tile instead is defensible at every step and is a different picture, out by about a tenth of a tile whatever the tile size.

Worth reading first: Four numbers and a window · A pixel is not a point.

A picture too large to render at once is rendered in pieces and the pieces are joined. There are two obvious ways to do it and one of them is exact.

Cutting each tile out of the frustumThe same tiling with each tile's sides read straight out of the whole frustum — the direction a pixel u looks along is (u − pₓ)/f, so a tile's sides are the frustum's sides at the tile's own bounds. The reassembly is exact to 1e-13 px over 43 points: same eye, same focal length, a different window.worst 1e-13 px outcorrect from 17 cm, at 160 mm wide3 × 3 tiles
Fig. 1 A picture cut into nine tiles, with each tile’s sides read straight out of the whole frustum. Every point lands where the whole picture puts it.

Which of the two is used decides whether the join is invisible or merely inconspicuous, and the difference does not show up in any single tile.

A frustum is four numbers and a windowThe camera's own section, looking down on it. The two slanted lines are the left and right sides: divide either by the near plane and it is a direction, -0.6285 and 0.3041, which between them carry the focal length 739.9 px and the principal point 465. The two crossbars are the near and far planes, and moving either of them moves no mark in the picture at all.the eyenear 0.90 mfar 4.20 mfocal 740 pxprincipal 465, 210
Fig. 2 What a tile’s frustum is: the same camera with its principal point somewhere other than the middle of the rectangle being drawn.

The exact way is one line

The direction a pixel u looks along has x/z = (u − pₓ)/f. That is the whole of the camera’s lateral geometry, and it is a statement about a pixel rather than about a picture — so a tile’s sides are the frustum’s sides evaluated at the tile’s own pixel bounds:

left=nearx0pxf,right=nearx1pxf\text{left} = \text{near}\cdot\frac{x_0 - p_x}{f}, \qquad \text{right} = \text{near}\cdot\frac{x_1 - p_x}{f}

and the same for top and bottom. Same eye, same focal length, same near plane; a different window.

The tiles then reassemble to the last bit. Over a scene of points spread across the frame, the largest disagreement between a tile’s answer and the whole picture’s is about a part in ten thousand million million of a pixel, which is two floating-point routes to one number.

That exactness is not a numerical accident and it is worth saying why. The composition of the tile’s matrix with the tile’s viewport is the same affine map of the same ray as the composition of the full matrix with the full viewport. The two matrices differ; the two viewports differ; the products do not.

Straightened — and taken from exactly where it wasThe tilted picture is drawn thin and the corrected one over it. The correction is built from the picture alone: where the imaged verticals meet, and the focal length. What comes out agrees with a level camera at the same eye — one the correction was never shown — to 3e-13 px, and its verticals are parallel to 0e+0°. What has not changed is the eye: the cross-ratio of four points along a ground line reads 1.3333 before and after, so every measurement the original supported the corrected one supports, from the same place and no other.corrected from the picture — 0e+0° between the verticalscorrect from 20 cm, at 160 mm wideagrees with a level camera at the same eye to 3e-13 px
Fig. 3 The same fact from the other end: a keystone correction is a homography between two picture planes and does not move the eye. Rotating a camera and cropping its picture are related by exactly that map.

The other way is a rotation

The alternative is the one a person reaches for without a frustum in mind: keep the eye where it is, aim the optical axis at the centre of each tile in turn, and give each camera a field of view wide enough to cover its tile.

Every step of that is defensible. The eye has not moved, so nothing about the scene’s perspective has changed. The field of view is computed from the tile’s own angular extent rather than guessed. And the tiles are the right size, so they abut.

Aiming the camera at each tileThe picture cut into 3 × 3 tiles, each rendered by a camera at the same eye turned to face the tile with a field of view that covers it. Every step of that is defensible and the result is a different picture: points land up to 12.0 px from where the whole picture puts them, with the lines drawn from each point to where its tile sent it. A rotation is not a crop.worst 12.0 px outcorrect from 17 cm, at 160 mm wide3 × 3 tiles
Fig. 4 The same tiling with the camera aimed at each tile instead. Each point is drawn where the whole picture puts it, with a line to where its tile sent it.

It is a different picture, and by a lot. The worst displacement across the same scene is about twelve pixels at three tiles by three, with a median of about three, on a picture six hundred and ninety pixels wide. Points near a tile’s centre are fine and points near a corner are not.

The reason is the one this site has already stated in another vocabulary: a rotation is not a crop. Straightening does not move the eye is about the same fact from the other end, where a keystone correction is a homography between two picture planes rather than a change of viewpoint. Rotating a camera and cropping its picture are related by a homography, and a homography is the identity only when the rotation is zero.

Cutting each tile out of the frustumThe same tiling with each tile's sides read straight out of the whole frustum — the direction a pixel u looks along is (u − pₓ)/f, so a tile's sides are the frustum's sides at the tile's own bounds. The reassembly is exact to 1e-13 px over 43 points: same eye, same focal length, a different window.worst 1e-13 px outcorrect from 17 cm, at 160 mm wide4 × 4 tiles
Fig. 5 Sixteen tiles cut out of the frustum. Still exact, because the construction does not care how many pieces the window is cut into.

What the two constructions differ by, exactly

The difference between the two pictures has a name, and having the name makes the size of the error predictable rather than measured.

Two pinhole cameras at the same eye, differing by a rotation R, produce pictures related by the homography K·R·K⁻¹ — where K carries the focal length and principal point. That is the standard result and this site uses it elsewhere: a keystone correction is one of these, and the panorama field is built on them.

So the rotated tile’s picture is the correct tile’s picture with a homography applied, and the homography is the identity only when R is. Near the fixed point of that homography — the direction along which the rotation acts trivially, which is the tile’s centre — the displacement is second order in the angular offset, which is why the middle of every tile is clean. At the tile’s edge the offset is the tile’s own half-angle and the displacement is proportional to it times the rotation.

Both factors are the reason subdividing fails to help in proportion. Halving the tile halves its half-angle, which halves the displacement — and it also halves the tile, so the ratio is unchanged. The two shrink together because they are the same quantity.

Aiming the camera at each tileThe picture cut into 2 × 2 tiles, each rendered by a camera at the same eye turned to face the tile with a field of view that covers it. Every step of that is defensible and the result is a different picture: points land up to 15.7 px from where the whole picture puts them, with the lines drawn from each point to where its tile sent it. A rotation is not a crop.worst 15.7 px outcorrect from 17 cm, at 160 mm wide2 × 2 tiles
Fig. 6 Four tiles by the rotated method, where the rotations are largest and the displacements are worst in absolute terms.

Subdividing does not fix it

The natural response is that the tiles are too big. Make them smaller and the rotation per tile is smaller and the error goes away.

The absolute error does fall. From two tiles across to twenty-four, the worst displacement drops from about twenty-five pixels to about three.

The error is a fixed fraction of a tileThe rotated-camera tiling's worst displacement, divided by the tile it happened in. From 2 × 2 to 24 × 24 it stays between 7.3% and 10.9% — so the absolute error falls only as fast as the tile does, and reaching half a pixel needs tiles a few pixels across, which is a few thousand renders of the same scene.05105101520tiles across the pictureworst error, as a percentage of a tile's widthflat in proportion25 px → 3.1 px absolute
Fig. 7 The rotated-camera tiling’s worst displacement divided by the tile it happened in, at eight subdivisions. It is flat.

As a fraction of a tile it does not move at all: between about seven and eleven per cent, everywhere from two by two to twenty-four by twenty-four. So the error falls only as fast as the tile does, and reaching half a pixel needs tiles a handful of pixels across — which is thousands of renders of the same scene, each one with its own camera setup, to avoid a construction that was one line of arithmetic.

This is the collection’s own convergence gate in miniature, and it is why that gate exists. “The error gets smaller under subdivision” is true and is a statement about the units. Whether a quantity is converging is a question about the ratio it is being measured in, and a number that shrinks in pixels while holding steady in tiles has not been fixed by anything.

Aiming the camera at each tileThe picture cut into 6 × 6 tiles, each rendered by a camera at the same eye turned to face the tile with a field of view that covers it. Every step of that is defensible and the result is a different picture: points land up to 6.0 px from where the whole picture puts them, with the lines drawn from each point to where its tile sent it. A rotation is not a crop.worst 6.0 px outcorrect from 17 cm, at 160 mm wide6 × 6 tiles
Fig. 8 Thirty-six tiles by the same method. Every rotation is smaller and the fraction of a tile the error occupies has not moved.

What tiling is for

Three things want a picture in pieces, and the construction is the same for all of them, which is part of why it is worth getting right once.

A picture too large for one pass. A print at three hundred dots per inch on a sheet a metre across is tens of thousands of pixels on a side, and the memory a renderer needs grows with the area. Cutting it into tiles trades passes for memory and nothing else — provided the tiles are exact, because a print is the one medium where a seam is permanent.

A picture split across machines. The same arithmetic, with the tiles rendered in different places. Here the exactness matters for a second reason: the machines have to agree without communicating, and they do, because each one is computing a crop of a picture that is fully determined by numbers all of them have.

A picture split across a wall of displays. Several projectors or panels showing one image, each with its own frustum. This is the case where the naive method is most tempting, because each projector really is aimed somewhere different — and it is still wrong, because the projectors’ lenses are aimed differently and the virtual camera should not be. The eye the picture is drawn for is one eye, in one place, and the wall is one window cut into several pieces.

The third case is the one where the error is most visible, since a wall of displays puts its seams in the middle of the picture rather than at the edges. A line crossing a seam between two naively rendered panels kinks by a few pixels, and the kink is in the part of the image a viewer is looking at.

Cutting each tile out of the frustumThe same tiling with each tile's sides read straight out of the whole frustum — the direction a pixel u looks along is (u − pₓ)/f, so a tile's sides are the frustum's sides at the tile's own bounds. The reassembly is exact to 1e-13 px over 43 points: same eye, same focal length, a different window.worst 1e-13 px outcorrect from 17 cm, at 160 mm wide2 × 2 tiles
Fig. 9 Four tiles cut correctly, for comparison at the same subdivision. There is nothing to draw between the two sets of marks, because there is no displacement to draw.
A frustum is four numbers and a windowThe camera's own section, looking down on it. The two slanted lines are the left and right sides: divide either by the near plane and it is a direction, -0.2771 and 0.6555, which between them carry the focal length 739.9 px and the principal point 205. The two crossbars are the near and far planes, and moving either of them moves no mark in the picture at all.the eyenear 0.90 mfar 4.20 mfocal 740 pxprincipal 205, 210
Fig. 10 And a tile on the other side of the picture, whose principal point is off its rectangle the other way.

Where the error is, and why it is invisible

The displacement is zero at every tile’s centre, by construction: the tile’s camera is aimed along the ray through that point.

It grows toward the tile’s edges, and it grows with how far off-axis the tile is — so the worst place in the whole picture is the outer corner of a corner tile, where a large rotation and a large angular offset compound. In the middle tile of a nine-tile grid the error is small everywhere, because the rotation is zero there and the tile is only a crop.

Which makes the failure hard to see in the ordinary way. Look at any single tile and it is a perfectly good picture — straight lines are straight, the perspective is consistent, nothing is distorted. The defect appears only at the seams, as a discontinuity where two tiles disagree about where a line crosses their shared edge, and even then it appears most strongly at the corners of the picture where a viewer is least likely to be reading detail.

A tiled render assembled this way looks right, has straight lines that kink slightly at tile boundaries, and gets worse toward the edges of the frame. Every one of those symptoms would be read as an artefact of something else.

The cost of assuming the principal pointThe two-vanishing-point route to a focal length needs a principal point supplied, and every textbook supplies the middle of the frame. On a shifted or cropped picture that is wrong, and f² = −(v₁ − p)·(v₂ − p) turns the error into a product of two large numbers: 1.6% at 150 px of shift. The three-point recovery does not assume it and has no such error.00.50011.50050100150how far the principal point really is from the middle of the frame (px)error in the focal length, from assuming it is not (%)unshifted: the assumption is truea 44° lens1.55% at 150 px of shift
Fig. 11 What happens when a recovery assumes the principal point is the image centre on a shifted frame. Every tile except the middle one is a shifted frame.

What the correct construction is not doing

It is worth being clear that the exact tiling is not a trick or a special case. It is what happens when the camera is left alone.

A tile’s frustum has all four sides on the same side of the axis for a corner tile — a frustum entirely off-axis, which four numbers and a window notes is perfectly legitimate and has no field of view in the usual sense. There is no camera “aimed at” that tile; there is a camera aimed where it always was, looking through a window that is nowhere near the middle of its own view.

That is exactly the situation a shift lens produces, and the principal point is not the centre measures what happens when a recovery assumes otherwise: on a fifth of a frame’s shift, a focal length computed from two vanishing points with the principal point taken as the image centre is out by one and a half per cent. A tile of a picture is a shifted frame, and a tile’s principal point is outside its own rectangle for every tile except the middle one.

One projection, two routes: divide by depth, or multiply and divide laterThe same box through the site's pinhole and through a 4×4 projection matrix with the divide postponed until after clip space. The worst disagreement over all twelve edges is 4.0e-14 px, which is the noise floor of double precision rather than an approximation.x/z, y/z — the pinholeM·p, then divide by wworst disagreement 4.0e-14 px over 8 verticescorrect from 21 cm, at 160 mm wide42° across · near 0.1 m, far 1000 m
Fig. 12 The agreement everything here rests on: the matrix route and the divide-by-depth route put every point on the same pixel, so a tile’s matrix can be compared with the whole picture’s.
What keystone correction actually costsA projector turned 15° from square throws its rectangular panel as a quadrilateral. Correction cannot add light outside it, so it shrinks the picture until it fits — and 18.7% of the projector's pixels are thrown away. The fraction is measured on the panel rather than on the wall, because turning the projector makes the wall picture larger while making the panel usage smaller.15° of yaw, 6° of pitch, 1.50 throw ratio81.3% of the panel reaches the corrected rectangleouter: the thrown quadrilateral · inner: what correction can keep18.7% of the panel discarded
Fig. 13 A projector turned from square, which throws a trapezium. The same rotation, in an instrument that cannot take an off-centre frustum.

Tiles that overlap, and tiles that do not abut

Nothing in the construction requires the tiles to tile. The sides are read at whatever pixel bounds are asked for, so overlapping tiles, tiles with gaps, and tiles of different sizes all work and all agree with the whole picture where they cover it.

That is more useful than it sounds and it is the test worth running on any implementation. Ask for two tiles that overlap by a strip, render both, and compare them on the strip: an exact construction agrees to the last bit, and a construction with a rotation in it disagrees by the same order as everything else. The overlap is a self-check that needs no reference picture at all.

It also disposes of a small confusion about resolution. A tile can be rendered at a different pixel density from its neighbours — twice as many samples across the same angular extent — and the geometry is unchanged, because the sides depend on the tile’s bounds in the original picture’s pixel coordinates and not on how finely the tile is drawn. A high-resolution inset of a low-resolution picture is a tile like any other, and it lines up.

Aiming the camera at each tileThe picture cut into 12 × 12 tiles, each rendered by a camera at the same eye turned to face the tile with a field of view that covers it. Every step of that is defensible and the result is a different picture: points land up to 4.1 px from where the whole picture puts them, with the lines drawn from each point to where its tile sent it. A rotation is not a crop.worst 4.1 px outcorrect from 17 cm, at 160 mm wide12 × 12 tiles
Fig. 14 A hundred and forty-four tiles. The absolute displacement is a few pixels and the proportion of a tile is where it started.

The check that makes it a claim

Both halves are asserted, because either alone is misleading.

The frustum tiling must be exact — otherwise the construction is merely close, and there would be no reason to prefer it to the other. And the rotated tiling must be wrong by a real amount — otherwise the essay is a warning against a mistake that does not matter.

The second assertion caught the only bug in the construction, and it caught it by being too generous. The first version reported forty pixels of error at every subdivision, from two tiles to twenty-four, with the worst point always the same one. It was a point outside the picture: the tile assignment clamped an off-frame point into the edge tile, and then measured a point half a picture from that tile’s centre and blamed the tile. A tile owns the points inside it, and a point outside the picture is in no tile at all.

The tell was the flatness. An error that does not respond at all to the thing it is supposed to depend on is usually not measuring that thing.

Cutting each tile out of the frustumThe same tiling with each tile's sides read straight out of the whole frustum — the direction a pixel u looks along is (u − pₓ)/f, so a tile's sides are the frustum's sides at the tile's own bounds. The reassembly is exact to 1e-13 px over 43 points: same eye, same focal length, a different window.worst 1e-13 px outcorrect from 17 cm, at 160 mm wide6 × 6 tiles
Fig. 15 Thirty-six tiles, cut correctly. Every one of them is an off-centre frustum and thirty-five of them have their principal point outside their own rectangle.

What each tile costs to set up

There is a practical asymmetry between the two methods that is easy to miss and points the same way.

The correct method needs, per tile, four numbers: the tile’s pixel bounds run through one subtraction and one division each. Nothing is rotated, nothing is normalised, no basis is built, and the near and far planes are the ones the whole picture had.

The rotated method needs a direction, a basis about that direction, a focal length derived from the tile’s angular extent, and a decision about which way is up in the tile — and that last one is where its own second bug lived. The basis has to be built the way the rest of the collection builds one, with the down vector as forward × right; written the other way round it produces a basis that is perfectly consistent, mirrors every tile horizontally, and passes every assertion about relative positions. It reported error figures four times too large until the sign was found.

So the wrong method is also the more elaborate one, which is the usual shape of this kind of mistake. It is not a shortcut that trades accuracy for simplicity. It is a longer construction that arrives somewhere else.

Where a depth buffer's codes actually goThe share of a 24-bit buffer used up within each distance, for a near plane at 0.1 m and a far plane at 1000 m. Ninety per cent of the range is spent inside the first metre; the last nine hundred metres share what is left.near 0.1 m · far 1000 m · 24-bit codeswithin 20 cm50.0%within 50 cm80.0%within 1 m90.0%within 5 m98.0%within 50 m99.8%half the codes are gone by 0.20 m — the harmonic mean of the two planesshare of the depth buffer used up within each distancearithmetic midpoint would be 500 m
Fig. 16 What a tile can do with its own planes: a tile covering only distant scenery can push its near plane out and buy back the precision the whole picture had to spend on the foreground.

One more thing the exact construction gives free

Because a tile is an ordinary frustum, everything the collection knows about frusta applies to it unchanged — and one consequence is worth stating because it is the kind of thing that gets rediscovered awkwardly.

A tile’s near and far planes need not match its neighbours’. They are not camera parameters, so changing them moves nothing in the picture; what they change is what each tile clips and how well each tile orders its own depths. A tile covering only distant scenery can push its near plane out and buy back depth precision that the whole-picture render had to spend on the foreground.

That is a real saving and it is available only because the planes are separable from the camera. In the rotated-camera method it would be available too, and it would be sitting on top of a picture that was already wrong, which is the general reason to fix the geometry before optimising anything.

The misregistration falls as 1/distance, exactlyAcross two decades of distance the stitch error times the distance is constant to 0.09%, and the slope on log axes is -0.9998. That is what says the fault is the pivot and not the lens: a calibration error would not care how far away the subject is.-0.50000.50010.50011.50log₁₀ distance to the point (m)log₁₀ misregistration after stitching (px)1.2 m → 7.85 px3.4 m → 2.77 px9.7 m → 0.97 px27.7 m → 0.34 pxpivot 50 mm behind the pupil, yaw 12°slope -0.9998
Fig. 17 The photographic case, priced. A panorama assembled from turned photographs has the same structure, and there the rotation is forced by the instrument rather than chosen.
Aiming the camera at each tileThe picture cut into 4 × 4 tiles, each rendered by a camera at the same eye turned to face the tile with a field of view that covers it. Every step of that is defensible and the result is a different picture: points land up to 9.9 px from where the whole picture puts them, with the lines drawn from each point to where its tile sent it. A rotation is not a crop.worst 9.9 px outcorrect from 17 cm, at 160 mm wide4 × 4 tiles
Fig. 18 Sixteen tiles by the rotated method, between the two subdivisions already drawn.

And the sun-lit version of the same mistake

The rotated-tile construction is the rendering form of a much older error, and naming it makes the pattern portable.

A panorama assembled from photographs taken by turning a camera on a tripod has exactly this structure — each frame is a rotation, not a crop — and the frames cannot be laid side by side on a flat sheet without warping. They have to be reprojected, which is what panorama software does, and the reprojection is the homography above. Anyone who has tried to butt two turned photographs together has met the error in the figures.

The difference is that in the photographic case the rotation is forced: a camera on a tripod cannot take an off-centre frustum, because a real lens’s principal point is where the lens puts it. In the rendered case it is not forced, and the matrix will take any four sides it is given.

Which is the small moral of the whole essay. The exact construction is available because a synthetic camera has a freedom a physical one does not, and the mistake is reaching for the physical camera’s constraint when nothing is imposing it.

What links here

Computed from the collection, not written here: the essays that point at this one.

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.

Camera matrixCamera tiltConditioningCrop factorHomographyPrincipal pointProjective mapRectilinear projectionTiled renderingView frustum