r/GraphicsProgramming

▲ 4.7k r/GraphicsProgramming+1 crossposts

Depth-aware light injection all running in browser

I got a 448x448 monocular depth model down to ~8 ms on my M4 Pro across ~250 dispatches, which is fast enough to use in realtime :D
Since the inference is written directly in TypeGPU, I can just feed the depth buffer straight into the lighting pass. It never has to leave the GPU or go through any extra synchronization/interop step

Inference, lighting and draw all go through the same command encoder.

Credit: X reczko_konrad

u/AI_ReleaseTheFIles — 9 hours ago
▲ 304 r/GraphicsProgramming+1 crossposts

Konrad Reczko's "Monocular Depth Injection" in TypeGPU is live!

My collegue Konrad Reczko recently shared a weekend project he made using TypeGPU, and it's now open-source and ready to play with in the browser: https://typegpu.com/examples/#example=image-processing--monocular-light-injection

It estimates the depth of a scene by inferring the DepthArt model with custom TypeGPU kernels, then reconstructs normals based on that depth information, and uses both to relight the scene, all in the same command encoder. For more information, check out the original series of Tweets:

https://x.com/reczko_konrad/status/2089670934009413751?s=20
https://x.com/reczko_konrad/status/2090472091149648121?s=20

u/iwoplaza — 10 hours ago

Real time AO in ray marched voxel worlds

Hello everyone. I'm in the process of building a minecraft-like voxel game, however unlike most implementations, I wanted to test it out using ray marching as the primary rendering technique.

Yesterday I developed an algorithm that calculates ambient occlusion by sampling the 9 neighbors directly in front of the face that was hit by a ray. The core idea is not too different from how most ray marched voxel renderers implement it, but I see most implementations rely on calculating the distance between the uv coordinate and the neighbor's uvs to act as the weight (the smaller the distance, the greater the ao)

In my version, I realized I only really care about the existence of the neighbors, since the uv coordinate of the face that was hit gives you all the information you need to know. We use the uvs to calculate weights for each combination of the edges of the face. For example, the bottom edge could be the inverse of the y coordinate, (so if uv.y = 0.2, then b= 0.8). Then the influence of the neighbors is just some combination of these weights.

So no distance calculations are needed.

The best part of this is that since the face's uvs are pre-calculated, the weights can be too. This makes the neighbor checking loop very minimal. We simply check for the neighbor's existence, and if its there, we add the corresponding weight to a running sum.

After that, we map the weight to a value between 0.0 and 1.0 to be used as the ao factor.

This worked surprisingly well, and is not too expensive. Here are the results:

My AO implementation on its own.

My AO + Textures (borrowed from Clarity 32x)

I'm sure it could be optimized further. On my system however I find that it's plenty fast enough.

Does anyone know of a similar technique?

reddit.com
u/coolmint859 — 16 hours ago
▲ 322 r/GraphicsProgramming+2 crossposts

Interactive Inception Map - Try it!

Been experimenting with Gaussian splatting for spatial interfaces and ended up building this little interactive demo.

It basically warps a 3DGS scene into an “inception map” type view, where you can see the space around you while also getting more of a top-down overview further away.

You can move around, rotate the view and toggle the effect on/off.

Try it here:
https://www.orbify.eu (link edited to load properly)

Still very much an experiment, so I’d be interested to hear what you think. Does this kind of view make sense to you? Any use cases or datasets you think would be fun to try with it?

u/bitruvius_ — 1 day ago
▲ 106 r/GraphicsProgramming+3 crossposts

Improving Rendering Distance in my Micro Voxel Engine

For the past three weeks, i’ve been working on hard improving the render distance in my Micro Voxel Engine, particularly due to the feedback of having N64 viewing distance 😅

I’m pretty happy with the end result of increasing render distance from 300m to ~10-15km, while running at 45-50 FPS on an Apple M1 Pro.

Note: this engine uses meshing rather than RT/DDA.

— Macro chunks —

All chunk generation functions now include a sieve function to automatically be able to generate at 1/N resolution without any changes. This also applied to generated features and stamps, enabling chunks to be generated at any resolution without downsampling.

Macro chunks also independently record and resolve local edits. They are saved (and cached) independently so that terrain edits are maintained without needing to maintain the full res copy in memory.

