VIPER 11 · NotesFly it →

Note 2 of 15

The GoPro that doesn’t exist

Why VIPER 11 looks like footage and not like a game: a lens, a canopy and a grade that were never real.

VIPER 11 began as an attempt to recreate one video: a GoPro clip shot from behind an F-16 pilot’s head on a takeoff from a desert runway. That clip did not look like a flight simulator. It looked like footage, and footage has a signature. The lens bends the horizon. The sun flares across scratched plexiglass. The colours lean teal and orange, and the exposure breathes as the sky fills the frame. None of that comes from a camera here, because there isn’t one. Each piece is a deliberate imperfection, rebuilt in a shader.

A lens wider than a lens

An ordinary game camera is a pinhole. It keeps straight lines straight, which sounds good until you widen it. Push a pinhole camera past about 120° and the edges smear outward without limit, so a pinhole can never see 180°. A fisheye gives up straight lines to see further. The game’s frame is 165° across a 2.39 : 1 letterbox, wider than the clip’s GoPro.

The catch with a plain fisheye is that it bends the middle of the picture too: the HUD, the instrument panel and the runway ahead, the very things you need to read. So the game uses a custom profile. Inside 28° of the centre it behaves exactly like a normal lens. Beyond that, an extra term adds the rest of the field, and the compression grows toward the edges. The middle stays honest and the sides do the bending.

Figure 1 · The mapping

Left: how far from the axis (in degrees) each point across the frame looks, from the centre to the left or right edge. Gold is the game’s profile, using the same function as the game’s lens code. The dashed line is a plain fisheye with the same 165° field; the faint curve is a normal lens with the same centre, which reaches only 57° at the frame’s edge. Right: a grid of straight lines in the world (the horizon, every 10° of pitch, and verticals every 15° of heading), seen through the lens. Change the settings to see the trade-off.

No single camera render can cover 165° without wasting most of its pixels on stretched edges. So the game renders three views: a centre view covering ±50° and two side views yawed out to about ±66°, each with its own sky, clouds and cockpit. They are cross-faded where they overlap, and the lens pass reassembles them into one curved image. The side views cost about 45% more frame time, and that cost is the real price of the look.

The runway at 190 mph through a plain equidistant fisheyeThe same frame through the game's profilePlain fisheyeGame profile
The 190 mph frame, both 165° across. The plain fisheye (left) shrinks the cockpit and the runway ahead. The game’s profile (right) keeps the centre at its natural size and leaves the bending to the edges.

The canopy

Between the GoPro and the sky there is a bubble of polycarbonate that has been cleaned with a rag a thousand times. You never notice it until the sun swings into view, and then every hairline scratch lights up. The game draws the canopy glass with its own shader, at three-quarter resolution, as a layer of its own:

Light that misbehaves

Real lenses scatter light. A bright sun bleeds into its surroundings (veiling glare). It throws ghosts of itself along the line through the centre of the frame, it spikes into a six-pointed star from the aperture blades, and in the clip it draws a faint horizontal streak. The game does all of these from the actual bright pixels, so clouds and the canopy frame block them as they should. Bloom comes from a six-level blur chain that starts only well above white. God rays are a radial blur toward the sun of the open sky, strongest when the sun is half hidden.

Figure 2 · Layer by layer

The raw render at 12,000 feet with the lens layers offThe same frame with the selected lens layersRawLayers
One frame at 12,000 ft, climbing with the sun ahead, captured from the game eight times with the layers on and off. Left of the line is always the raw render: same lens geometry and tone mapping, no canopy, flare or grade. Drag the line and switch layers.

Exposure, grade and grain

A GoPro meters the scene and adjusts as it goes. The game does the same. It takes a log-average of the brightness of the world, leaving out the cockpit, counting the open sky at only 30% and the ground ahead a little extra, and eases the exposure toward it over about 1.2 s, within a range of 0.6–1.6×. Pitch up into a bright sky and the cockpit sinks into shadow; roll toward the desert and it lifts again.

Then the colour. The image stays in high dynamic range all the way down the chain and is tone-mapped exactly once, with ACES. A split tone pushes shadows toward teal and highlights toward warm orange. Then a 33 × 33 × 33 lookup table finishes the job. That table was not drawn by hand. It was fitted: frames from the game were matched region by region to frames from the reference clip, using a statistical technique called sliced optimal transport, and the result was smoothed into one table. The cockpit is treated a little differently, with a 1.7× exposure lift and its darks pulled back toward neutral, so that the grade’s teal doesn’t turn the instrument panel navy. Last come a vignette and a fine film grain.

The takeoff roll. The runway streaks are camera motion blur, using a 180° shutter at 30 fps and applied to the world only, so the cockpit stays sharp. Near the ground the blur is strengthened 1.6×, and there is no fake overlay: the streaks come from the runway’s real rubber marks and paint.

Figure 3 · The chain, in order

Everything after the 3D render happens in one post-processing pass per frame. The image stays linear and high-dynamic-range until the very end.

The letterbox

The frame is a fixed 2.39 : 1 cinemascope strip, centred on whatever window you have, with the page’s own black for bars. It is a framing choice, and it also saves work: nothing is rendered where the bars are. Hold a phone upright and the letterbox drops away. The same lens turns on its side, about 70° across and 124° tall, and the two outer views are pitched up and down instead of yawed left and right.

The lens is what makes VIPER 11 read as a recording rather than a game. Each part on its own is a defect a camera maker would try to remove.

Fly it

viperoneone.com

VIPER 11 is a browser flight game: three.js, WebGL2 and Web Audio. Every figure on this page is drawn from the game’s own code and numbers. Made by apek. Follow me @apekshik for what’s next.