Alexander Karpukhinak23.xyz

rndr

rndr is a CPU-side rendering and physics experiment in Rust. It implements its own math primitives, camera projection, triangle rasterization, fragment-shader abstraction, depth-buffered pixel output, ray casting, mesh collision checks, and rigid-body collision response.

The workspace is divided into:

  • rndr-math,
  • rndr-core,
  • rndr-phys,
  • several executable examples.

rndr-math

The math crate defines custom vector and matrix types rather than delegating to a standard linear-algebra package.

Vector support

V3 implements:

  • vector addition and subtraction,
  • scalar multiplication and division,
  • magnitude and normalization,
  • dot products,
  • cross products,
  • reflection-vector generation,
  • Euler-angle rotation through internally constructed rotation matrices,
  • weighted interpolation.

These primitives are reused across camera projection, geometry transforms, ray intersection tests, and rigid-body response.

Matrix support

The rendering and collision code use custom M3x3 matrices for:

  • camera basis transforms,
  • rotation-related calculations,
  • solving ray/triangle intersection systems through matrix inversion.

Scene and component model

The core crate uses an object/component structure:

  • Object,
  • ObjectManager,
  • Component,
  • built-in components such as Transform, Camera, and MeshRenderable.

This provides a minimal engine-style organization. Rendering and physics systems fetch the components they require rather than operating on bespoke one-off structs.

Camera projection

The Camera component constructs a projection matrix from camera orientation vectors:

  • forward,
  • right,
  • up.

For each vertex:

  1. Object transform is applied.
  2. Camera translation is removed.
  3. The point is multiplied by the projection matrix.
  4. Optional perspective scaling is applied if enabled.
  5. Points behind the near plane are rejected later in the render path.

The perspective branch uses a configurable display-surface offset and a zero_threshold guard to avoid unstable divisions near zero projected depth.

Mesh rendering pipeline

The main renderer processes mesh triangles in parallel using Rayon:

  1. Retrieve object transform and mesh.
  2. Compute the camera projection matrix.
  3. Project the three vertices of each triangle.
  4. Reject triangles that lie behind the near plane.
  5. Convert projected vertices into 2D triangle coordinates.
  6. Rasterize pixels inside the triangle bounding box.
  7. Compute barycentric weights through sub-triangle area tests.
  8. Interpolate vertex attributes with those weights.
  9. Create fragment data and call the mesh fragment shader.
  10. Emit candidate pixels to a PixelGrid.

This is a software rasterization pipeline. The fragment stage is abstracted through a FragShader trait, allowing different shading behavior without changing the triangle traversal logic.

Rasterization detail

Triangle coverage is determined by comparing the sum of sub-triangle areas to the total triangle area within a small epsilon. For covered pixels, barycentric weights are derived from those sub-areas.

The interpolated values are used to construct:

  • relative position,
  • world-space position,
  • interpolated color/depth output.

That fragment data is then passed to the active shader implementation.

Pixel storage and depth handling

PixelGrid maintains:

  • RGB pixel storage,
  • one depth value per pixel.

Each candidate fragment is written through set_pixel. The existing depth is compared before color replacement, which implements a z-buffer-style occlusion test.

Ray casting

The physics crate introduces:

  • Ray,
  • ObjectIntersectionRay,
  • the Raycastable trait,
  • hit structures containing position, normal, and distance.

Ray intersection against mesh colliders is computed by transforming triangle vertices into world space, constructing a 3x3 system using triangle edges and ray direction, inverting that system, and reading the resulting barycentric coordinates plus ray distance.

The hit is accepted when:

  • the distance is non-negative,
  • barycentric coordinates are inside the triangle,
  • the optional maximum distance constraint is satisfied.

Mesh collision detection

MeshCollider implements collision checks by:

  1. Finding the transformed mesh center.
  2. Iterating vertices in parallel.
  3. Casting rays from the mesh center toward each vertex.
  4. Testing whether those rays hit another mesh before reaching the chosen vertex.
  5. Returning an intersection point and normal when a hit is found.

This is a geometric collision-detection strategy built from the ray-casting machinery rather than a broad-phase/narrow-phase physics library.

Collision response

The physics manager gathers collisions and reacts using:

  • linear velocity,
  • angular velocity,
  • collision offsets relative to mesh centers,
  • momentum-like directional calculations,
  • updates to both translation and rotation state.

The implementation is still experimental, but it already connects detection, normal extraction, and response.