Making “huge” environment textures for Raw 3D graphics

In theory, “tileable textures” can be repeated over and over without any visible seams in them. Which does work well for very uniform textures like bathroom tiles, but once you get a bit of variation in color from just the roughness of a concrete wall and especially natural rock or grave, regular repeating patterns start to stand out very noticeably once you get beyond even just four or five repetitions. And on a castle or bunker wall, or the side of a grass covered hill or sand dune, it just looks really bad.

There are some fancy ways to break up the repeating patterns of tiling textures with shaders in Blender, but as it turns out those shaders aren’t easily imported over into Godot like meshes, UV maps, and textures. Of course, all the shader magic can be done inside Godot as well, but I really like to keep things as simple and straightforward as I can to help me keep some kind of grasp of what I’ve actually done and let me repeat it in a consistent way if I need it again in the future.

So instead of shaders that might break or cause some kinds of other problems I can’t even anticipate it, I decided to solve this repeating pattern directly in a single plain .png texture file with Gimp.

Step 1: Pick a Base Texture

50% dimensions of the original file.

Since I’m crunching down everything into jagged pixels anyway, I get all my texture files in 1024×1024. Anything bigger would be a waste of download time and storage space.

Step 2: Scale and Tile the Layer

I scale the layer down to 128×128 pixels with Interpolation set to none! to get the nice retro pixel look and then duplicate the layer to fill out the whole canvas, and then merge all the layers down into a single layer. This makes the repeating pattern very noticeable already, but the rest of the process is all about getting rid of that.

Step 3: Duplicate and Rotate the Layer

Duplicate the layer and rotate it by 45 degrees. This doesn’t cover the corners of the canvas, but those areas will be sufficiently covered up later anyway.

Step 4: Add a Layer Mask

Right click on the rotated layer and click “Add Layer Masks…” and select “Full White”. In the layers tab, there is now a white box next to the regular layer preview box. This is like a second layer that determines whether a pixel on the main layer is visible or transparent. Pixels in the white areas will be visible, pixels in black areas will be transparent.

Step 5: Add Noise to the Mask

With the layer mask selected in the layers tab, go “Filters > Render > Noise > Solid Noise”. (Or whichever noise of your choice.) For my 1024×1024 pixels texture, I like setting the scale to 16. Checking the “Tilable” box might help reducing visible seams on the edges of the texture, but with how pixelated it will be I’m not sure if it really matters.

Step 6: Adjust Levels

The default noise will have very few areas that are fully white or fully black, resulting in everything on the layer being at some level of semi-transparent.

With the Layer Mask still selected, go “Colors > Levels”. I never fully learned how everything in this dialog window works, but moving the black triangle and the white triangle sliders under the curve towards the center lets you turn dark grey areas fully black and light grey areas fully white. The values can also be typed into Low Input and High Input fields right below the sliders. I like setting black to 63 and white to 159.

This turns the layer into a pattern of colored splotches and empty spaces, with smooth gradients between them.

Overlaid over the original texture, it’s now still clearly the same texture, but the repeating patterns are gone. This texture will still repeat. But it will repeat every 1024 pixels instead of every 128 pixels and basically unnoticeable.

Step 7 (optional): Add another texture layer and repeat the process

For a forest floor, it looks nice to have some patches of grass or moss among the leaves and branches. So I added a grass texture on top of the other layers and did all the steps above again.Step 8: Set image to Indexed Colors

This isn’t directly about breaking up repeating patterns, but I have found that a very important final step to getting the classic 90s pixelation look is to change the image from RGB colors to indexed colors by going “Image > Mode > Indexed…”. For textures with relatively uniform colors like all wood furniture or concrete walls, I like going with 16 colors, but for more complex things like this forest floor this might not be enough and 32 colors work much better.

And here we see the result in action. There is still that triangle shape that’s in the original texture file, but now it’s no longer repeating in a regular pattern.

The downside of making ground and wall textures like this instead of using shaders is that the file size for the texture increased by 64 times! And probably would have been a problem for the PlayStation’s memory to handle 30 years ago. But when the default texture size is only 128×128 pixels to begin with, 64 times bigger is still only 1024×1024 pixels, which by today’s standards is still quite small. Even when you’re making a game to run on a 20 year old potato, using three or four of such mega textures really won’t make any difference.

It’s barely half of a megabyte. I think we can afford it.

First image from the surface of Kion

This is a quick mockup of two tree models I made with a hastily thrown together forest ground and some shrubs. It’s not much, but I could throw this into Godot and run around in it. And plenty of games do actually get released and even sold looking more basic than this.

If Jedi Knight had attempted to have a forest in the game 29 years ago, it probably wouldn’t even have looked as good as this.

Really looking forward of how Kion will look a year or two from now. And three and four.

I made a tree

This tree might not look like much by itself, but this marks one of the biggest milestones in my development as a game creator.

For the last three months I’ve mostly been busy working out non hobby stuff, but when I’ve been coming back to work on the game on and off, developing an art style and figuring out the techniques to produce plants for Iridium Moons has been the main focus of all my efforts.

And I am very happy that today everything has come together, exactly the way I wanted. I now have one first tree that I will happily have appear in the finished game.

Back in spring, I found that the way in which I approached making environment assets in Blender would basically let me make things in the visual style of early 2000s games like Morrowind and Knights of the Old Republic just as fast as the mid-90s style of Quake or Tomb Raider, and followed that direction for a while. But with some more time to reflect on it, it would absolutely be doable for me and looks very nice, but I just don’t find it nearly as inspiring or interesting to work on. The blockiness of low-polygon models and graininess of unfiltered textures of this art style has a very important impact on the overall aesthetics of the games that use it, and is an art form that uses abstraction and reduced fidelity as a key tool, like impressionist painting, black and white photography, or claymation. It’s a visual style that was created in response to very severe technical limitations. But the way that artists found ways to get something at least passably interesting and inspiring on screen under these limitations resulted in a new form of artistic expression that remains interesting and inspiring even after it’s no longer a technological necessity. And for that it has become an art movement in itself, that has seen a huge resurgence in the last 8 years. (For which I entirely credit David Szymanski’s Dusk.)

