Skip to main content

Project · 2025

Way2Terrain

An low-level exploration of shader and graphics programming.

Way2Terrain

Project info

Role
Graphics Programmer
Team
Solo
Duration
3 weeks
Engine
Godot
Languages
GDScript, GDShader, GLSL

Overview

Acerola’s Dirt Jam is a community event encouraging participants to explore graphics and shader programming. For this project, I started with the provided reference code and a list of suggested ideas to experiment with.

Creative Direction

The visual direction started with Kankan’s Way2Geeked album visualizer, particularly in combination with the song Livefastdieslay ##4Kaine. I was drawn to its noisy, high-contrast imagery, shifting between washed-out grays and saturated pinks. I initially considered recreating some of these visuals through shader effects, but eventually decided to take the elements I liked and reinterpret them in a different context.

Other visualizers and artwork I was drawn to at the time, including SoFaygo’s Pink Heartz and LUCKI’s s*x m*ney dr*gs, helped shape the direction further. I collected these references into a moodboard, using their colors, textures and abstract imagery as a starting point for the environment.

Album visualizer of Way2Geeked.
My 'Lean Aesthetic' moodboard.

Getting Familiar

Before jumping into new features, I first wanted to understand what I was working with. The jam provided an annotated terrain generator in Godot, so I went through the code and experimented with the available inspector parameters.

I played around with the mesh settings, noise generation, octave count and material properties to get a better understanding of how they influenced the generated terrain. This gave me a starting point for deciding what I wanted to build on top of the existing implementation.

The jam also provided a list of suggested features for participants looking for ideas. After considering which ones were most relevant to my plans, I settled on four to start with:

  • Distance Fog - introducing atmospheric depth.
  • Shader Include Files - reducing duplicated code between shader stages.
  • Level of Detail - adjusting terrain detail based on camera distance.
  • Improved Lighting - extending the existing Lambertian model with specular highlights.
Playing with the octave parameters Playing with the noise parameters

Building Atmosphere

With the initial features implemented, I started bringing them together in an early scene mock-up. I experimented with different post-processing combinations, comparing screen-space and additive bloom before settling on the latter.

Distance fog became a more involved exploration. Linear fog was relatively straightforward to implement, which gave me the confidence to push it further with exponential fog. That proved considerably more challenging and led me to investigate the underlying attenuation theory, eventually discovering Beer's Law through discussions in the jam community.

After the change Before the change

I also extended the terrain’s existing Lambertian diffuse lighting with Blinn–Phong specular highlights, giving the surfaces a more pronounced response to light.

Lambertian Diffusion: The old lighting model
Blinn-Phong: The new lighting model

Outside the implemented features, I researched other fog approaches, including animated and layered fog, through OGLDEV’s Mastering Fog Rendering in OpenGL.

Atmospheric variations explored during development.

Texture Mapping

Moving beyond the terrain’s initial flat coloring, I wanted to introduce actual surface textures. This turned out to be the most challenging part of the project. Getting triplanar texturing to work without unwanted stretching took several days of debugging.

I was struggling to get the textures and their associated resources to work correctly, so I reached out to other jam participants. Refoulay shared their implementation, which gave me a useful reference for troubleshooting my own. After working through the initialization and texture-binding issues, I eventually got the terrain rendering correctly. I then extended the implementation to support different texture resolutions, testing everything from 128px to 4K in the running scene.

Rocky texture
Alien texture
Lean lake 😷🥼
Icy texture

Final Composition

After the texturing work, I wanted to spend less time on low-level implementation and more on shaping the environment. For the water, I opted for a simpler GDShader implementation using vertex displacement and Fresnel reflection, which I integrated with Sky3D to follow the changing sky.

Post Mortem

This project was my crash course into graphics programming. Along the way, I got familiar with concepts such as:

  • GPU Data Alignment
  • Procedural Terrain Generation
  • Terrain Rendering
  • Compositor Effects (Godot)
  • Vulkan and SPIR-V

My favorite takeaway was figuring out GPU memory alignment - and coming up with my own analogy to really understand it:

Think of UBO memory like an inventory grid. Each data type has its own slot size, and a row can only hold 4 slots. Bigger items like vec3 and vec4 need VIP spots at the start of the row, and smaller items can fill in the gaps.

If you change the order on the CPU side, you have to rearrange the layout to match what the GPU expects.

How it felt writing that analogy

The project was AI-assisted throughout, with ChatGPT generating much of the implementation. I steered the exploration by choosing what to build, selecting features, integrating the results, testing them and working through issues as they appeared.

I wasn’t writing every line of code myself, but I was actively shaping the systems and learning how they worked along the way. It became a crash course in graphics programming, and one I genuinely enjoyed

At the time, AI gave me the opportunity to explore technical territory I couldn’t yet implement independently. Since then, I’ve built a stronger foundation and feel much better equipped to tackle more of that work myself.

I know I don’t have to do everything the hard way to learn something from it. 💯🗣️