Splat maps
Each of those 4 layers had their own 8bit texture that associated where the texture was placed on the terrain.
So 4 channels(rgba) allowed 4 layers. However when scaling up to use more textures it would become memory hungry and would be quite a pain to blend all the channels of different textures to know whether a texture was there or not, and on top of that the ordering of the second splatmap would overwrite the first splatmap.
On top again of this, to paint or remove the texture from terrain would require iterating over 2 textures if the max was 8 terrain layers. However what if number of terrain layers was 128. This would require 32 different splat map textures with each channel filled. Quite a lot to bind to the GPU per frame at runtime. and of course would also use a shab load of memory.
What we have come up with is an approach similar to material ids where a colour is placed on a mesh and then the shader translates that to a material. However, this approach does not work well if you need to blend 2 materials together.
Enter the new system:
The new approach would be to have 2 textures for the entire chunk of the terrain(more to come later on this).
- Texture 1 would be a 4 channel unsigned int which is 32 bytes wide; in each pixel we can store up to 32 layer ids and we have 4 channels, so in fact that is 128 layer ids per pixel. However we cannot blend that many textures so setting the number of terrain layers per pixel will be 4. Why 4?
- Texture 2: This texture will be a standard RGBA texture holding an 8bit alpha value on each channel for each of the 4 terrain layers that were assigned for the previous texture. This allows us to use the full bit depth range for blending which means we wont have any stepping and the overlaps will look as smooth as they are now..... ( getting there )
Essentially this new technique will allow us to use 128 texture layers with 4 active layers on one pixel at a time. so you could blend your grass to your mud and rock and one other, but on the next pixel you could use 4 different textures. Remember that painting with a brush you cover many pixels on the splat maps and generally unless edge blending, only 1 texture would be used per pixel on the texture.
This is also hyper efficient on memory as scaling up to 128 textures with separate splat maps would not be as intensive on memory as separate channels per splat map as we use only 2 textures for all data.
For instance, with the old system, if scaled up to 128 textures; it would have 32 textures with 4 8bit channels. So for a 1024x1024 splat map assigned from the front of the terrain editor, would use: 128mb of memory but remember 32 textures ( limit of GL is 16 )
With the new system we have 2 textures, one 32bit wide per channel which is 16mb and another 4mb for the alpha data texture talked about before. Again only 2 textures for all 128 layers.
Scaling up to 2048 and 4096 will see 4x or 16x the amount of memory on both. On the old way of doing things at 4096 would use 2Gb of video memory to store all the 128 splats in 32 seperate textures of 4096 width and height, however on the new system only 320mb would be used... quite a big difference from unusable to very much usable even on older cards that dont have as much memory.
More details of the first texture: This is the layer ids storage and as mentioned before we could store 32 layer ids per pixel per channel using "bitwise inclusive or" operations. Bitwise for those that dont know is a useful ability to shift up and down the binary codes..e.g. "0000 0001" would represent an 8 bit value of the integer 1. We are using 32 bits so that looks like "0000 0000 0000 0001" for the same value of 1. But means we can store 32 options inside a single 32bit integer.
Each channel of the texture would represent a terrain layer id range, for example
- Channel 0 = 0 - 31
- Channel 1 = 32 - 65
- Channel 2 = 64 - 95
- Channel 3 = 95 - 127
However, if you only needed 32 textures for your terrain, we could reduce down to 8bit unsigned ints to make use of the 0-255 range instead of 0 - 4,294,967,295 which is 32 bit.
Bitwise works by bitshifting. so for example a 0-256 range would allow us to store 8 layer ids per channel giving us a total of 32 terrain layers.
- bitshift left: 1<< 0 = 1
- bitshift left: 1<< 1 = 2
- bitshift left: 1<< 2 = 4
- bitshift left: 1<< 3 = 8
- bitshift left: 1<< 4 = 16
- bitshift left: 1<< 5 = 32
- bitshift left: 1<< 6 = 64
- bitshift left: 1<< 7 = 128
- bitshift left: 1<< 8 = 256
Thats 2mb over the 4mb ( 1024(width) x 1024(height) x 1(byte) x 4(textures) ) used of the older system for 32 textures instead of 128 textures. Remember the math scales exponentially.
Ok, enough math as ive probably lost a few of you already lol
Its all down to the fact of how we can increase the number of layers without exponentially increasing memory usage over just a simple splat map and how we would combine that data in the shader to know what layer goes where without overriding the previous 4 layers.
Because with the new system you could use layer 2 and layer 30 and the texture data would have the correct information stored and be able to blend those 2 layers without having to load different textures to access their blend map data's.
It does mean that importing or exporting a terrain layer blend map would be a bit harder for us, but you guys would not see that side of things.. it would just work.
Paged terrain and large scalable worlds
Next is the plans on how to make that infinite world. This problem is much simpler.
Basically as you go around the world(could be millions of units), you would have a set amount of chunks around you at all times, this could be 16km of tiles but the magic comes down to how we handle the painting and editing of the terrain at that scale.
One issue we all know is that if we create a larger terrain, we lose resolution of both painting and editing.
The new terrain paging will use similar size tiles all over and start to use a new texture when the size of an area enters a new coordinate, could be based on each tile or could be based on a set number of tiles. These set number of tiles would share a splat map and the tiles next to those a different splat map and so on.
These would be loaded and unloaded on the fly.
Keeping tiles to a same dimension regardless of the overall terrain size would for this example, imagine that the highest resolution you could edit would be 1m polygons. You would be able to edit 1m polygons if it was a 16km terrain or a 1 km terrain as the terrain tiles or chunks would be loaded and unloaded rather than stretching them to based on an overall resolution currently set on the front terrain panel. You would now set the resolution of a chunk and a world size that you wish to use.. this could become a editor where you make a grid that represents your world.
Now generating terrain data on the fly is intensive, so when a tile is generated it would cache the data for that coordinate to disk so when the terrain chunk is unloaded, it would reload from the disk cache rather than generating again. When editing we often edit up to 4 chunks or more depending on brush size when editing edge seams or larger brush sizes which might encompass more than 4 tiles at once(typically why is gets slow to raise a large portion of the terrain).
This wouldnt be too different, until it comes to painting. Since we might have more than 1 splat map now to keep good resolution even when editing a large world, we would now be required to edit the data of the splatmaps based on brush size and location. Only having to update a minimal amount of textures to paint will be much easier than seperate splats.
For Instance if the splat maps were like the old one and you edited over 4 splat maps(corner of each for example), that would be 128 x 4 texture edits... yikes, hope you have all year for one mouse click. Now we would only edit 2 x 4 = 8 textures if you edited at the seams of a paged terrain splat maps. Still quite intensive though.....
So what if we utilised the GPU with its mighty power and did the work on a compute shader. 8 textures would probably be doable in 2 compute shader calls.. Texture 1(layer IDs) x4 and texture 2(alpha blends) x 4. Now that would probably start happening in real time if we can keep the textures sizes to a minimum.
Remember that because the terrain would be fully paged and we would have multiple maps to keep resolution of the area, the texture sizes wouldnt need to be as big as they used to.. a 256 or even 512 could be the highest resolution needed to paint at a 25cm resolution.
Conclusion
So yeah, thats what ive been up to today, working out how to make more use of the terrain and the painting to get more resolution yet have a larger world, like seriously larger, perhaps infinite if you have the disk space haha....
These sames techniques can also be applied to vegetation like spraying grass, bushes or tress and keeping resolution rather than having like a 10m block of editing control at 16km lol
Hope you find it interesting, if you can understand it that it
How many textures do you guys think the terrain needs maximum ?
Edit:
I may have gotten something wrong as its all been purely theoretical today and is subject to change as looking and reading back it might be wrong that to fit bit 32 and a lower bit would be larger than a uint32, so bitwise range 0-16 would be used. But that is a problem for when i come to implement the whole thing... it would still be around 120-124 or 28 textures on an 8bit integer though