This document describes the rendering process of the RFS2 engine in more detail. At its core, RFS2 engine uses a mixed approach of raycasting (like Wolfenstein 3D), and portal rendering (like Build), while entirely CPU-based software rendering like true retro DOS engines of pre-1996. There is a 3d-accelerated upscaler for the final image, but it's optional.
This is how RFS renders a 2.5D world image:
1. Initialize/update viewplane vectors. First, if the camera angle or FOV changed, the engine will recompute the viewplane dimensions, and precompute the raycasting vectors rotated by the current horizontal angle.
2. Recursively discover and render room base geometry. Starting out with the room the camera is in, the engine will scan and render the base geometry left-to-right along the walls via sparse raycasting. Any encountered solid wall is rendered immediately, any portal wall will be immediately recursed into to discover that next room, before the current one is continued. This results in a primarily left-to-right, secondarily back-to-front render order for base geometry.
3. Once a room's base geometry is done, render objects. Once a room has all nested portal rooms as well as its base geometry rendered, it will render its sprite objects and its additive geometry, both at once in primarily back-to-front order (left/right positioning has no influence on order here). There is no depth buffer involved, only a sorter.
The rendering of objects is clipped to the horizonal extents of the portal through which the room was discovered, since the rooms closer to the camera will be rendered afterwards sticking to the back-to-front overall room draw order. This means step 2 (base geometry render) and step 3 (object render) happen interleaved.
This section sums up the both technical and visual quirks of RFS2 Engine's renderer.
Due to the room discovery providing inherent sorting to the base geometry already, and the objects being sorted before rendering, no depth buffer is used. For additive geometry or sprites in complex positions like half in one room and half in the other, this can lead to occasional sorting errors - however, this is authentic behavior for many retro 2.5D engines from back in the day.
There is also as a result no depth buffer fighting, and no real near-clipping on close-ups, and no far-clipping. While there are distance limits, these limits are based on the amount of rooms that can be portal traversed per screen column, and the overall allowed map size, instead.
Since rooms are discovered each frame by visibility in above left-to-right, back-to-front order, any obscured rooms have almost no impact on render speed. This gives RFS2 Engine excellent occlusion culling for room base geometry, better than many modern engines.
Raycasting with free-form room polygons is slower to compute compared to simpler, classic Wolfenstein-style grid raycasters. To make up for this, the engine only casts the left-most ray to figure out what it hits, then computes computes the screen dimensions to skip over without in-between rays.
This makes RFS2 Engine a sparse raycaster, unlike simpler one-ray-per-screen-column raycasters.
While this is not how the DOOM engine or Build engine worked, neither of which use raycasting, it produces a similar visual result at decent speed. (We are actually not aware of an engine with a similar approach. If you know one, let us know in a post.)
Rendering is done at a lower resolution than the output window and upscaled, so if it seems pixelated as a whole that is why. It serves for a more retro look, and yields higher performance at the same time.
This upscale is the only part of RFS2 Engine that may use 3d acceleration if available, boosting speed on mobile devices.
Rendering is based on fixed point integers, not floats, alike to the DOOM and Build engines. This produces characteristic minor precision artifacts when looking at things close, causing a noisy, grainy look to close-up textures that you wouldn't see in any modern game.
3d perspective is only approximated with a lossy interpolation, leading to slight warping and unevenness on some surfaces as the camera moves around. True to other retro 2.5D engines, there is also no texture filtering or mipmapping.
Dynamic lights with falloff are interpolated over a coarser area, comparable to old-school gourad shading, and infinitely tall: a light at the bottom of a wall will light it up all the way to its top. This is how dynamic lights behaved in many old 2.5D engines, and it allows rendering them at greater speed and authenticity.
Looking up and down is only supported with slight distortions via Z shearing (click for video), due to shortcuts in how the walls are rendered that assume them to be always vertical on the screen. (After all, RFS is quite 2.5D e.g. due to its renderer.)
Higher CPU usage is a side effect of the rendering simply being fully CPU based (=runs on main processor), with no use of the GPU (=graphics card unused).
CPU-based rendering, also called Software Rendering, is admittedly dated in an age of somewhat fast graphics cards contained even in phones. But that is also the point: it is how old DOS games truly worked, and it allows RFS2 Engine to use fixed point maths replicating visual quirks of true retro engines better than most "traditionally modern" alternatives.
Despite the CPU usage RFS2 Engine's raycasting is well-optimized, and it usually runs at usable speed even on many single board machines which are slower than most regular computers in current-day use.