PDE Raycaster (preview)
⚠ NOTE: this post is a preview! Many areas throughout are unfinished, unnecessary, or missing entirely. The page is likely littered with random garbage like a scratch pad. Code snippets may contain errors, inconsistencies, or complete nonsense as, in many cases, excerpts are taken straight from local projects (possibly years old) so they must be adapted to make sense out of context and revised to promote best practices.
Hi, folks!
Lately I’ve been contending with a bout of emotional strife. My struggle with general anxiety in particular has been exacerbated by the guilt I feel from subjecting the people in my life to the worst of my avoidant tendencies. What’s more is that phases of intermittent apathy amidst a seemingly ever-increasing backlog of obligations have left me yearning for a respite. This has prompted me, for better or worse, to seek refuge in the sanctuary of escapism.
So, without further ado please join me in this babbling stream of consciousness fueled by the onset of hyperfixation! Allow yourself to become entranced by a flame of nostalgia ignited by the spark of an old obsession of mine: raycasting!
What Is Raycasting?
I’m not certain, but over the years I’ve got the impression that there exists some contention or, at the very least, some “fuzziness” as to the exact boundaries of what may formally be considered a raycaster. As a result, I set out to identify some qualities which precisely distinguish raycasting from its derivatives (e.g.: ray marching, ray tracing, etc.) and unambiguously define the relationship between them.
My Interpretation
In general, I consider raycasting to be a broad class of computer graphics rendering techniques all characterized by their ability to visualize geometric entities in a scene based on those entities’ interactions with “view rays”. From this perspective, anything which tests objects for intersection with view rays and then renders some representation of those intersections is a raycaster.
What distinguishes various raycasting derivatives from one another, in my estimation, lies within the finer details: how do we arrive at an intersection in the first place and how do we represent the resulting interaction and any subsequent interactions?

My First Raycaster

I have written a raycaster in the past. Granted, it was a certifiable mess, but it served its purpose as a worthwhile learning experience. It featured texture-mapped walls, vertical camera panning via Y-shearing in addition to neat post-processing effects like a reflective “water” floor effect as well as a frame-blending-style motion blur effect.
public class Edges {
// ...
public static float[] intersection(
float xa1, float ya1,
float xb1, float yb1,
float xa2, float ya2,
float xb2, float yb2
) {
float r1=(xb1-xa1);
float r2=(xb2-xa2);
float m1=(yb1-ya1)/r1;
float m2=(yb2-ya2)/r2;
float y1Int=ya1-xa1*m1;
float y2Int=ya2-xa2*m2;
// NOTE: replace "==" & "!=" with threshold
if(m1!=m2)//slopes are not equal; not parallel
{
float x;
float y;
if(r1==0)//vertical line 1
{
x=xa1;
y=m2*x+y2Int;
}
else if(r2==0)//vertical line 2
{
x=xa2;
y=m1*x+y1Int;
}
else
{
x=(y2Int-y1Int)/(m1-m2);
y = m1*x+y1Int;
}
//if(Math.signum(x-xa1)==Math.signum(ra) && Math.signum(y-ya1)==Math.signum(yb1-ya1))
float[] hit={x,y,(xa2-x)*(xa2-x)+(ya2-y)*(ya2-y)};
if(isInView(hit) && isOnEdge(hit,xa2,ya2,xb2,yb2)) {
return hit;
}
}
return null;
}
private static boolean isOnEdge(float[] hit, float xa, float ya, float xb, float yb)
{
//*
float dotProduct = (hit[0] - xa) * (xb - xa) + (hit[1] - ya)*(yb - ya);
if (dotProduct < 0) return false;
float lengthSqrdba = (xb - xa)*(xb - xa) + (yb - ya)*(yb - ya);
if (dotProduct > lengthSqrdba) return false;
return true;
}
private static boolean isInView(float[] hit)
{
return hit[1] > 0;
}
// ...
}
By testing the equations of each ray with that of each element of the unorganized list of line segments and then either sorting (painter’s algorithm) or filtering (Z-buffering) the resulting set of intersection points by distance to the camera we are able to solve the visibility problem by brute force ultimately enabling us to render the nearest edge in view if one exists. Because we are essentially solving for the intersection point of a pair of arbitrary, infinite lines, it means that we impose no restrictions on the orientation (position, angle, length or otherwise) of scene elements (“edges” or “walls” if you prefer) relative to one another. This is in contrast to what most of us may be accustomed to observing among titles from the primordial era of 3D games which utilized raycasting. Specifically, consider the likes of Hovertank 3D, Catacomb 3D, Wolfenstein 3D and its derivatives, etc.

⚠ NOTE: if you stopped by to check out this preview I appreciate it! Soon I'll add the full digest post serving as the overview of a series of posts I intend to create which will include additional narrative entries with code snippets to follow along with in your local project!