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
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
}
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));
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;
}
}
Top comments (0)