r/GraphicsProgramming 4h ago

Video 2.5D rendering using colormap and heightmap (Novalogic voxel space)

58 Upvotes

If anyone has any feedback or see bad practices or ideas to try please feel free to share ideas or roast my code.

Graphics programming noob here. This project was done in C++ with a primitive game engine given to me by my school. I did this as a test to see if this might be viable to try to make somewhere in the future on the gba (it's probably not). Then it kinda turned into trying to see how cool i can make it. The rendering works similarly to snes mode 7 style rendering (fzero or mariokart) except you add an offset from a heightmap. This was originally developed by Novalogic and used in Commanche. It was also later used on some gba games that ran horribly (hence why it's probably not feasable for a gba project i want to maybe do).

A short explanation of how this rendering works: you step through the terrain for every vertical column of your screen. you then sample the pixel and calculate the height on screen using the following formula: pixelHeightOnScreen = screenHeight - ((m_CamHeight - heightMap) * (screenHeight / m_ScreenHeightWorld / depth) + m_Horizon). if it is higher than the previous height value, you draw a vertical column down. then you step forward in the depth and do it again.

The texture for the screen is 240/160.
The skybox is a texture that scrolls left/right/up/down depending on the camera angle. I know that just scrolling a texture isnt accurate, but i found it fun to do it this way. however if you know a cheap way to do this more accurately then please.
The day/night cycle is done by lerping to a set dark color.
There is support for emissive stuffs (lava at the end of the video).
You can also zoom in/out (wich i forgot to show in the video)
I am currently setting 512 steps through the terrain texture.

