DEV Community

Joseph
Joseph

Posted on

Voxel Raycaster (Voxlap-style) with baked cubic PBS/PBR lighting C#

I've always dreamed of making a renderer that captures the look and vibe of games like Jet Set Radio, Wind Waker, and PS1/PS2-era classics. So I built this: a voxel raycaster using baked physically based shading (PBS) and stylized outlines — written from scratch in C# with OpenTK.

This article walks through some of the ideas, code, and visuals behind it Goals

  • Stylized look, not realistic
  • Voxel-based 3D rendering (like Minecraft, but denser)
  • Prebaked lighting (GI, AO, skylight) to simulate advanced shading without real-time cost
  • Post-processing outlines to add thickness and style
  • Portable, optimized, compact

💡 How It Works

This voxel renderer is based on raycasting inside a 3D texture (a sampler3D) with voxel color+alpha data. Here's what the renderer does:

1. Voxel Texture Generation

byte[] data = new byte[size * size * size * 4];
// Fill with procedural shapes (islands, crystals, etc.)
GI_bake(data);
GL.TexImage3D(...); // Upload voxel data to GPU
Enter fullscreen mode Exit fullscreen mode

I use multiple GI passes:

  • GI_bake: simulates light exposure from above
  • GI_bake_Skylight: contrast-boosted skylight
  • GI_bake_Bounce: adds diffuse bounce from neighbors

2. Raycasting Shader (GLSL)

The main fragment shader walks a ray through the voxel volume:

vec3 ray = camPos;
ivec3 v = ivec3(floor(ray));
// Step through the voxel grid
for (int i = 0; i < maxSteps; i++) {
    vec4 c = texelFetch(vox, v, 0); 
    if (c.a > 0.1) { fragColor = c; return; }
    // Advance along ray using DDA
}
Enter fullscreen mode Exit fullscreen mode

3. Outline Shader

A full-screen pass uses simple brightness edge detection:

float edge = 0.0;
edge += step(0.1, abs(lum - lumX));
...
vec3 finalColor = mix(c.rgb, outlineColor, clamp(edge, 0.0, 1.0));
Enter fullscreen mode Exit fullscreen mode

It adds thick outlines around color or brightness changes — giving it a cel-shaded, "inked" look.

🛠 Technologies Used

  • C# / .NET 6
  • OpenTK (OpenGL bindings for C#)
  • GLSL shaders (330 core)
  • No engine, no Unity — just raw code

💬 Why This Project?

My younger brother said it looked like Jet Set Radio — and honestly that made me super happy. I wanted a renderer that could be:

  • Small and fast
  • Super customizable
  • Stylized, with that early-2000s console look
  • Fun to code and draw with

I'm also planning maximum optimization for the future — GPU performance, data compaction, and rendering tricks.

💭 Closing Thoughts

If you're into rendering, graphics, or building stylized engines from scratch, this kind of project is super fun and rewarding. You don't need huge engines — just some math, code, and creativity.

Want to learn more? I can share more about the probe-based lighting, optimization ideas, or outline tweaks in a follow-up post. Thanks for reading!

📦 Code Snippet (Optional)

void GI_bake(byte[] data)
{
    for (...) {
        // Exposure-based lighting
        float light = Math.Clamp(exposure / 10f, 0.2f, 1.0f);
        data[i + 0] *= (byte)light;
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)