Here are explanations of various aspects of Oni's graphical code.
- Wikipedia: Environment mapping
It's a technique to simulate reflectivity: the diffuse texture is mapped using regular texture coordinates (UVs), and there's an additional texture layer, the UVs for which are generated from the vertex normals and the orientation of the object in space (and, ideally, the orientation of the camera). While env mapping is the most advanced shader in Oni, it's about the worst implementation of env mapping there ever was (the projection is planar and the camera angle is not taken into account at all).
- See BSL:Frustum_and_fog on how to alter fog using BSL.
- In mid-to-late 1990s games, when processing power was not enough to render far viewing distances, clipping was employed. However, the effect could be very distracting since bits and pieces of [objects] would flicker in and out of view instantly; by applying a medium-ranged fog, the clipped polygons would fade in more realistically from the haze. — "Distance fog", Wikipedia
- For more technical information on fog and on frustum-based space (or whatever it's called), see here and here and elsewhere.
The frustum (see below) defines a set of coordinates in which the near plane is at Z = 0 and the far plane at Z = 1 (X = -1 and X = 1 correspond to the left and right side of the frustum; the top and bottom of the frustum are planes with Y = -a and Y = a, with a the aspect ratio of the screen).
The important thing for fog is Z (the depth coordinate in frustum-based space). Every pixel of every object within the frustum is blended with a color (the fog color), depending on the amount f of fog in front of that pixel. The default fog color is 25% gray.
For f < 0, there is no fog in front of the object and the pixel retains its original color. For f > 1 the object is completely fogged and the pixel has the color of the fog. For 0 < f < 1, the color is interpolated linearly between the original pixel color and that of the fog.
The value of f is taken to be 0 on the fog's "start" plane and 1 on the fog's "end" plane and is interpolated/extrapolated linearly elsewhere, in the frustum-based coordinates. The natural order is: near clip plane, fog "start" plane, fog "end" plane, far clip plane.
The conversion formulas between z (units of distance from the camera) and Z (percentage of frustum depth) are:
- Z = z_f * ( z - z_n ) / [ z * ( z_f - z_n ) ]
- z = z_n * z_f / [ z_f - Z * ( z_f - z_n )]
By default, z_f is 10,000 world units and z_n is 4, so Z = 0.925 corresponds roughly to z = 53.07 world units (or 5.307 m, or 17.411 ft).
- See Frustum and fog on how to alter the far clip plane and field-of-view (FOV) using BSL.
- In 3D computer graphics, the viewing frustum or view frustum is the region of space in the modeled world that may appear on the screen; it is the field of view of the notional camera. The exact shape of this region varies depending on what kind of camera lens is being simulated, but typically it is a frustum of a rectangular pyramid (hence the name). The planes that cut the frustum perpendicular to the viewing direction are called the near plane and the far plane. Objects closer to the camera than the near plane or beyond the far plane are not drawn. — "Viewing frustum", Wikipedia
- Field of view (FOV)
- Oni defines the vertical field-of-view (FOV), i.e., the angle between the top and bottom planes of the frustum as measured at the camera's location, and its default value is 45°. On a 4:3 screen, the corresponding horizontal viewing angle is 2 * arctan(4/3 * tan(45° / 2)) = 57.8224°
- Widescreen FOV
- On a 16:9 or 16:10 screen, the horizontal viewing angle for a vertical FOV of 45° is larger: 2 * arctan(16/9 * tan(45° / 2)) = 72.73435° and 2 * arctan(16/10 * tan(45° / 2)) = 67.0682°, respectively. This causes insufficient frustum culling in some of Oni's cutscenes (intro sequences of CHAPTER 02 . ENGINES OF EVIL, CHAPTER 11 . DREAM DIVER, and CHAPTER 14 . DAWN OF THE CHRYSALIS for example). A quick way to fix this is to bring the horizontal viewing angle down to the supposed 57.8224° during the cutscene: the 4:3 viewport will then effectively be trimmed vertically rather than extended sideways. On 16:9 and 16:10 screens, the corrected vertical FOV will be 2 * arctan(3/4 * tan(45° / 2)) = 34.515877° and 2 * arctan(5/6 * tan(45° / 2)) = 38.0871°, respectively. Remember to set it back to 45° after the cutscene to fully enjoy the extended viewport. (Note that the AE contains patches for some of these cutscenes.)
A game never needs to draw all the objects in a scene at all times. Whole parts of the scene (polygons, meshes or groups of objects) can thus be "culled" (i.e. killed, not drawn).
An object (polygon, mesh) can be culled for several reasons, including the following:
- it's outside of the camera's field of view (view frustum culling)
- it's within the camera's field of view, but it's hidden by other objects (occlusion culling)
Culling speeds up the drawing process, but runtime detection of objects that need to be culled also takes time... One generally adopts a hierarchical subdivision of the scene, whereby the children (subparts) of a large object are automatically culled if their parent (the large object) is detected as hidden or out of sight.
Oni's solution is the combination of two algorithms: oct trees and ray casting.
- Oct trees
- Unlike the more common "visibility groups" placed manually by the level designer, Oni uses an automatic partition of the visible space into smaller and smaller cubes, until each "leaf" cube holds no more than a certain small number of polygons (see OTLF for details). Environment polygons (as well as characters, particles, etc) will only be drawn if they are in a leaf node of the oct tree, and if that leaf node is detected as visible. The detection of whether a leaf node is visible is done with ray casting.
- Wikipedia: Ray casting
It's an original algorithm that achieves both view frustum and occlusion culling. During each frame, 16-20 rays (depending on graphics quality) are shot from the camera into the environment, distributed randomly in the frustum. Along the path of each ray, the engine first determines the relevant leaf node of the oct tree (either by navigating the oct tree or using the information on the neighbors of the previously traversed leaf node), and then the ray is tested for collision with non-transparent environment quads intersecting that node (if the ray is stopped by a quad, then its further path is ignored). Quads thus detected with collision will be drawn. An overview of the algorithm is presented in the paper An Algorithm for Hidden Surface Complexity Reduction and Collision Detection Based On Oct Trees by Brent H. Pease (note that figure 1 and 2 are mixed up).
- Just a side note: the ray-casting feature which handles occlusion/frustum is not to be confused with the common term "ray-tracing", a CG rendering method where a ray is cast for every rendered pixel, achieving a high degree of photorealism. This method is very CPU-intensive and is typically used only for still images, short video sequences or CG movies such as Pixar's Cars. Implementations of real-time ray-tracing for gaming are underway, but still need to be complemented with the standard scanline rendering and rasterisation approaches for performance reasons.
Pease acknowledges that even ray-casting was too CPU-intensive until they lowered the number of rays being emitted and started altering their emission angles even when the camera remained still, in order to catch any polygons that the first emission of rays missed; this is why distant polygons, when suddenly revealed by the camera, sometimes are culled at first (pictured, right) and then appear a moment later: because the rays didn't hit them on the first pass. A distant face in the environment can be accidentally culled if it's only visible through a narrow slit, because the rays have a low probability of passing through the slit and hitting the objects behind it. A similar problem occurs when modders experiment with outdoor levels that have uneven ground (natural terrain): many of the ground polygons will be culled, making the map look like there are holes everywhere.
Oni uses the graphics quality slider in Options to help determine what level of detail (LOD) at which to render characters. Each character has up to 5 LODs, though one 3D model can be used in as many of the 5 slots as desired. The first step in determining which LOD to draw each character at is to consult this table found in the character's ONCC.
After that distance-from-camera filter is applied, Oni counts the total polygons of all characters in the scene (henceforth "TPCS"). If TPCS is not above the maximum character polys in the scene ("MCPS") which is allowed by the game's graphics quality setting, the game then experiments with raising the LOD of all characters one level at a time, stopping when it would pass MCPS. If TPCS is starting off above MCPS, it lowers all characters' LOD one level at a time until it gets below that limit.
For reference, here is the table of MCPS by graphics quality level: (1500, 2200, 3000, 3400, 4000). Note that there is no consideration at all for how many environmental polys are in the scene.
Next, iterating over each character from closest to farthest, the LOD of that character is raised by 1 as long as this won't raise TPCS over MCPS. Thus Oni seeks towards the maximum detail level that it can use for each character, prioritizing the closest ones. As a special consideration, if the closest character is below Medium LOD, it is raised to Medium regardless of MCPS, and the second-closest character is raised to Low if it was Super-Low.
Once the desired LOD is determined for a character, they will be switched to it immediately if they are not standing still (which would make the switch too noticeable). If they are moving, the game waits several frames to make sure the new LOD is continuously being requested by the above algorithms and then they are switched. The goal is to avoid a character rapidly switching back and forth between LODs.
After all the above considerations, one more: if a cutscene is playing, every character is guaranteed a minimum LOD of Low so that no one will be rendered at Super-Low detail.