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 BSL: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.
- 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.
- Ray casting
- Wikipedia: Ray casting
It's an original algorithm that achieves both view frustum and occlusion culling. During each frame (i.e. 60 times per second), 20 rays 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), 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). An overview of the algorithm is presented in the following paper:
An Algorithm for Hidden Surface Complexity Reduction and Collision Detection Based On Oct Trees (figure 1 and 2 are mixed up) by Brent H. Pease
- Just a sidenote: the ray-casting engine that 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 can not yet compete with the standard scanline rendering and rasterisation approaches.
Pease acknowledges that even occlusion testing 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 and then appear a moment later (pictured, right): because the rays didn't hit them on the first pass. More generally, a distant object 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.