Project Treadmill Godot (LIDAR 4)

February 22, 2024

The best way to learn a new engine is to jump right into the deep end, right? No? Well, too late. I decided to familiarize myself with Godot by re-creating my LIDAR effect in the new engine.

Part 1: Re-invent the laser

I picked up a few tricks over the course of my original LIDAR project, so I figured if I’m starting from scratch maybe I could try to find a different way of doing things. My first thought was that instead of capturing and re-projecting the world geometry, I could simply use a shader to render the effect I want directly on the geometry.

While this approach was promising, especially in terms of performance, it had the disadvantage that objects didn’t inherently occlude each other from the LIDAR caster’s view. I found a workable solution using Godot’s regular lighting system to create the occlusion effect by hijacking the built-in shadow maps

There were two problems with this approach, one major and one minor. The minor problem was that the method I used for creating the stripes (essentially the inclination angle from the caster to the pixel mod 2, then floored) didn’t actually recreate the effect I was going for. The stripes got wider further from the caster and at steep angles.

This may have been a solvable problem but the bigger issue was that this method eliminated the effect of narrowing the angle of the LIDAR sweep to get more detail and range, which was one of the more interesting and unique features of the system as it existed in Unity. This is also likely a solvable problem, but at this point I decided that the original ring-of-cameras method might be less performant, but it was also more robust and easier to tweak and improve. So I dropped this new method and set about re-creating the method I’d used in Unity.

Part 2: Wow, debugging shaders looks rad

At first porting the system to Godot is fairly straightforward. Instead of Render Textures they’re Subviewports but the broad strokes are the same. The first roadblock comes when it’s time to project the points back into the world. Unity made this easy, there was a built-in method to generate a dynamic mesh on the GPU, and several unique shader tools for converting between the various coordinate spaces involved in rendering.

After several days of trial and error, and reading Godot’s documentation, I got the effect working, though not as efficiently as it did in Unity, and with one major drawback…

Part 3: Anamorphs

One feature of Unity that the original version of this effect relies on is that it can do anamorphic projection, using a camera with one aspect ratio to render an image with a different aspect ratio, such that the ratio of pixels per degree of FOV is different for the horizontal axis than it is for the vertical axis. This is important for the LIDAR effect because having a higher points-per-degree density on the horizontal axis helps create that signature striped LIDAR look, and being able to adjust that ratio in real time is crucial for the sweep-narrowing mechanic to work.

Godot has no such feature. In fact, the current release builds offer no tools for altering the projection matrix of its cameras. Thankfully, it does have a tool for creating a custom projection matrix, which I generate once at startup and then every time the sweep angle is broadened or narrowed. The matrix gets passed to the shader and used in the vertex shader instead of the camera’s actual projection matrix. To my surprise, this method worked pretty much right away, no major debugging or tweaking required.

I also managed to improve the performance nearly to where it was in the unity version by removing a bottleneck in how the data was passed from the initial camera renders to the shader that draws the points. Plus, this version doesn’t have the same segmenting issue the Unity one does, where the borders between neighboring cameras are plainly visible in the final point render.