The lower band LODs are very quick to generate. At this point I could add even more bands, and a 1/64 res chunk takes the same time to generate as a high res chunk, but covering huge distances.

— LOD transitions and adaptive fog —

I primarily use transient transitions where the detail levels fade between each other once, rather than a continuous gradual transition, as this is around 30% cheaper on the GPU and looks “nearly” as smooth in most scenarios.

Adaptive fog scales the effective draw distance dynamically based on loaded bands. Bands generate from high to low so during fast motion, if needed, we temporarily reduce draw distance until chunks have loaded.

— Macro chunk cards and props —

This was the hardest part, keeping identical prop coverage for trees and items without needing to instantiate millions of entities:
-Macro chunks retain a list of props whose IDs are deterministic based on position and type. If the real entity is destroyed, we can map this to the macro chunk set and remove. Likewise for newly spawned props.
- grass and foliage do not map 1:1 with the actual loaded props, but follows the same generation pattern, so technically there will be disparities, but a good trade off to avoid millions of tracked grass items.

youtu.be
u/MGMishMash — 1 day ago

Kleinian Drift

The shape is real math, not modeling. It comes from Kleinian groups.

Idea came from the movie "Cube" and designed to be a dynamic maze.

u/Far-Employee-9531 — 22 hours ago
▲ 169 r/GraphicsProgramming+2 crossposts

Wind Tunnel Simulation | Vulkan and C++

It’s the first step in my attempt to simulate an F1 car. Right now, the simulation is very low-resolution and quite slow, so there’s still a lot of optimization to do. It’s also my first time working with compute shaders, so there’s plenty to learn and improve along the way.

u/ThatTanishqTak — 2 days ago
▲ 17 r/GraphicsProgramming+3 crossposts

I built a Vulkan game engine from scratch in Java over the past few months — open-sourcing it in late September

I've spent the last few months building CryoTheatre, a 3D engine written in Java on top of Vulkan via LWJGL — no middleware, the rendering pipeline is built from scratch. It's heading toward its first public release, v0.1F, in late September, and it'll be fully open source.

Rendering-wise it's got a full deferred pipeline — IBL (diffuse irradiance + prefiltered specular, split-sum approx), CSM(cascaded shadow maps), reflection probes with parallax correction, bindless + streamed compressed textures so load times don't die. Transparency's been the pain point — running MBOIT and still fixing some edge cases here and there. There are a couple problems on Volumetrics too, so any help is very much appreciated.

On the tooling side there's a full custom editor built straight on Vulkan (asset browser, live previews, material inspection), plus LightBulb — a little scripting language I built that transpiles to Java and hotswaps at runtime, so you're not restarting the project you're working on.

It's a solo project (with help from a couple of collaborators on art and community side — shoutout to the small crew helping test and give feedback). Licensed under GPLv3, so the full source will be on GitHub at launch — you're free to dig through it, learn from it, or fork it, with the usual copyleft terms if you build on it directly.

Download links will go live on the project site once v0.1F ships; for now the GitHub repo will go public alongside it. If you want to follow progress before then or ask questions about any of the systems above, there's a Discord for the project in the comment below so that moderation doesn't think this is a spam.

Happy to answer anything about the rendering pipeline, the editor architecture, or the scripting system — this is very much a "figure it out from scratch" project and I'm glad to talk through what worked and what didn't.

u/Xelvant — 1 day ago

I implemented "Spherical Harmonic Exponentials for Efficient Glossy Reflections" in D3D12

I implemented Activision's new SH reflections paper in D3D12 and released the code on github!