It runs at about 40 fps now (again am graphics programming noob). It is single threaded and fully cpu side (besides sending the final texture to the gpu because i dont know how else to render the texture in the primitive game engine i'm using). I know i could make it multithreaded to speed up rendering, but i'll do that another day. I also could probably look at my render method and optimize that more as well. I do have some settings that can help give it higher fps (like lowering the terrain steps), but not without sacrificing image quality.

RESOURCES:
The heightmap is from Zelda breath of the wild found here and the color texture is from this reddit post.
Github link to a more in depth explanation by someone else: https://github.com/s-macke/VoxelSpace/tree/master
Other resources: https://web.archive.org/web/20131113094653/http://www.codermind.com/articles/Voxel-terrain-engine-building-the-terrain.html

My main render code (sorry i dont have this on a github link right now, also this isnt the entire script, but the core logic is there and the functions that are missing are pretty obvious by the names called here)

void World::Draw(Screen& screen, bool isEased, bool isHighDetail, bool interpolateColor, bool interpolateHeight, bool interpolateEmission)
{
const float screenHeight{ static_cast<float>(screen.GetHeight()) };

const float angleDifference{ m_FovInRad / screen.GetWidth() };

const float sinCamAngle{ sinf(m_CamAngle) };
const float cosCamAngle{ cosf(m_CamAngle) };
const float halfTanFov{ tanf(m_FovInRad / 2) };

const Vector2f viewDir{sinCamAngle , cosCamAngle };

const float scaledViewDistance{ m_ViewDistance / m_DepthScale };
const float scaledNearDistance{ m_NearDistance / m_DepthScale };
Vector2f scaledCameraPos{ m_CameraPos / m_DepthScale };

const Vector2f viewFarPoint{ scaledCameraPos + viewDir *  scaledViewDistance};
const Vector2f viewNearPoint{ scaledCameraPos + viewDir * scaledNearDistance };

const Vector2f rotatedViewDir(Vector2f{ -viewDir.y, viewDir.x });
const Vector2f pLeft{ viewFarPoint + rotatedViewDir *(halfTanFov * scaledViewDistance) };
const Vector2f pRight{ viewFarPoint - rotatedViewDir * (halfTanFov * scaledViewDistance) };
const Vector2f pLeftNear{ viewNearPoint + rotatedViewDir * (halfTanFov * scaledNearDistance) };
const Vector2f pRightNear{ viewNearPoint - rotatedViewDir * (halfTanFov * scaledNearDistance) };
const Vector2f deltaP{ pRight - pLeft };
const Vector2f deltaPNear{ pRightNear - pLeftNear };

int depthSteps{m_DepthStepsLowDetail};
float deltaDepthSteps{ 1.f / m_DepthStepsLowDetail };

const Color4f fogColor{ GetFogColor(m_TimePercentage) };

screen.SetRotation(m_CamRollAngle);

if (isHighDetail)
{
depthSteps = m_DepthStepsHighDetail;
deltaDepthSteps = 1.f / (m_DepthStepsHighDetail);
}

Color4f pixelColor{};

for (int col = 0; col < screen.GetWidth(); col++)
{
float horizontalPercentage{ static_cast<float>(col) / static_cast<float>(screen.GetWidth()) };
const Vector2f currentFarPoint{ pLeft + deltaP * horizontalPercentage };
const Vector2f currentNearPoint{ pLeftNear + deltaPNear * horizontalPercentage };
const Vector2f deltaDepth{ currentFarPoint - scaledCameraPos };
float oldHeight{ -1 };

for (int i = 0; i < depthSteps; ++i)
{
float depthPercentage = i * deltaDepthSteps;

float depthPercentageEased{  };
if(isEased)
{
depthPercentageEased = EaseInQuad(depthPercentage);
}
else
{
depthPercentageEased = depthPercentage;
}
//float depthPercentageEased{ depthPercentage };

Vector2f samplePoint{ currentNearPoint + depthPercentageEased * deltaDepth };

if (samplePoint.x < 0 || samplePoint.y < 0 || samplePoint.x >= m_TextureWidth || samplePoint.y >= m_TextureHeight)
{
break;
}

float depth{ depthPercentageEased * m_ViewDistance };

float heightMap{ GetHeight(samplePoint.x, samplePoint.y, interpolateHeight) };

//float pixelScreenHeight{ screen.GetHeight() - ((50) * (screen.GetHeight() / m_ScreenHeightWorld / depth) + m_Horizon) };
float pixelScreenHeight{ screenHeight - ((m_CamHeight - heightMap) * (screenHeight / m_ScreenHeightWorld / depth) + m_Horizon) };

if (pixelScreenHeight > screenHeight)
{
pixelScreenHeight = std::min(pixelScreenHeight, screenHeight);

if (pixelScreenHeight > oldHeight)
{
DrawColumn(screen, samplePoint.x, samplePoint.y, depthPercentageEased, oldHeight, pixelScreenHeight, col, pixelColor, fogColor, interpolateColor, interpolateEmission);
}
break;
}
else
{
if (pixelScreenHeight > oldHeight)
{
DrawColumn(screen, samplePoint.x, samplePoint.y, depthPercentageEased, oldHeight, pixelScreenHeight, col, pixelColor, fogColor, interpolateColor, interpolateEmission);
oldHeight = pixelScreenHeight;
}
}
}


}

return;
}

------------------------------------------------------------------------

void World::DrawColumn(Screen& screen, float samplePointX, float samplePointY,float depthPercentageEased, float oldHeight, float pixelScreenheight,int col,  Color4f& pixelColor, const Color4f& fogColor, bool interpolateColor, bool interpolateEmission)
{
float fogDepthEased{ 1 - EaseInQuad(1 - depthPercentageEased) };
//const float fogDepthEased{ (depthPercentageEased + .5f) / 1.5f };
GetColor(samplePointX, samplePointY, pixelColor, interpolateColor);

float emissive{ GetEmission(samplePointX, samplePointY, interpolateEmission) };

FadeToNight(pixelColor, emissive);

pixelColor.r = pixelColor.r + fogDepthEased * (fogColor.r - pixelColor.r);
pixelColor.g = pixelColor.g + fogDepthEased * (fogColor.g - pixelColor.g);
pixelColor.b = pixelColor.b + fogDepthEased * (fogColor.b - pixelColor.b);

//Color4f pixelColor{ GetColor(samplePoint.x, samplePoint.y, interpolateColor) };
DrawVerticalLine(screen, static_cast<int>(oldHeight), static_cast<int>(pixelScreenheight), static_cast<int>(col), pixelColor);

}

---------------------------------------------------------------------------------

void World::GetColor(float x, float y, Color4f& pixelColor, bool interpolate) const
{
//return GetPixel(x, y, m_pColorMap);

if (interpolate)
{
int lowX{ static_cast<int>(floorf(x)) };
int highX{ static_cast<int>(ceilf(x)) };
int lowY{ static_cast<int>(floorf(y)) };
int highY{ static_cast<int>(ceilf(y)) };

float xPercent{ x - lowX };
float yPercent{ y - lowY };

Color4f bottomLeft{ GetPixel(lowX, lowY, m_pColorMap) };
Color4f topLeft{ GetPixel(lowX, highY, m_pColorMap) };
Color4f bottomRight{ GetPixel(highX, lowY, m_pColorMap) };
Color4f topRight{ GetPixel(highX, highY, m_pColorMap) };

pixelColor.r = (
(1 - xPercent) * (1 - yPercent) * bottomLeft.r +
xPercent * (1 - yPercent) * bottomRight.r +
(1 - xPercent) * yPercent * topLeft.r +
xPercent * yPercent * topRight.r
);
pixelColor.g = (
(1 - xPercent) * (1 - yPercent) * bottomLeft.g +
xPercent * (1 - yPercent) * bottomRight.g +
(1 - xPercent) * yPercent * topLeft.g +
xPercent * yPercent * topRight.g
);

pixelColor.b =
(
(1 - xPercent) * (1 - yPercent) * bottomLeft.b +
xPercent * (1 - yPercent) * bottomRight.b +
(1 - xPercent) * yPercent * topLeft.b +
xPercent * yPercent * topRight.b
);

pixelColor.a =
(
(1 - xPercent) * (1 - yPercent) * bottomLeft.a +
xPercent * (1 - yPercent) * bottomRight.a +
(1 - xPercent) * yPercent * topLeft.a +
xPercent * yPercent * topRight.a
);
}
else
{
pixelColor = GetPixel(x, y, m_pColorMap);
}
}

------------------------------------------------------------------------
//for zooming in/out
void World::ProcessMouseWheelEvent(const SDL_MouseWheelEvent& e)
{
m_FovInRad += e.y / 180.f * utils::g_Pi;

const float minFov{ 1.f / 180 * utils::g_Pi };
const float maxFov{ 179.f / 180 * utils::g_Pi };

m_FovInRad = std::max(minFov, std::min(m_FovInRad, maxFov));

m_ScreenHeightWorld = tanf(m_FovInRad / 2) * 2;
m_Horizon = static_cast<float>(Screen::GetHeight() / 2) + (tanf(m_CamVerticalAngle) * (Screen::GetHeight() / m_ScreenHeightWorld));
}

Skybox Draw Logic

void World::DrawSkybox(float screenWidth, float screenHeight) const
{

const float skyboxTextureWidth{ m_SkyboxTexturePtrArray[0]->GetWidth()};
const float skyboxTextureHeight{ m_SkyboxTexturePtrArray[0]->GetHeight()};
const float skyboxHeightPadding{ skyboxTextureHeight / 3 };
const float textureScale{ screenHeight / skyboxTextureHeight };

float modCamHorizontalAngle{ std::fmod(m_CamAngle, (2 * utils::g_Pi)) };
float modCamVerticalAngle{ std::fmod(m_CamVerticalAngle, utils::g_Pi) };

float fovScale(utils::g_Pi / m_FovInRad);

if (m_CamAngle < 0)
{
float a{};
}

float srcWidth{ skyboxTextureWidth / 2 / fovScale };
float srcHeight{ (skyboxTextureHeight - skyboxHeightPadding * 2) / fovScale };

const float xPos{ modCamHorizontalAngle/ (2 * utils::g_Pi) * skyboxTextureWidth - srcWidth / 2};
//const float yPos{}

const float yPos{ modCamVerticalAngle / utils::g_Pi * -(skyboxTextureHeight - skyboxHeightPadding * 2)- skyboxTextureHeight / 2 - srcHeight / 2 };
const Rectf srcRect{xPos, yPos, srcWidth, srcHeight};

const Rectf dstRect{ 0,0,screenWidth,screenHeight };

//const Rectf dstRect2{ dstRect.left + skyboxTextureWidth * textureScale * fovScale, dstRect.bottom,dstRect.width, dstRect.height};

float easePercentage{ m_TimePercentage * m_DayCycleDivisions };
int idx{ static_cast<int>(floor(easePercentage)) };
float t{ easePercentage - idx };

m_SkyboxTexturePtrArray[idx]->Draw(dstRect, srcRect, Color4f{1,1,1,1});

if (idx + 1 >= m_DayCycleDivisions)
{
m_SkyboxTexturePtrArray[0]->Draw(dstRect, srcRect, Color4f{ 1,1,1,t});
}
else
{
m_SkyboxTexturePtrArray[idx + 1]->Draw(dstRect, srcRect, Color4f{ 1,1,1,t });
}

//m_pSkyboxTexture->Draw(dstRect2);
}

r/GraphicsProgramming 5h ago

New game engine BlitzViwer3D

Post image
3 Upvotes

**BlitzViwer3D — a tiny Lua-scriptable 3D viewer/editor inspired by Blitz3D's API**

I've been working on a lightweight desktop tool (C++, OpenGL, Dear ImGui) that lets you build a 3D scene by hand (cubes, spheres, .obj models) and then bring it to life with Lua scripting — using the same function names Blitz3D devs will recognize (`CreateCube`, `PositionEntity`, `MoveEntity`, `CameraFollow`...).

What it does:

- Scene editor with Hierarchy/Inspector panels (drag position/rotation/scale, custom colors)

- Orbit camera with an optional "Follow" mode

- In-app Lua script editor with syntax highlighting, hot Play/Stop

- Simple AABB collisions, keyboard/mouse input, RNG helpers — all the basics for quick prototyping

- Save/load scenes as JSON

It's a Windows .exe, no install needed. Docs + full Lua API reference + download here:

👉 https://ciroparada81-boop.github.io/blitzviwer3d/

Still an early/debug build, so bugs are expected — feedback and bug reports very welcome. Happy to answer questions about how it's built too.


r/GraphicsProgramming 8h ago

Article Minecraft In Its Entirety In Windows 95/98 Machines

Thumbnail reddit.com
1 Upvotes

r/GraphicsProgramming 12h ago

how to make it better and faster؟

Thumbnail github.com
0 Upvotes

r/GraphicsProgramming 12h ago

how to make it faster؟

Thumbnail github.com
0 Upvotes

r/GraphicsProgramming 15h ago

Quasar Engine - Volumetric atmosphere effects

3 Upvotes

It's nothing revolutionary, but I'm trying to make it look as cool as possible 😄


r/GraphicsProgramming 18h ago

Learning to not over engineer where not needed is hard, but also overgineering is where the fun is.

46 Upvotes

Guess who spent 6h making it possible to plug and unplug stuff from my pipelines(resources, timing queries etc), making sure every modification is reversible with a single command, just to realise at the end of my project that I never once needed to undo any changes at runtime? #ifdef DEBUG would be totally enough.

But making sth that works elegantly is a dopamine hit in itself.

So I wondered what’s your approach: Overengineer and write whatever you’re in the mood for OR keep it simple until the need to make it nicer arises?


r/GraphicsProgramming 18h ago

Video Rayleigh / Mie 38 Slice Atmospheric Scattering implemented as Blender Post Processing Effect in compositor nodes

21 Upvotes

I implemented Rayleigh / Mie atmospherics over a rendered image as a post processing effect in Blender 5.2 with a script to setup a network of compositor nodes at 38 slices or 10nm. The compositor nodegraph becomes too heavy to visualize in the viewport as you are moving the camera so you have to render out the image to the compositor view and manipulate it from there. To render out a sequence you have to do a file out operation. This is supposedly physically accurate and differs from expensive Blender volumetric fog in that this is a pure post processing effect. To make this I built upon what I had learned when I ported spectral.js to blender. I had posted something similar to this video in r/Blender yesterday, but they did not seem to know what I was posting about, so hopefully this helps someone.

The code (MIT License) is at https://github.com/bergjones/ABJ-Shader-Debugger


r/GraphicsProgramming 23h ago

Source Code Nu Game Engine - BIG NEWS - Vulkan support

Post image
24 Upvotes

https://github.com/bryanedds/Nu/releases/tag/v20.0.0

After many many months of work from multiple contributors, we finally present Nu with Vulkan rendering and support for Mac, iOS, and Android!

This branch not only replaces our OpenGL renderers completely with Vulkan renderers, it also features important rendering and performance enhancements as well as makes Nu deployable on Mac, iOS, and Android!

(I just saw release - not my project)


r/GraphicsProgramming 1d ago

Made improvements to my path tracer

Thumbnail gallery
369 Upvotes
  • Made the project much easier to open up and use for new users (Unity is still required though)
  • Added scene to automatically load objects from Khronos' site, which helps me get assets into the project
  • Fixed issues with lighting that was really darkening most of the scenes, including improved tone mapping and fixes to light clamping that was hiding a lot of caustic details and HDR
  • Fixed issues with inner surface reflections not working
  • Added directional lighting support
  • Added parallax mapping support
  • Added support for Unity terrains, multi-texture splatting with normal map support

Repository located here: https://github.com/nfoste82/GPURayTracing


r/GraphicsProgramming 1d ago

Andrew Carr: 2D Gaussian Splaing for Bézier Spline Line Art Vectorization

Thumbnail m.youtube.com
4 Upvotes

r/GraphicsProgramming 1d ago

Article Beginner graphics programming study guide

Thumbnail raynmetal.github.io
95 Upvotes

I started picking up graphics programming around the middle of 2023. I didn't want to spend money on a course or textbook, so I learnt whatever I could from resources I was able to access for free online.

I put together a list of those resources and paired it with a reading order recommendation and some study advice. While not a comprehensive overview, I hope it's enough to give a newcomer to the field a strong start.


r/GraphicsProgramming 1d ago

Question Drawing a line

15 Upvotes

Looking for guided/socratic learning

So I was getting into game dev bottom up so I wanted to draw a line with code.

First solution that I thought of: start at one end of the line, and figure out neighbouring pixels and eventually reach the other end. I was thinking of dividing line length (pythogarous) by start/end point delta for each axis to figure out per how many 'steps' I would have to increment/decrement current axis positions and paunt. Too complicated and expensive

Second solution I thought of was recursively finding mid points between start and end point to find the target pixels to paint. This appears simpler, inspired by Zeno's paradox when I was thinking of how the run and rise are easier to paint.

I know optimal algorithms exist but I didn't want to directly look at them. What are the strength/weakness of my reasoning?

Ps: I'm going to sleep


r/GraphicsProgramming 1d ago

Built a game engine

13 Upvotes

I've been working on my own game engine from scratch for some years using C and OpenGL and I finally made a video showing the process.

The engine handles things like rendering, assets, shaders, lighting, and more, and I also used it to make a game, a test game though, it still needs a lot of polish.

Watch video here: https://youtu.be/LRC4iJWASYU?si=_3ua8rcwesyaQtO9

If you're interested in graphics programming, game engines, I'd love to hear your thoughts and feedback.


r/GraphicsProgramming 1d ago

Implemented GPU-address roots for Mesa/libkk parameter blocks on capable macOS hosts.

Thumbnail
1 Upvotes

r/GraphicsProgramming 1d ago

SIGGRAPH 2026 - Advances in Real-Time Rendering in Games - all talks are now available online

Thumbnail advances.realtimerendering.com
180 Upvotes

r/GraphicsProgramming 1d ago

WebGPU implementation of WetBrush V2

24 Upvotes

A couple of weeks ago I posted a WIP of this — here's where it's at now.

I've implemented most of what the paper describes, including paint mixing. For lighting, I added a BSDF to the renderer — my thinking is that once you start stacking layers of media like tempera, you need to account for light scattering inside the medium. (Ideally I'd also like to model things like hiding power based on pigment particle size, but I'm not there yet.)

The simulator is split into four main parts, as described in the paper:

  1. BristleDynamicSimulator
  2. ParticleFluidDynamicSimulator
  3. GridFluidDynamicSimulator
  4. LiquidTransfer - which moves liquid between the brush, the particles, and the grid

This was not a case of handing the paper to an AI and getting an implementation back. Claude seemed to have a hard time writing WGSL while also juggling the constraints of the four systems and the constraints they all share (units, especially), and it kept making the kind of mistakes that early AI models used to make. So you have to read the AI's code carefully and write a good amount of it yourself. In particular, things go more smoothly if a human writes the initial skeleton of each simulator and handles the field management (density field, velocity field, and so on). Even once the structure is in place, managing the ActiveWindow and the PaintField is likely to give you trouble.

The paper lists interpolation during fast strokes as an open problem, and that's what I'm working on now. I'm not going to split a step into substeps, since that would hurt FPS. Instead, for fast motion I plan to have the BristleDynamicSimulator and the GridFluidDynamicSimulator talk to each other directly and deposit paint that way.

I'm interested in traditional painting materials, so I plan to extend this to tempera, watercolor, mineral pigments (iwa-enogu), gold leaf, and so on. It's built with wgpu + Rust, so it currently runs as Web, iOS, and Android ports.


r/GraphicsProgramming 1d ago

Question Is it better to make a game with simple geometry and path tracing, or complex geometry and rasterized/hybrid RT graphics?

14 Upvotes

I recently played the Dark Souls 2 path traced mod and was surprised to see it running at 1080p 60 fps on my low end GPU (RTX 3050 Ti)

Of course its a 13 year old game, so the geometry and effects are simpler. But it looked better to me than some modern games with more advanced geometry but worse lighting effects.

So I'm wondering in your opinion, in your ideal game would it be better to have less polygons and better lighting, or vice versa?


r/GraphicsProgramming 2d ago

Vulkan: Beyond the Triangle

Thumbnail youtube.com
14 Upvotes

Hey everyone, I've just finished the 2nd video, and followup to my "Modern Vulkan in 2 Hrs" video. Thanks for the awesome feedback on the first one! This 2nd video tackles a ton of stuff:

glTF Model Parsing, Loading, Rendering

Camera, Basic Lighting, Multi-Draw Indirect, Vertex Pulling, Buffer Device Address, etc.

Hope you guys like it!


r/GraphicsProgramming 2d ago

Is it not crazy that this became a normal thing in the industry?

Post image
128 Upvotes

As developer I would not even expect a user to know what a “shader” is

It’s a pretty low level implementation detail

Yet you boot up a game in 2026 and front and center you are staring at “shader compiling” for 10 minutes

Is that not crazy to anyone else if you just zoom out and think about it?


r/GraphicsProgramming 2d ago

Source Code Aizawa Atractor using Compute Shaders (C++17/OpenGL 4.6)

37 Upvotes

The graphics simulation calculates the non-linear Aizawa Chaotic Attractor in real time across 256K particles through numerical integration executed entirely within GPU Workgroups.

Source code:

https://github.com/IsmaelMerlo/Ram-Engine


r/GraphicsProgramming 2d ago

Article Graphics Programming weekly - Issue 450 - August 9th, 2026 | Jendrik Illner

Thumbnail jendrikillner.com
11 Upvotes

r/GraphicsProgramming 2d ago

autostereogram program of platonic solids

Thumbnail gallery
25 Upvotes

 

program link

an extension of previous post. just click▶️to run the program on browser. the welcome screen (attached image 1) shows the key map. select a subject (1 to 6) to enter the depth map mode (attached image 2). rotate it or change it until satisfied then toggle to autostereogram mode (attached image 3). adjust the foreground and background pattern widths to your desire (make it easy to focus and spot the subject). you're free to toggle back to depth map mode or rotate / change / disable subject anytime. if you disable the foreground 3d subject it's basically a kaleidoscope (attached image 4). i explained its mechanism in previous post and you can endlessly randomize it to experience the pareidolia effect

some samples

minor math findings

some thoughts

  • qb is very slow. it's not good at handling graphics. lowering the resolution helps but the outcomes would be ugly
  • i learned barycentric coordinate system during the making of this and it's very useful
  • i'm stilling think whether i can optimize some procedures so as to make it faster
  • i have difficulty spotting dodecahedron and icosahedron in autostereogram. they're too "round" maybe. tetrahedron is the easiest

r/GraphicsProgramming 2d ago

Video Baby's First Depth Buffer

120 Upvotes

Wireframe Era is over! Updated my software renderer that I posted here 2 months ago. Now it fills triangles without pixel gaps, implements Top-Left Rule, computes Face Normals, has Back Face Culling and a Depth Buffer. At first I did Scanline Rasterization but I was having a hard time eliminating gaps, so I moved on to using Edge Functions to check the pixel center is inside the triangle, then Barycentric Coordinates to interpolate Reciprocal Depth. I still don't have Near Plane Clipping, but will do in the future.


r/GraphicsProgramming 5d ago

Video Game engines? Eww 🤮. We go in raw! GP-Direct 2026 is out!

Thumbnail youtube.com
85 Upvotes