This document provides information about some of the software associated with the Tarpeeksi Hyvae Soft banner – in other words, software made by a dude in his own time, some of it professional-like and some just for fun.
A capable capture tool for Datapath's VisionRGB range of capture cards, intended for both recreational and professional use.
VCS's features include:
VCS introduces minimal delay to the capture chain. The full latency from a visual event triggering in the target system to it being captured by a Datapath VisionRGB device, processed by VCS, and displayed in VCS's output window is roughly a single frame for common VGA resolutions.
The VCS architecture runs a central execution loop which calls on functionality provided by its subsystems to capture, manipulate, and display frames obtained from the capture device. The subsystems are self-contained and provide a public interface both for using the subsystem and replacing it with another implementation of the interface. For example, support for new capture devices can be added by replacing the implementation of the capture subsystem interface.
Developer documentation for VCS, with fully custom Doxygen theming.
The docs are produced as in-code Doxygen-formatted documentation that's converted in an intermediate step into XML using Doxygen and finally into HTML using a custom XML-to-HTML converter.
The custom XML-to-HTML converter uses Python's ElementTree to load in Doxygen's XML files, then recursively iterates over the XML trees to convert them into HTML markup. The converter is designed with modularity in mind, using swappable components that produce HTML output from XML input and so allowing different output themes to be created separately of the converter.
Compared to Doxygen's standard HTML output, the custom theme includes light and dark mode, a mobile-friendlier layout (work in progress), more semantic HTML, and code markup that's compatible with the highlight.js syntax highlighter. Future features will include search functionality.
A well-featured, retro-oriented 3D software renderer for the HTML5 canvas. Rasterizes n-sided polygons onto a given canvas – or, optionally, asynchronously into an offscreen pixel buffer.
The renderer's features include:
The renderer is also quite modular, allowing the user to swap out individual parts of the render pipeline at run-time and to write custom shaders for various stages of the pipeline (vertex transformation, polygon rasterization, pixel shading, and render context manipulation).
<canvas id='canvas' style='width: 200px; height: 200px;'></canvas> <script src='rngon.cat.js'></script> <script> const triangle = Rngon.ngon([ Rngon.vertex(-1, -1, 3), Rngon.vertex( 1, -1, 3), Rngon.vertex( 1, 1, 3)], { color: Rngon.color_rgba(0, 150, 255) }); const mesh = Rngon.mesh([triangle]); Rngon.render('canvas', [mesh]); </script>
// The renderer will call this function once the current frame has been rasterized. // The 'fragmentBuffer' property contains metadata about each pixel, like world- // space coordinates and n-gon material properties. function wavy_pixel_shader({renderWidth, renderHeight, fragmentBuffer, ngonCache, pixelBuffer}) { const timer = (performance.now() / 300); for (let y = 0; y < renderHeight; y++) { const horMagnitude = 7; const verMagnitude = ((y / renderWidth) * 10); const cos = (horMagnitude * Math.cos(timer + verMagnitude)); for (let x = 0; x < renderWidth; x++) { const dstIdx = (4 * (x + y * renderWidth)); const srcIdx = (4 * (Math.min((renderWidth - 1), (x + horMagnitude + ~~cos)) + y * renderWidth)); const fragment = fragmentBuffer[srcIdx / 4]; if (!ngonCache[fragment.ngonIdx].material.isWavy) { continue; } pixelBuffer[dstIdx + 0] = pixelBuffer[srcIdx + 0]; pixelBuffer[dstIdx + 1] = pixelBuffer[srcIdx + 1]; pixelBuffer[dstIdx + 2] = pixelBuffer[srcIdx + 2]; pixelBuffer[srcIdx + 0] = 255; pixelBuffer[srcIdx + 1] = 255; pixelBuffer[srcIdx + 2] = 255; } } }
A web app for playing and corrupting the 1996 DOS game Rally-Sport.
The app provides a UI for selecting pre-defined corruptions, then uses the API provided by DOSBox viewer to run the game in the browser with the corruptions' modifications applied to the game files.
You can have a lot of fun with this app, especially if you have nostalgia for the game.
Related to the RallySportED project.
A Node.js app for monitoring trends in retail prices at Kesko's grocery stores (e.g. K-Market).
There's all kinds of talk recently of food prices going up – and honestly, they've been creeping up all the time for years – so it's a good time to start keeping a methodical eye on pricing.
This app lets you log prices on products of your choosing at Kesko's grocery stores, then view trends in those data via a web interface (see the screenshot, below).
For more info, check out the project on GitHub. Since it's a relatively recent project, you can expect changes, improvements, etc.
A framework for constructing single-page, end-user-oriented documentation using Markdown. Work in progress.
Dokki's feature's include:
Dokki extends HTML with a set of new tags designed for marking up documentation and the like, backed by a Vue.js front-end for rendering the tags as standard HTML elements. The front-end also provides an automatically-generated table of contents, responsive styling, keyword search, and both a light and dark color theme, among other things.
With dokki, you can easily embed various documentative content, like code, images, tables, videos, and so on. For example, you can show code snippets with syntax highlighting and styled sample output:
int main(void) { const char *str = "Hello there."; printf("%s\n", str); return 0; }
For more examples, visit the dokki API reference. Although the project is work in progress, my blog is made using it, and so is the page you're reading now.
A vanilla multi-threaded path tracer written in TypeScript.
Features cosine-weighted Lambertian sampling, importing scenes from Blender, exporting renderings into PFM, continuing previous renderings from PFM, multi-threaded rendering, tone mapping, normal interpolation, a BVH tree to accelerate ray–triangle intersection tests, a JSON scene file format, and a functional coding style.
{ "camera":{ "position":{"x":0,"y":0,"z":1.745841}, "axisAngle":{"x":0,"y":1,"z":0,"w":0} }, "sky":{ "model":"cie-overcast", "zenithDirection":{"x":0,"y":1,"z":0} }, "materials":{ "default":{ "type":"lambertian", "color":{"r":0.1,"g":1,"b":0.1} } }, "triangles":[ { "material":"default", "vertices":[ {"position":{"x":-2.653676,"y":-2.627338,"z":-8.797296},"normal":null}, {"position":{"x":2.666877,"y":-2.627337,"z":-8.797296},"normal":null}, {"position":{"x":2.666877,"y":-2.627338,"z":-14.179447},"normal":null} ] } ] }
Looks like an old browser, acts like an old browser. Serlain is a frontend for Internet Archive's Wayback Machine, displaying archived pages inside period-correct-looking mock browsers.
Serlain's features include:
A browser in Serlain is a plain HTML element with its background image a screenshot of a blank browser window (empty title bar, nothing in the address bar, etc.), and the blank portions filled in with interactible React components.
When the user types a URL into the mock browser's address bar, Serlain fetches the corresponding archived page from the Wayback Machine API and displays it in an <iframe> element inside the mock browser.
Although the mock browsers correctly display dynamic, period-correct UI messages in response to navigating to and loading the initial page, further navigation by clicking on links in the documents doesn't trigger updates to the mock UI – so the immersion kind of breaks after the first navigation. It should be possible to inject event handlers into the archived pages' source to detect in-page navigation and so keep Serlain's mock UI up-to-date; but this would probably involve a fair bit of dealing with the quirks of 1990s HTML, so I'm not planning on doing it right now. It'd be a cool feature to have, though.
const nn4 = { desktopIcon: { title: "Netscape Navigator 4", imageUrl: "./img/icons/netscape-navigator-4.png", }, browserClassName: "netscape-navigator-4 resolution-800x600", // The year from which this browser will show Wayback Machine captures. browsingYear: 1997, // The desktop styling to be shown when this browser is open. operatingSystem: "Windows 95", // The standard interactible buttons available in this browser. buttons: ["reload", "home", "back", "forward", "close"], // Strings shown in the browser's message bar in response to events, // replicating the original, period-correct wording. messageBarStrings: { fetching_page_url: (url)=>`Connect: Looking up host: ${url}...`, loading_page: (url)=>`Transferring data from ${url}`, page_load_failed: (url)=>`Unable to connect to site ${url}`, page_load_finished: ()=>"Document: Done", }, };
A web app for Finnish hobbyist birdwatchers to keep list of their sightings in a visually engaging way. The app tracks the date of your first sighting of each species, and shows a pleasing visual tally of your sightings so far.
Lintulista's features include:
Lintulista's back-end is Heroku-compatible and minimizes the use of database rows – potentially useful for those on the limited Heroku Free plan.
Lintulista's front-end codebase comes with tooling to semi-automate the harvesting of bird thumbnails from Wikimedia Commons.
Lintulista is currently for private use only, but a public sample is provided (see links, below).
A unifying interface for various 3D rasterizer APIs on the Win32 platform.
Kelpo's features include:
The 1990s saw the proliferation of consumer 3D hardware along with non-standard rendering APIs and diverging hardware feature-sets (see e.g. vintage3d.org for more info). If you wanted your 3D application to capture the home market, you had to write several variations of your rendering code – for example, the renderer for Tomb Raider came in at least six versions: one for software rendering and the rest for the various 3D hardware offerings from ATi, Matrox, S3, and others.
The goal for Kelpo is to create an easy-to-use generic render API that exposes a minimal feature-set shared across the various 3D implementations of this time period (Glide, S3D, Direct3D, and others), such that the client application only needs to write its rendering code once and can be fairly certain that the output will be the same regardless of the end-user's hardware, while in the background the application still takes advantage of that hardware via its native API.
Kelpo is work in progress, and there's no hurry to complete it – it's a casual project being done for the sake of doing it. It currently supports Glide 3, Direct3D 5, Direct3D 7, OpenGL 1.1, and OpenGL 3.0; planned support includes the proprietary rendering APIs of Matrox, S3, and ATi, among others.
You can find out more technical details about the project in this blog post.
/* Initialize Kelpo for OpenGL 1.1. */ const struct kelpo_interface_s *kelpo = NULL; kelpo_create_interface(&kelpo, "opengl_1_1"); /* Open a 640 x 480 (16-bit) render window on video device #0. */ kelpo->window.open(0, 640, 480, 16); /* Run the render loop. */ while (kelpo->window.process_messages(), !kelpo->window.is_closing()) { kelpo->rasterizer.clear_frame(); kelpo->rasterizer.draw_triangles(&triangle, 1); kelpo->window.flip_surface(); }
A binary file format for storing 3D models and their textures; a companion format for the kelpo renderer.
A KAC file stores vertices, normals and UV coordinates in 32-bit floats. Texture data are stored in 16-bit color with transparency (RGBA4444); polygon colors are stored in 16 bits with alpha (RGBA5551). Textures are stored with mipmapping down to 1×1. Because a KAC file includes the model's entire set of textures, each texture is associated with a 128-bit SHA-256 pixel data hash to allow duplicate textures to be discarded when loading multiple KAC files.
Includes a C89-compliant importer for loading KAC meshes into C/C++ programs, and an OBJ-to-KAC mesh converter. The converter automatically generates texture mipmaps, resizes textures to the required power-of-two aspect ratio, and converts colors into KAC's native 16-bit format.
A visually minimalist HTML5 wireframe renderer of arbitrary polygons into an <svg> image element.
Luujanko's features include:
In normal rasterization, you first clear the frame buffer and then plot in the new frame's pixels. Luujanko works kind of like that, except rather than clearing the frame buffer, it empties the target <svg> element of its <polygon> child elements; and rather than plotting pixels, it re-populates the <svg> element with <polygon> children from a pre-generated <polygon> element cache, inserting into them the new frame's screen-space vertex coordinates.
The render performance is very adequate for single frames, but continuous rendering suffers from some amount of stuttering.
Luujanko is an offshoot of the retro n-gon renderer, sharing much of its API style.
<svg id='rendering' width='200' height='200'></svg> <script type='module'> import {Luu} from 'luujanko.js'; const targetSVG = document.getElementById('rendering'); const triangle = Luu.ngon([ Luu.vertex(-1, -1, 0), Luu.vertex( 1, -1, 0), Luu.vertex( 1, 1, 0) ]); Luu.render([Luu.mesh([triangle])], targetSVG, { viewRotation: Luu.rotation(0, 0, 0), viewPosition: Luu.translation(0, 0, -3) }); </script>
A 3D front-end for the PC emulator PCem. PCbi replaces PCem's text-based user interface with a 3D scene in which you can assemble and run the emulated PCs.
Rather than clicking through plaintext menus to select which components an emulated PC should have, PCbi lets you stick components into a motherboard in a 3D view – making the assembly a more tangible, rewarding experience. Once the PC has been assembled, PCem can be made to run the emulation on its virtual screen.
PCbi's features include:
PCbi implements run-time inter-process communication with PCem via shared memory (Linux and Windows). First, the PCem source code is modified accordingly to enable the communication, then two-way access into a byte array is established at run-time between PCbi and PCem. Specific control bytes are toggled by either program to let the other one know of changes in status, and data bytes carry particular payloads to the other process (e.g. the pixel data of PCem's screen buffer, to be displayed by PCbi on the screen of the virtual PC).
PCbi also implements a connector interface for installing components into the virtual PC. For each connector (e.g. a female ISA slot on the motherboard and a corresponding male ISA card edge on a video card), a type and gender are assigned, which must match when attempting connection (supports multi-capable connectors, e.g. a female 16-bit ISA slot being also able to accept 8-bit ISA card edges). Each connector is also associated with two vectors: one pointing in the direction of plugging, and the other a common direction for aligning the two components when connecting. The vectors allow PCbi to properly rotate a component's 3D mesh to match the orientation of its parent when connecting. Early versions of PCbi supported a tree structure for component connections, where a component could be connected to a component connected to another component connected to a third component, etc. (e.g. inserting memory chips onto a video card installed in a motherboard); but later versions simplified this system for usability reasons to just allowing components on a motherboard.
On Linux, PCbi includes a handy shared folders feature, which allows the user to select a folder on their hard drive and have its contents be made available in the emulated environment. This is achieved behind the scenes simply by creating an ISO file of the folder (using a system() call for genisoimage) and telling the PCem process to mount it.
The 3D renderer uses OpenGL for rasterization, with vertex transformation handled CPU-side (first versions of the app used full software 3D rendering). The program has a fully custom GUI styling made with Qt. Mouse picking in the 3D scene uses standard ray casting. Other features include the ability to set a background image for the 3D scene.
Ever wanted a cloud physics simulator and renderer? Maybe not, but this app provides.
The program simulates the rising of moist air and the subsequent condensation of cloud-forming droplets as the air cools; then renders the resulting 3D droplet grid into a realistic image of clouds.
You can configure the simulator's atmospheric properties to your liking, including the environmental lapse rate and relative humidity curves. The GUI provides real-time visualization of the state of the simulation, showing the relative humidity, air temperature, and droplet distribution either as averages across the simulation grid or as horizontal or vertical slices.
The renderer uses volumetric path tracing and photon mapping to model in-cloud light transfer. Spectral Mie scattering is empirically approximated (by eyeballing MiePlot graphs), while the Hosek–Wilkie 2012 sky model is used to approximate Rayleigh scattering. Optionally, a HDR environment map can be used in place of the parametric sky model.
OpenCL is used to accelerate calculations of condensation (a square root and a multiplication or two per simulation grid cell, each cell being processable parallel to the others), while the renderer works on tiles distributed across the system's cores.
This program required a beefy computer. With a mid-to-low-range home PC of the time, running the simulation on a high-resolution grid (e.g. 300 × 300 × 300) could take hours, and the rendering would take several days to converge to a relatively noise-free outcome even at low render resolutions (e.g. 512 × 512).
A hybrid voxel/polygon 3D software renderer that gets its inspiration from retro voxel/polygon games like Delta Force and Outcast.
The voxel renderer makes 2D heightmaps into 3D landscapes. It's effectively a ray tracer, tracing a ray through each pixel on the screen and sampling height values from the heightmap along the ray's path. The renderer has full six degrees of freedom, multi-threading, and bilinear interpolation.
The polygon renderer supports single-threaded scanline and barycentric rasterization. It uses the depth map generated by the voxel renderer to discard pixels occluded by the terrain.
// This function will be called by the voxel renderer for each sky pixel, with // a direction vector from the viewer to the pixel's sky patch as an argument. const auto hosekWilkieSkySampler = [&](const vond::vector3<double> &direction)->vond::color<uint8_t, 4> { const double thetaSky = std::acos(skyZenith.dot(direction)); const double phiSky = std::acos(sunPos.dot(direction)); vond::color<double, 3> result; for (unsigned int i = 0; i < 3; i++) { result.channel_at(i) = (5 * arhosek_tristim_skymodel_radiance(skyModelState[i], thetaSky, phiSky, i)); } return { uint8_t(std::min(255.0, std::max(0.0, result.channel_at(0)))), uint8_t(std::min(255.0, std::max(0.0, result.channel_at(1)))), uint8_t(std::min(255.0, std::max(0.0, result.channel_at(2)))), 255u }; };
A modding toolset for the classic racing game Rally-Sport that lets you create new content for the game. As far as I know, RallySportED is the only available modding tool for this game.
The toolset has included a variety of asset editors over time, including for tracks, cars, textures, and the game's hard-coded keyboard controls; and has supported a number of operating systems from 16-bit DOS to modern Linux and browsers.
For the moment, I've condensed the toolset into two parts: a browser-based track editor, and a DOS-based loader program for loading custom tracks into the game (which doesn't support modding out of the box).
The track editor's features include:
The track editor's custom software 3D renderer closely reproduces Rally-Sport's unique 3D rendering style, with n-sided polygons, screen-space texture-mapping, and one-point perspective. The renderer uses the retro n-gon renderer with custom transformation and rasterization paths. You can read more about replicating Rally-Sport's visuals in this blog post.
I learned x86 assembly and reverse-engineered good chunks of the game to enable modding support for it, and put together a bunch of what I learned into documentation about the game's file and data formats.
A platform for users of RallySportED to share and download custom-made content for the game.
Rally-Sport Content's features include:
The platform provides a back-end that stores RallySportED-made content – for example, custom tracks – and exposes a documented REST API through which clients can access those data. Also provided is a reference implementation front-end view for accessing the content in a browser.
Patches a bug in Ultima 7 that forces the CPU cache on at game launch.
Like many games of its time, Ultima 7 lacks hardware-agnostic game cycle timing, meaning that its gameplay will run too fast on hardware that's much newer than the early Pentium-class machines the game was originally developed for.
The usual workaround for this problem is to disable the CPU's cache, making it run much slower and so more in line with what the game was written for. Unfortunately, this workaround doesn't work for Ultima 7, as the game will always force the CPU cache back on at launch.
This patch modifies the game executables so that they no longer touch the CPU cache flag (which controls whether the cache is disabled), and so the cache can remain disabled during gameplay. With the patched executable, I've seen a cache-disabled Athlon 64 CPU run the game absolutely fine.
It seems that the (unpatched) game resets the CPU cache flag as part of its logic to enter unreal mode. However, this process shouldn't require the cache flag to be modified, so it seems like a programming oversight (i.e. bug). The patch simply prevents the game code from modifying the cache flag bit, while retaining the relevant functionality to enter unreal mode.
; The game wants to enable protected mode as the first step in entering ; unreal mode. Normally, it assigns 1 to EBX, which sets the relevant ; first bit but also resets all the other bits (importantly, bit #30, ; for cache disable). Instead, we want to set only the first bit and ; leave the others untouched. mov ebx, cr0 or bl, 1 mov cr0, ebx ; ... ; The game has entered unreal mode and is ready to disable protected ; mode. Again, we want to manipulate only the relevant first bit. and bl, 0feh mov cr0, ebx
A set of command-line tools for converting data from the DAT files of Grand Prix Legends into more standard formats. The tools can extract files from DATs (container files), convert MIPs and SRBs (images) into PNG, and turn 3DOs (3D meshes) into OBJs.
The game's assets are stored in DAT container files, which are collections of straightforward binary blobs represented as a data header and a stream of bytes. Of the types of assets, the most notable is the 3DO mesh: its data are stored in a tree format, with nodes defining properties of the mesh (visibility, texture, color, polygons, etc.) and branches joining multiple nodes together (resulting in e.g. visibility culling based on which branch path is taken). This seems to be a pre-compiled format for consumption by the game's renderer rather than a format for storing meshes in general.
I used PHP to write the DAT and MIP/SRB converters, but chose C++ for the 3DO-to-OBJ converter for better binary-parsing performance (I recall the speed-up was about an order of magnitude going from a PHP implementation to C++).
I wrote this toolset to help generate scenes for testing the retro n-gon renderer. There didn't appear to be existing tools that ran natively on Linux.
A HTML5 video player whose seek bar shows a spectrogram of the video's audio.
To generate the spectrogram, the app samples the output of a <video> element using an AnalyserNode to get a time-variant spectrum, then paints the spectral values into a <canvas> element overlaid on the video's seek bar.
This app was originally made for a friend who had a need for it, but it turned out their iPad was overly paranoid about letting the browser access videos on the system, and so they couldn't use this.
A program to measure aspects of your typing, NPTO provides statistics like percentage of per-key mis-presses, breakdown of typing time per key, and others things of that nature.
Comes with a fully custom styled GUI made using Qt.
Check out some of the videos demonstrating the program in action.
A 3D visualizer that lets you embed n-gonal meshes into a HTML5 document via extended HTML syntax, with a simple UI for controlling the visualization.
Possible use cases include debug rendering in the workflow of writing a mesh file importer, and for demonstrating the contents of a mesh file.
Uses Luujanko for the wireframe rendering.
<mesh-preview> <wireframe-mesh id='triangle' name='Triangle' src-array='[ [{x:-1, y:-1, z:0}, {x: 0, y: 1, z:0}, {x: 1, y:-1, z:0}] ]'> </wireframe-mesh> <wireframe-mesh id='two-squares' name='Two squares' src-array='[ [{x:-2, y:-1, z: 0}, {x:-2, y: 1, z: 0}, {x:-1, y: 1, z: 0}, {x:-1, y:-1, z: 0}], [{x: 1, y:-1, z: 0}, {x: 1, y: 1, z: 0}, {x: 2, y: 1, z: 0}, {x: 2, y:-1, z: 0}] ]'> </wireframe-mesh> </mesh-preview>
A utility to parse hard drive SMART data dumps from smartctl into customizable graphs.
The app provides the ability define individual graphs (e.g. temperature per time) and to compose these graphs into an image. The graphs' data can be either directly from the SMART dumps or transformed using simple mathematical expressions.
The app uses Qt to draw the graphs and for exporting them into a PNG image.
Smarse is a more recent version of this program.
A web-based version of Smartti parseri.
The app parses SMART attribute data from smartctl log dumps via a Node.js back-end, then converts the data using client-side JavaScript into SVG plots for display.
The fundamental output of the app is a single plot of a SMART attribute's values over a period of time. The user is then free to style and compose these plots onto a HTML page as they see fit.
Test your old processor's floating-point performance with this DOS path tracer that renders a scene of spheres and counts the number of seconds it took.
The program runs in VGA mode 13h (320 × 200, 256 colors) and renders its test image in 120 × 80 resolution (upscaled to 240 × 160 for display). The scene to be rendered is made of eight spheres (five diffuse Lambertian and three reflective) and a large triangle for the ground plane; the background is a light source approximating the CIE Standard Overcast Sky.
Requires a math co-processor.
A simple 16-bit real-mode 3D rasterizer benchmark for recreational perf-testing of DOS-capable C compilers, compatible with a number of legacy C and C++ toolchains.
The benchmark renders a spinning 3D model in VGA mode 13h, then provides basic characteristics of its runtime performance.
Supported compilers:
A simple user interface for js-dos, intended primarily as a means to embed and demonstrate Tarpeeksi Hyvae Soft's DOS apps in this document.
The app provides a JavaScript API for defining a set of containerized DOS programs; a URL hash interface for running one of the containerized programs in js-dos (e.g. //localhost:80/dosbox/#quake); and a styled HTML container for the js-dos canvas with dynamic resizing that maintains the pixel aspect ratio.
The app was built for embedding and showcasing DOS apps on web pages – namely, DOS apps I've made and in this document (see e.g. DOS path tracing benchmark.
A tool to make impulse response recordings into waterfall graphs.
The app reads WAV files representing impulse responses, uses the Kiss FFT library to extract the audio's frequencies in the time-domain, and renders the resulting spectrogram.
The method of rendering varied from ray-tracing a 3D mesh of the spectrogram to drawing slices of the spectrogram as polygons using Qt's QPainter.
No-frills data storage and graphing for minimalist logging and monitoring of sequential data (e.g. temperature).
The database is a program providing IO for a headerless binary storage format consisting of 12-byte time/value pairs: a 4-byte float that stores a value logged on an occasion, and an 8-byte int that records the corresponding timestamp. The data are stored in files on disk, for which the program provides a simple command-line interface to enter and fetch data.
Complementing the database is a grapher that produces visual readouts of data from the database files.
The program conforms to feature minimalism: it has a small set of base features, just enough to do the intended job, and no more are to be added.
This was the first path tracer I made, serving as a personal testbed for messing around in offline rendering.
The renderer featured BVH-accelerated ray–triangle path tracing on the CPU and GPU, photon mapping, spectral and volumetric rendering, HDR environment mapping, and various other similar things. Its render performance wasn't stellar, but as with path tracing in general, it was able to generate fairly realistic images.
The renderer was later used in my cloud simulator to produce its cloud images, in which rays were traced through moisture volumes representing cloud bodies.
My first ray caster, from 2003. Its initial incarnation was based heavily on Permadi's historic ray-casting tutorial, but evolved from that original Wolfenstein-style 90-degree-walled caster into a heightmap-based 2.5D voxel renderer as I kept experimentally tinkering with it.
This project is included here for the sake of history. Vond is a more recent heightmapped voxel renderer of mine, and I continued my ray-tracing experiments with YQR.