This tech is a little bit different from normal spherical harmonics, and there are 4 main differences:

  1. They use log space instead of linear space for the lighting, which reduces ringing and enables #2 and #3 to actually work.
  2. Instead of using a circular symmetry assumption (i.e. N=V=R) as with the split sum approximation used for IBL, they instead factorise a pair of spherical harmonics, with an Order 4 SH parameterised by the reflection vector, and an Order 2 SH parameterised by the halfway vector.
  3. To enable a continuous roughness representation, they convolve the coefficients (or rather, the basis function) by the von Mises Fisher kernel which takes 1/alpha=1/roughness^2 as a parameter.
  4. To actually obtain the spherical harmonic coefficients we have to collect samples for several normals, views and roughness levels (or more specifically alpha levels since we're using linear roughness, not perceptual), and then optimise the coefficients using least squares.

My code does this all end to end with HLSL compute shaders, even the least squares optimisation, and we achieve above 95% MSE compared to a raytraced ground truth for roughness in the range [0.5, 1.0], which actually beats split sum IBL.

Only downside is for roughness below 0.5 the spherical harmonics simply don't have enough detail for accurate reflections... HOWEVER, when applied to "bumpy" low roughness surfaces (like the leaf textures at the beginning of the video) you can hardly see a difference, so this effect is only apparent for flat surfaces and surfaces with near zero roughness.

Activision got their SH representation down to 400 bytes, but I went further using 16 bit packing to get down to 208 bytes which gives us better performance due to fewer memory loads. The 16 bit implementations come in 4 flavors: emulated 16 bit for older GPUs and native 16 bit, and SRV packed vs CBV packed. There also exists a 10 bit packed SRV flavor, but the extra bitshift work ends up being slower.

On my RTX 2080 Super and my wife's RTX 4070 Super, the native 16 bit CBV packed shader runs the fastest, and compared to the IBL version it is only 0.1 milliseconds slower while using 2000x less memory!

u/Avelina9X — 2 days ago
▲ 9 r/GraphicsProgramming+4 crossposts

New Version Available

A new version of CyberVGA released on itch!

This version has:
Static octree based renderer
A separate 64-bit Windows port

You can use editor, save the world you created and see in action using VIEWER module.

expfunction.itch.io
u/exp_function — 2 days ago
▲ 81 r/GraphicsProgramming+2 crossposts

Minecraft In Its Entirety In Windows 95/98 Machines

This program is basically an beta recreation Beta 1.7.3 entirely developed in C89 Open Watcom V2.0 C code and uses only Open GL 1.1 Rendering. Newer versions of the program are even a hybrid between Beta 1.7.3/Release 1.2. This program was intended to run on Windows 95/98 machines, but it can also even run on newer windows. Documentation is included

Almost every implementations from the version was converted and implemented into C including world behavior. gameplay, visual style, GUIS, redstone/piston systems, "entities", and even able to load, render, and save actual Java worlds properly. The newer version supports enchanting, potions, newer blocks/items, and more. The custom rendering system, extensive frustum culling, lazy chunk updates, and more were key to achieving good performance and reaching hundreds of FPS in systems even in high render distances.

This program is intended to run on Windows 95/98 machines specifically, but can also run on modern windows and could be ported to other systems, it uses mcp43 for java files as reference and core boilerplates development as key with some conversion tools used, however much of the custom code implemented or even added and gl 1.1 rendering is described below.

When I first initially started this project, I used a tool to change the extensions of the java files from .java to .c while also deleting its main data along and also developed proper h files and unity manifests. However for some reason, it also left a bunch of junk metadata which I used as a bar to figure out the most importance of what implementations and code were needed to be added first to look and run like the real deal over time. Many of the files have just seemingly metadata, but many of them still have functional code and updated to run like the real deal. Files that were fully implemented and basically converted were called Direct C89 port of a java file. The entire program uses clean implementations and reverse engineering to prevent some level of copyright issues.

One of the key things actually developed, was the custom chunk rendering system and performance systems to make sure it looks and runs like the real deal. Extensive hidden face culling was implemented which hided faces of blocks not visible to the player. Another key thing developed was when the chunk geometry and its objects are compiled into Open Gl 1.1 lists, the CPU, instead of sending calls for every single vertices per frame which used to be really bad during development, but now only send a few calls to be processed, meaning the program uses its completed lists to render unless changes like block placing/breaking, lighting change, neighbor chunk changes, and more happens. The worlds uses the standard procedural terrain generation and Perlin and octave noises from the java version, biomes and their temperature code, standard placement of blocks like trees, grass, flowers, gravel, stone, sand, overworld/nether rendering, and more. The geometry has separated into three groups, opaque terrain such as stone, and dirt, clear geometry like water and ice blocks,. and cutout geometry which is basically objects/items that are seemingly 3d, but are see through like grass, ladders, iron bars, and more which all were manually configured and separated to allow graphic processors mainly to process them efficiently in open gl properly. There is still a bit of bottleneck of CPU and GPU performance especially in way newer systems as open gl 1.1 uses immediate mode for rendering and newer versions like Open Gl 1.5 actually do support VBOs and actually skip draw calls when needed, but performance is still relatively decent for its codebase.

Chunks were rendered in this version using nearest-first chunk admission which loaded nearest chunks closest first before loading chunks first which prevented extreme lag and frame issues. Caps were also implemented in the memory management and in how many meshes could the world use. Just like in the java version, it runs at 20 ticks per second to allow the world and entities to function as normal and made sure to not tie in with the FPS rate. Mathhelper files from the java version were also converted and developed to have much more efficient equations and perform better mathematical equations in trigonometry, mainly for graphics rendering, 3d movements, and block placement which was important to getting good performance on older hardware.

The NBT and McRegion storage code was also really tedious to convert and was initially very unstable to use. When a chunk was updated, like placing a block or dropping an item, it became a dirty chunk which needed to be meshed and be converted into graphical data, when it needed to be saved, it used to take very long as it saved somehow even entities in chunks, but it was later excluded and rendered individually as individual 3D models.

Neighbor-border invalidation in more detail was also implemented properly to help save processing power and tells chunk to just recalculate its edges when touching another chunk. The lighting system and their algorithms were also implemented and converted to be just like from the java version that allowed different light levels and detect sunlight code to initiate any chunk updates, however it was simplified a little to make sure it wouldn't lag out the system. A render distance meter was actually introduced, like in the newer java versions instead of the original used java version, tiny, medium, and far distance which allowed the player to see more of the world which for some reason during development, it kept being too short. Now for the Open Gl 1.1 code, display lists were used which compiled all the color and vertex data in a chunk in a single list where the CPU would just send a couple of commands instead of the hundreds if not thousands of commands to draw and send the many vertices, which was a bit bugged and actually heavily lagged out the game during testing.

glGetError which used to initiate every frame and bog down the system, it was now removed and later coded in to sample every 128 frames. The check of whether or not chunk graphics list were valid were removed along with replacing the glGetFloat which pulled data back and forth to Cpu and GPU which could slow down system, to just use the earlier calculated frustum matrices and render that. A 3x3 read only chunk neighborhood was implemented where it basically copies around 9 chunks to read only memory to prevent world corruption and process the data to then build a visual mesh. This fixed much of the stuttering and lag issues that were in earlier versions.

The physics and collider systems entities and blocks were using it exactly as the java version which was converted This was how mob attacks, water/lava flow, and more related were implemented to look like the real deal. The day and night cycle is around 24,000 ticks which was exactly implemented in the program and dropped items despawn around 6000 ticks in loaded chunks.

Now for networking and multiplayer support, nearly all packet files were implemented from the original java version and converted into C code, though most of its related and needed code is concentrated in packet.c and the rest is a bit metadata. It currently only uses/supports Winsock 2 code which was like around Windows 98 and above systems, windows 95 could have it if it has winsock 2 dependencies. Restarting the games a couple times and waiting between time intervals like from 1-3 seconds before reopening seems to fix some ghost networking errors.

All the 45 GUI files were able to be converted and later developed to make it all work in Open Watcom, and work under windows 95/98 restrictions. An additional meant usable button was added later on in the controls section which is the double sprint toggle button which can be on or off.

For memory allocation, there were different memory modes developed depending on the Windows OS chosen, for example, Win98 has a max ceiling ram limit of 128mb, 48 chunks renderable, 8-12mb chunk budget, and 20 mb for CPU mesh budget. NT operating systems which usually have a decent amount of Ram has max ram limit of around 256mb, 80-96 render distance limit which was the max the render distance can go currently, 20-32mb chunk budget, and around 64-128 mb CPU mesh budget. Networking queues were even also limited, going from 512kb in lower end windows 98 machines to around 8 mb on modern machines. The memory management and pointers were really hard to develop and there were hundreds of versions before this that just kept crashing the game.

The assets folder which contain all the textures and atlases for the game uses mainly pngs for newer windows and tga or older windows. The texture for blocks mainly uses terrain.png while items use items.png. TexturePackList files in the java version were later on converted to also have support for texture packs for mainly beta 1.7.3. There is however a glitch, especially in newer versions of the game where the sounds and music folder in the assets folder would cause the game to crash, especially in Win9x compatibility mode from some mp3 issues, though it may not happen on a real win9x system, if it somehow crashes the game, renaming the sounds and music folder in the main assets folder to something gibberish can help, but there would be no sound. Initially during development, the music and sounds were fillers, and there are some real songs and sounds from the game. Texture UV mapping also wasn't really thought of until V3 and then later V5 which now has proper java limb math and uv mapped textures from the atlases. Even still, there are some slight gameplay glitches and rendering issues, but it is overall stable and playable.

The bare requirements for running the program, especially the stable V135 version is just having the exe file as well has having a texturepacks folder with a texture pack zip file inside of it. This should work and even stop some crashes on Win9x machines however it may be missing some icons and textures for some items and GUIS which are in the actual assets folder.

Minimum Requirements: OpenGL 1.1-compatible graphics adapter, Approximately 16 MB video memory, recommended is 64-128mb of vram Working 16-bit or 32-bit color desktop mode Approximately 800 MHz Pentium III, Athlon, or comparable x86 processor "Possible to Work On Windows 98" but text rendering may fail Can Work On Modern Windows As Well Around 256mb of Ram Hardware texture support A stable vendor OpenGL driver is strongly recommended, but Open Gl 1.1 can work Module-registration manifests Asset paths Version and executable-name definitions for it to run.

This program uses only imports of WS2_32, OPENGL32, GDI32, USER32, WINMM, KERNEL32, and WS2_32 which I later on figured out it can be relatively easily to be converted or even able to run with a x86-64 emulator on Linux.

I gave the project a very basic name, called CloneMC V2.0 which might change soon, the repo is still a bit of a mess, but the source code files and the executable, along with other files like the makefile is all in the zip file. There is a lot more info about this there.

This project was more or less like a side project with an efficient CAD program being the main goal for the test creation of this project. The License Example file is provided if needed to use any of the java files for references or for adding features, however understanding of the code and custom code may be needed currently to implement any additional features/bug fixes/missing implementations from the java version.

https://github.com/sworks692/CloneMC-V2.0-Entirely-In-C-and-Open-Gl-1.1/tree/main

u/sworks694 — 3 days ago
▲ 19 r/GraphicsProgramming+1 crossposts

How i create a working OpenGL 3.3 renderer with normal maps, Blinn-Phong specular, and post-fx with no C++ knowledge and no source code, using an AI coding assistant

Unreal Tournament 2004 is an undisputed classic. But graphically it's frozen in the era of Direct3D 8 and flat textures — the Fixed-Function Pipeline. I always wanted to see what the legendary Unreal Engine 2 could look like with proper normal maps and honest Blinn-Phong specular, something closer to Unreal Tournament 3.

1 - diffuse only, 2 - normal, 3 - Blinn-Phong specular

In the fan and modding community there are plenty of failed attempts to bolt normal maps onto UT2004, but it was always considered impossible — no source code, fundamental engine incompatibility with modern techniques, and the sheer complexity of low-level graphics development. Then AI and agentic coding tools changed the picture.

After OldUnreal released their patch moving the game to full 64-bit and adopting OpenGL 3.3 with broad compatibility across both vintage and modern hardware, I finally had a real foothold for the experiment.

There were a few obvious obstacles going in:

I don't know C++. Not its idioms, not its syntax — I can barely read someone else's C++ code, let alone write it.

I had no game source code. (Yes, leaks from Unreal Championship 2 and Unreal Warfare exist, but the codebase differs substantially from UT2004.)

Zero knowledge of the engine's internal architecture or its quirks.

On top of that, buying a Claude Code or Codex subscription wasn't an option. I ended up working with Gemini Flash through the command-line Antigravity CLI — the cost of entry being roughly $6/year.

Still, after three months of constant experimentation, reverse engineering, and hundreds of Access Violation crashes, I managed to write a native DLL renderer module — ENDrv.dll. Here's the story of how it was done: through Antigravity CLI, UE2 architecture hacks, a lot of patience, and more sleepless nights than I'd like to count.

---

Stage 1. Flying Blind: Reconstructing the VTable and Getting the First Frame

Any renderer for Unreal Engine 2 is an external dynamic library that must implement the engine's internal URenderDevice interface. In plain terms, the renderer DLL is the bridge between the game engine and the GPU. The engine communicates with the library through virtual function tables — VTables.

In C++, the order of functions in a VTable is critical down to the byte. If you're off by even four bytes on a single function's offset, instead of calling the frame-draw routine the game jumps into a random region of memory and crashes instantly.

[ UE2 Engine ]  --->  ( VTable: offset +0x18 )  --->  [ ENDrv.dll ]
                +--       Off by 4 bytes?       ---> CRASH (Access Violation)

The first two weeks were pure guesswork: running UT2004 binaries through a disassembler, studying the UC2 and Unreal Warfare source leaks, trying to work out which functions UT2004 expected and at exactly which offsets, rebuilding the library and launching the game every five to ten minutes, logging crashes, and nudging offsets based on the fault addresses.

The game launched for the first time to a solid black screen without crashing — only after I managed to correctly guess six of the key base functions out of 32 (at that point I could only confirm 14 functions with any confidence). No image yet, but the DLL was accepted by the engine. That was the first real breakthrough: not only did the game stop crashing, but you could hear it working — clicking through menus, launching a match "blind," and hearing bots run and shoot each other. It was alive.

The next few days went into establishing vertex streaming and getting any geometry to appear on screen in wireframe mode. The stub functions that were just returning True or Null had to become real implementations. Things were complicated further by the fact that the engine was designed around DirectX, while OpenGL has its own coordinate system conventions and state-management model. At first all I got was a mess of random lines, but after dozens of iterations the DLL finally produced a correct wireframe.

---

Stage 2. From Fixed-Function to GLSL: Materials, Combiners, and Static Lighting

The original UT2004 handled materials through the old GPU state machine — the Fixed-Function Pipeline with its Combiners, Shaders, and FinalBlends. Modern OpenGL 3.3 Core Profile has no fixed pipeline at all: everything has to be written in GLSL shaders.

About four to six weeks went into reconstructing material logic from scratch. The UC2 and Unreal Warfare sources were nearly useless here — worse than useless, actually, because the AI kept getting confused by them. At times I had to pipe every agent response through a second AI just to sanity-check the logic and catch hallucinations. Eventually I decided to ignore the foreign source code entirely and work everything out independently.

The hard part wasn't "just drawing a texture." The UE2 material system is a full node graph. A single UShader material can have separate slots for Diffuse, Opacity, Specular, SpecMask, SelfIllumination, and SelfIllumMask — each of which can itself be another material. The Diffuse slot might contain a UCombiner blending node that has Material1, Material2, and a Mask, each of which can in turn be another Combiner, a texture with animated UVs (TexPanner, TexRotator, TexScaler), or a cube map. All of that had to be unrolled into GLSL, which doesn't support recursion or dynamic loops from that era — only a linear graph with a fixed number of steps.

The solution was a "combiner register" system: before the main color calculation, the shader sequentially evaluates up to 32 nodes and stores the results in an array. Each node knows the indices of its inputs (a texture slot or another register), the blend operation (Multiply, Add, AlphaBlend, Modulate, etc.), and its mask. On top of that: 16 texture slots with independent UV transform matrices, cube map reflection support, projective textures (Projector), animated panoramas, and FinalBlend mode with alpha testing. Near the end of the texture work I found the 32-bit game source code — but it wasn't much help since the original was entirely tied to Direct3D 8 / Fixed-Function. It did help fix a couple of nasty logic bugs involving complex layered textures, alpha-channel materials, and depth buffer issues.

One example of a creative workaround: UTexture field offsets are unknown without source code, so the code locates them at runtime by scanning memory and checking logical conditions. Once found, they're cached permanently.

Post-processing was integrated in about a week after that with almost no friction, since screen-space effects (Depth of Field, Bloom, Chromatic Aberration) are fairly straightforward to write in GLSL and have little dependency on engine internals.

Bloom on Bio Rifle's globs

---

Stage 3. The Hardest Part: Normal Maps, BSP, and Optimization

UShader with specular and normal map on BSP geometry

The whole point of the project was proper PBR-style lighting and normal maps. And this turned out to be even harder than the original VTable guesswork.

For normal mapping to work, the renderer needs per-polygon tangent and bitangent vectors from the engine — and the engine simply doesn't provide them. Getting the effect into the shader was actually quick: screen-space derivatives (dFdx / dFdy) let you compute a tangent basis directly in the fragment shader, eliminating the need to change the game's vertex format. But the performance was catastrophic: a couple dozen meshes with normal maps in the frame would bring the game to its knees.

The core problem: all the static lighting in the game (there's almost no dynamic lighting at all) is baked into the level at build time — texture color and brightness are modulated by lightmap textures and per-vertex color. Roughly speaking, it's like applying a gradient to a texture in Photoshop's Multiply blending mode. The renderer doesn't "see" any light sources at all — it just draws textures onto polygons. But since the DLL is a native module running inside the game process, it has direct access to everything happening under the engine's hood: light sources can be found and their parameters read directly.

Recalculating every light source per pixel every frame for all geometry was too expensive — especially since the engine's architecture hard-limits it to a single CPU core. A deep caching strategy was necessary: all geometry preserves its original static lighting (lightmaps and vertex colors) baked by the map authors 25 years ago. The dynamic per-pixel GLSL calculation only handles the normal map delta and the Blinn-Phong specular highlight.

Final color = (Lightmap * Diffuse) + (NormalMap_Affect * DirectLight) + BlinnPhong_Specular

Where NormalMap_Affect isn't the full light value, but only the "uplift" from the surface relief — how much the normal direction deviates from flat geometry. The lightmap stays as the foundation: the 25-year-old author lighting doesn't go anywhere, it just gets a relief "cap" on top.

Even that wasn't enough for acceptable framerates: with a few hundred meshes carrying normal maps on a level, FPS would collapse to 10-20. The entire pipeline needed its own caching layer. In effect, on top of the already-baked static lighting, we additionally bake normal map data during the first few frames of a map load.

The BSP Problem

If caching for StaticMesh geometry came together relatively quickly, BSP wall surfaces fought back with everything they had. Unreal Engine 2's architecture slices BSP surfaces into dynamic nodes during the frame itself. The engine resisted caching at every turn: texture coordinates would drift, caches would invalidate every frame, and the normal map and specular effects would flicker in and out alongside a full zoo of visual artifacts — or an FPS collapse into single digits. I had to roll back to week-old backups several times and redesign the architecture from scratch. The problem was eventually solved by building a light-source state hash table and an array of 128 static-dynamic lighting slots directly in the shader, tied to the geometry streaming.

---

Just shipping the renderer upgrade wasn't enough without a proper visual showcase, so I ported two maps from UT3 to UT2004. Compared to building the renderer itself, this was a walk in the park. The UT3 maps were chosen deliberately: they have clean UV unwraps and high-quality next-gen normal maps — exactly what was needed for the demo. The BSP geometry, static meshes, their properties, and placement were ported in five to ten minutes with a couple of CLI prompts. The meshes were converted from pskx to ASE format using a 3ds Max script that the AI assembled in about five minutes, after reading the UModel documentation itself and extracting everything needed into a separate folder. Importing models and textures into UnrealED went the same way. The only thing that actually required real hands-on effort was assembling the shaders in the editor. Two evenings later there was a near-complete port of both maps. The goal was never full playability — just a great-looking screenshot — so missing lifts and scripted events will stay missing. If you're interested in the game and these maps, they're definitely finishable, and stripped-down versions without normal maps for the original renderer would be easy to put together.

---

In last version i add support for MSAA 2/4/8, Anisotropic filtering, settings for draw distance normal maps, restore emitters rendering, improve normal maps stability and quality.

Testing dual normal maps for water shader

Results

Three months of work. No C++ knowledge going in. No game source code. The result is ENDrv — a working OpenGL 3.3 renderer for Unreal Tournament 2004 that runs on original unmodified maps and brings proper surface lighting to a 22-year-old engine.

What it actually does:

- True per-pixel Blinn-Phong specular and normal mapping via screen-space derivatives — no vertex format changes, no engine modification required.

- Dual Normal Map support for convincing animated water surfaces.

- Post-processing pipeline: Depth of Field, Bloom, and Chromatic Aberration — Bloom works on many original maps with zero asset changes.

- Playable framerates on original content, thanks to a multi-level OpenGL state caching system and a hybrid lighting model that layers dynamic per-pixel calculation on top of the original baked lightmaps.

Modern AI in the hands of someone with a working understanding of 3D graphics, basic engine architecture, and a large enough reserve of stubbornness can compensate for a complete lack of knowledge of any specific programming language.

---

tl;dr: Spent three months reverse-engineering UT2004's renderer interface with no C++ knowledge and no source code, using an AI coding assistant ($6/year). The result is a working OpenGL 3.3 renderer with normal maps, Blinn-Phong specular, and post-processing effects that runs on original unmodified maps.

u/SoMuchCo — 2 days ago

How to deal with ambient occlusion?

I have a problem implementing SSAO. I am trying to do it learnopengl.com way, and I am failing. At this point I am at loss.

There's just too many steps to achieve the result, each of which can fail and I don't know which one is failing.

I wanted to ask, what is the best way to ensure correctness of each step? How would you test SSAO creation steps?

u/Kverkagambo — 2 days ago

Render engine written entirely using DirectX 12, focusing on high details and real-time GI lumen like, for mid-lower PC/Laptop. (Demo running on Xe Graphics 11th GPU)

Implemented simple GLB parser from this Github repo: salvatorespoto/gLTFViewer: A glTF file viewer in Directx 12 .

Global Illumination using hybrid Surfel GI and SSGI.
Lens flare are modeled after Panavision styled lens flare (Anamorphic types with distinct hue ray light propagation inside lens it's flare), and grass are inspired from this IcterusGames/SimpleGrassTextured: Plugin to make grass on Godot 4.

My main point of the game is about GI and Nanite-like culling on low end devices, and yet i barely see ones, so i decided to implement it without using any modern techniques such as Primitive Shader or Mesh Shader. Pure Compute Shader dispatch, with many fused Shader optimizations instead of separate pass, simple workgroup tiling optimizations. It tooks me almost 3 months for this project just for the optimization workaround with the help of Codex (Claude sucks at this lol). I'm also planning to open source this if this thing is stable enough, modular, and scalable.

All of that combined to make this engine runs 40-60 FPS on my Xe 11th gen Graphics laptop, because why not.. (i don't have better GPU than this for now lol). This might be also potentially be a complete game engine after all.

u/Informal_Toe4672 — 3 days ago

[2607.22738] Nova3D: Code-Native Generation of Programmable 3D Assets

I co-authored this paper. It's a new technique to generate 3D graphics as source code instead of a point cloud.

Under the hood:
It generates 3D objects with separate, sophisticated internal assembly, producing an editable "kit of parts" (instead of monolithic blobs). E.g. imagine you generate a 3D washing machine via this approach. It's not merely going to be geometry that looks like a washing machine. We actually know that there is a Door, Drum, Control_panel etc. Which things belong to which assemblies. What moves. Where its pivot is. And eventually what those components are supposed to do.

Why current 3D GenAI cannot do this:
Most AI 3D generators generate "monolithic blobs" that look good, but are unusable in downstream workflows (e.g. game engines). If you generate a 3D bicycle, it's essentially a blob. If you want the wheels to turn, a human must spend time cutting the blob into parts, naming them, placing pivots and rigging joints. I.e. you need post-generation segmentation workflows of some sort (either manual work or more compute).

The paper breaks down the whole technique, and comes with a github repo too if you're interested in viewing it.

arxiv.org
u/mhb_11 — 3 days ago

Confusion regarding fundamental understanding of Ray tracing

Is there any podcast, academic talk and or conference recording that will help me understand raytracing, path tracing, rendering and graphics better? i wanna understand it because my boyfriend is into it and would love to actually know what he does and why he finds the field interesting

reddit.com
u/xxxDiptaxxx — 3 days ago
▲ 54 r/GraphicsProgramming+1 crossposts

Marching Tetrahedra - Volumetric Render Engine (OpenGL/C++) (Opensource)

We added Marching Tetrahedra Rendering effect to our Volumetric Render Engine.

Here, we Render our Volume data as a set of Polygon meshes by extracting 'iso surface'. It goes through whole dataset and tries to fit a polygon based on data values to calculate a polygonal mesh from the volume dataset.

Here's the Git Repo Link - https://github.com/mikejernil/volumetric-render-engine

We are building this over at 3D ENGINERD. & are planning to push our implement more features starting with Custom file loading, and support for more volumetric formats like DICOM, VDB etc.

u/Neither_Coffee_2308 — 3 days ago