Iridium Moons is directly based on several movies from the 80s and 70s, which themselves had been references to even earlier works, many of which are depicting very anachronistic worlds. So even besides the inherent artistic qualities of the Raw 3D graphics of id Tech 2 and the PlayStation, going with a decades old graphics style from before the major revolution that created modern 3D graphics in the mid-2000s is just a very appropriate fit. An Iridium Moon game made in the Build Engine with character sprites instead of 3D models would also work. Or even RPG maker with an SNES aesthetic. But pixel art sprites have always confused and intimidated me, so the Raw 3D look with simple wireframe models is the way that feels most practical to me.

Going from my earlier tree models, that were coming along quite nicely, to something that would look fitting for a PS1 game turned out to be surprisingly more complicated than I thought. If you simply lower the fidelity by reducing the number of polygons and decreasing the number of pixels, you soon get to a point where the details of a realistic plant simply disappear. Just as with low-resolution 2D sprites, you have to approach the model and textures differently. Instead of creating a realistic tree at very low resolution, you need to construct something that evokes the idea of a tree in the observer’s brain. It doesn’t even have to look like an actual tree. It needs to have traits that the brain interprets as a tree. You need to stop trying to depict reality. And instead create an object that manifests the traits of the actual thing that matter to the brain.

We recognize bark by the patterns in the variation of colors caused by pigments and the way light interacts with uneven surfaces. We recognize branches by the way that they fork and get thinner towards the end. We recognize a mass of millions of leaves as being leaves by the patterns of leaves being lit by sunlight or being in the shadows of other leaves, as well as being able to see the background behind the tree peaking through in some spots. These patterns are what a very low fidelity model needs. Even when the scale is 10 or 20 times off for what would be found on a real tree of its size, the color patterns and shapes are still enough for the brain to tell itself that it is seeing a tree.

The tree model I made only has three branches. The leaves are only 12 flat planes with a mottled green pattern full of holes. Real trees look nothing like this. This is an anatomical abomination. But it works as a tree because it checks all the boxes that our brain is looking for, while at the same time being so far from reality that it doesn’t trigger the instinctive reaction that something is not quite right with it.

Cartoonists and pixel art makers probably understand all of this, as they are facing the same issue of not having the fidelity to depict important fine details. But this has been a very interesting learning experience for me. I started out spending days looking for the right textures for what I wanted and never coming up with anything. And tried out so many plugins and extensions to generate realistic plants that I could scale down. But it never looked good. And in the end, I did everything on this tree with placing individual vertices in Blender and using lots of copies of a heavily scaled image of a branch with 30 leaves. The end result comes out at 616 triangles and 43 kb of texture files. And it looks perfect.

The most interesting discovery in all of this is that this style of tree actually never existed. Certainly not in the 90s or 2000s, and I think not even at all until I made it. I thought I had seen trees in this style everywhere in dozens of games, but checking back on all the games I thought might have them, none of them do. They all do have two billboards of a tree photo stuck into each other so you can look at it from any side. The only place where I was able to find anything similar to this style, and where I probably got the idea from, is in Morrowind. Which has always been using much larger texture files and texture filtering and only came out in 2002.

These trees are a figment of my imagination. The first thing I encountered that seems to be the full on Mandela Effect. But for something that images a game from the 90s that never was, which is adapting a movie from the 80s that never was either, this seems like a perfect fit.

Low resolution Tree Branches asset pack released on Itch

As another big milestone in my career as a game developer, I have released my first free game asset pack on Itch.

New Font

I’ve been wanting to use this type of font for ages, and now I finally found a Creative Commons font that is a very close match.

Grand9K Pixel font by Jayvee Enaguas

I think it looks really quite good here for the site.

While I did start playing PC games when such fonts had already gone out of style, I still saw it in X-Wing, Worms, and Albion all the time, and it is in a lot of other games from that time as well. Not completely sure how good it will look with 3D graphics, but it’s my first choice for the UI of Iridium Moons.

New Gamedev Milestone Reached

I created my first texture with Material Maker yesterday, and when I went to try it out in Godot today, I encountered this glitch.

At six rows of blocks, every third row would have a double-width gap below it, which is caused by an extra wide line that’s clearly visible in the normal map. At first I thought this is an error in the normal map node, but then suspected it’s actually an issue with the black and white brick pattern at the very start of the node chain, which all the shadows are based on.

I first checked if the issue might go away if I increase texture size from 512×512 to 1024×1024, but that made no difference. I then tried to change the brick pattern from 5 rows to 6 and then to 10, which still had the same issue. But it did disappear complete at 4 rows, and on a quick check, also didn’t appear at 8 rows.

I believe this is because numbers like 512 and 1024 can not be evenly divided by 3, 5, 6, or 10, and so when the final texture files are being generated, some rows have to get an additional line of pixels to fill out the entire image. Unfortunately, Material Maker does this by adding an additional black row to the bottom of each row instead of a white line in the center, producing a very noticeable wider gap under that row instead of making the blocks impercivably taller. But powers of 2 like 512 and 1024 do divide evenly by other powers of 2 like 4 and 8, so no such error there.

So the error is caused by some decimal fractions being impossible to accurately convert into binary fractions.

Which means I ran into, diagnosed, and solved my first floating point error.