An Enchanted Forest
I return with progress. I have both trees and people in the game.
Here's the sorceress exploring the forest:
The trees are static meshes, exported from Blender using the mesh exporter I described in my last update.
Textures in Your Pocket
I'm not the most confident modeller. As I've developed my skills, my process has been to start with a painting or a texture, and cut out silhouettes. Then I stitch these flat pieces together like I'm sewing a jacket.
With the tree trunk, I approached it a bit more traditionally. I started with a simple shape and extruded to create edge loops. However, modelling with a grey box doesn't work for me. I need a texture to give me a feel for the colour and shape I'm aiming towards.
Cue searching online for leaf and bark textures. Difficult to find one that's excactly what I need.
Except, I realised, we all walk around with a texture-capturing device in our pockets!
Out the window, in the real world, there are a bunch of leaf and bark textures just waiting to be photographed:
It's crazy how easy it is to forget that not everything is online. With a camera, it's easy to get something unique, and no need to license textures from third parties.
Traditionally, foliage in games hasn't been modelled to the level of the individual leaf. Instead, the alpha channel is used as a mask, to make the spaces between the leaves semitransparent.
Modern graphics APIs don't expose alpha testing machinery. Instead they use
discard in the fragment/pixel shader. Implementing an alpha test is a
simple check of the alpha value, followed by a
Using alpha testing can be suboptimal, as it may prohibit the GPU from performing early-Z optimizations. But the only 'optimal' model is one with no vertices, and alpha testing is a useful tool.
Right Handed Coordinates
If you remember, the clip space of my engine is left-handed, and for good reasons:
- Origin at top left matches how images are typically stored.
- Y ascending downwards matches most windowing systems and UI toolkits.
- Z increasing towards the viewer is required for reversed-Z.
Blender's coordinate system has the opposite handedness. It's right-handed, with an XY ground plane and Z upwards. This choice is arbitrary - the contrarian's law is in full force:
- Unity is left-handed with Y upwards.
- Unreal is left-handed with Z upwards.
- Maya is right-handed with Y upwards.
- Bullet is right-handed with Y upwards.
Converting between coordinate systems can be tricky. Transforming a model to a coordinate system with the opposite handedness means you have to flip normals and reverse polygon winding.
I've avoided this problem for now by switching my engine to use a right-handed world space that matches Blender. The camera matrix handles transformation of this right-handed space to the left-handed clip space.
Instancing, or GPUs are Insanely Powerful
Tree rendering was mostly straightforward, but I did add one technical improvement to my engine.
Instancing essentially allows you to draw a lot of copies of the same mesh using a single draw call. You have the vertices of your model, and then you have another set of 'vertices' which are actually the parameters for each instance. For example, the instance's transform matrix.
Adding this to my OpenGL backend was a case of marking a vertex buffer as
instanced and calling
There's no culling yet, so the engine throws a thousand trees at the GPU in a huge batch. Each is only a little over 100 triangles, but that's still a lot of geometry. But we're living in the future. Even my ten year old laptop's anaemic GPU barely breaks a sweat.
It's crazy how powerful GPUs are, and how lazy they let you be!
I think it's fair to say that drawing people has been one of the biggest mental blocks in my entire game development journey. Working out an art style and workflow that I'm happy with has taken a very, very long time.
This latest attempt is looking good, but I'm still not entirely sure...
Art Style at Scale
Game characters don't just have to look good. They have to look good at the correct pixel size and remain distinct when viewed in context.
I've tried a lot of different styles:
- Retro 16x16 sprites, scaled up with a pixel art scaling algorithm.
- 3D with various proportions, from fairly realistic to super-deformed.
- Painterly 2D with skeletal animation.
- My own pixel art at slightly higher resolution.
The more realistic an art style is, the muddier and more indistinct it looks when rendered at small pixel sizes.
The tragedy of art that reads at small sizes is that it absolutely needs to be so styilized and simplified that you can't add all the details you might want. You have to imply a costume. Our sorceress doesn't even have boots!
I find pixel art extremely difficult, and pixel art can have the opposite problem - it is difficult to scale up.
This latest attempt has the following features:
- Simplified shapes, so it's clear at small sizes.
- Cartoonish proportions, again, to help it read at small sizes.
- Drawn using vectors, so it's scalable.
- Bold, dark outlines, so the outlines don't blur away.
I was inspired by Paper Mario. It's come out better than I thought it might.
Maybe the key to getting past a mental block like this is to pick something and run with it. It'll inevitably evolve along with the rest of the game.
The art style I've gone with is a 2D one, on top of the 3D background, making this a 2.5D game.
It's an illusion that will break down at certain camera angles. All creative projects are about casting one illusion or another on the audience!
Because the sprite is always head-on to the camera, the entire sprite is drawn from a single mip level. If you look closely as you zoom in and out, you can see the threshold at which the GPU switches to the next mip level.
There's a simple way to slightly improve the apparent sharpness of the sprite, and that's by sampling with a negative mip bias. This makes the GPU use the larger, more detailed mip level for longer. The bias can't be too high, though, otherwise you end up with artefacts.
There's loads that can be improved here - the style is not consistent between the sorceress and the trees, and she only has two animation frames. But hopefully it's enough to start exploring the important part - the gameplay.
Next I need more than one character, and ways to give those characters commands. And if a bunch of people are going to be walking around, that means pathfinding.