The Mud of MudRunner

Let’s take a game screenshot and deconstruct it.

How about this one?

alt text

Let’s start by disabling post-processing: SSAO (ambient shadows), FXAA (softens object edges), DOF (blurs foreground), Sharpen Effect and Color Correction:

alt text

Now let’s take away all the layers in the reverse order they are applied when the game renders a frame.

1. Terrain Mud Decals (will get back to it later)

alt text

2. Wheel Tracks (which we will get back to later)

alt text

3. Mud Particles
Those objects float on top of the mud and are affected by vehicle wheels (can stick to them even), they only act as decoration and don’t affect the physics. Mud particles are simulated by the CPU in a separate thread. A lockless synchronization technique makes this whole effect very performance-friendly.
alt text

4. Mud Chunks
These are bigger chunks of mud, each is a full-fledged rigid body so they interact with the physics world properly and they even break into smaller chunks, when vehicles drive over.
They are maintained by the same systems that simulate and render all other plant types in the game. (A “plant” is an object that is scattered over the terrain in large quantities, like rocks or trees.)

alt text

5. Road Overlays with Parallax Effect.
Roads, that map author places within Level Editor, are then baked into a special data that game uses to draw them efficiently. They are not actually rendered as a separate layer and are embedded into terrain shaders.

alt text

Now we are left with the mud itself, which is a simple heightfield (essentially a 2d mesh):

alt text

Have a look at its wireframe.

alt text

Most of the game level terrain is actually rendered at lower resolution. So if vehicles have not deformed terrain, its wireframe would have looked like this:

alt text

Now let’s take a closer look at each layer…

MUD HEIGHTFIELD
Game levels in MudRunner can be up to 1km x 1km in size. They are subdivided into a grid of 16m x 16m blocks. Each block contains a list of plants (trees, rocks, etc.), base terrain data, mud data (optional), and other data. The game only draws blocks that are inside camera “frustum”. Only blocks that are near the truck that player is driving are “active”. The game only performs plant physics simulation, mud simulation and water simulation for “active” blocks.

The mud simulation consists of mud-vehicle physics processing (performed by the CPU) and rendering of the mud (performed by the GPU). For a number of reasons (the CPU and GPU are out of sync on PC, high level of detail required for rendering is not required for physics, etc..), these two tasks operate with completely different sets of data. We won’t go into details of physics simulation here, but in short, there is no rocket science involved, but it’s not something Havok (the physics engine in MudRunner) can do out of the box.

In order to draw the mud, the vertex shader takes in a simple 2D grid of vertices, then fetches two A8R8G8B8 textures that look like this:

alt text
Texture 1. 25x25 in size for each block, is used for rendering both low-res base terrain and the high-res mud.
R (red) channel – packed height (elevation). This is unpacked into real-world height with a little math.
G (green) channel – grass/dirt materials mix factor. Currently, each block can only mix 2 materials.
B (blue) channel – “tint” factor. Tint adds some color variation to the environment, and illustrates physics properties (“tinted” terrain is softer).
A (alpha) channel – marks portions of base terrain that are substituted by the mud, it’s the only channel that is dynamically updated as vehicles deform terrain.

alt text
Texture 2. 128x128 in size, is only allocated for blocks with mud.
R (red) channel – “mud height”, is relative to elevation of the base terrain. It gets unpacked into a floating point value with simple multiplication.
GB (green, blue) channels – used for “mud offset” effect (the mud slides away from vehicles wheels as they move).
A (alpha) channel – “mud tracks”, a blending factor of the terrain shader into the mud shader, In combination with A channel of Texture 1 that makes mud transition into terrain unnoticeable.

The visual part of mud simulation boils down to properly updating Texture 2:

alt text
With knowledge of wheels and chassis positions, their size and velocities, and their current mud penetration depth, the CPU forms various “primitives” and draws them into a “Texture 2” RT (render target), which is then read back by GPU. That is an approximation and has a very vague connection to real world physics!

The mud pixel shader itself is nothing fancy, and it simply blends few diffuse textures based on the normal which it derives from the heightfield data.

Now let’s get to more interesting stuff…

WHEEL TRACKS
Let’s take another game screenshot and see the steps involved in rendering wheel tracks:

alt text

Wheel tracks are rendered after terrain and most of the environment. So by then we already have screen normals and “wheel tracks screen mask” (which masks a portion of the screen, on top of which tracks shouldn’t be rendered), in addition to the Z buffer, of course. The mesh itself is a simple series of trapezes that follow the path of a wheel. But for the parallax effect, that gives tire treads a cool volumetric look, we first need to render wheel track “heights”:

alt text
Wheel track heights are required for a parallax effect. The textures are projected onto the terrain using Z-buffer. The tricky part is ordering tracks properly (more recent tracks should cover older tracks). There are several approaches to do this, we use GPU-only method: render tracks with their own z-buffer (so no hardware z-test against scene), and offset more recent tracks towards the camera in vertex shader. Z-test against the scene is performed manually inside pixel shader.

The wheel tracks mesh is then drawn again for a second, final, pass:

alt text

Traditional parallax effects take multiple samples from associated “heights” textures and then offsets texture coordinates accordingly (we won’t go into details of the math of it). Problem is, if you are z-projecting your texture onto z-buffer, there is no easy way to make said samples. That is why wheel tracks are rendered in 2 passes. The visual artifacts are neglectable with that approach, you just need to make sure you don’t sample from parts of the texture where you don’t have wheel track height info at (that’s what green mark-up of wheel track heights texture is for).

TERRAIN MUD DECALS

When vehicle’s chassis or wheels move through the mud at high speed, or when a wheel spins in a mud, it launches mud particles. Mud particles affect per-vertex mud data of most of the environmental assets, and they also generate terrain mud decals:

alt text

Each terrain mud decal has a mesh of oriented box:
0_1507645197081__decals.jpg

In the same fashion as wheel tracks, after the terrain and most of environment is rendered (it’s called “Color Pass” and it uses MRT – multiple render targets), we have screen normals (used to compute lighting) and terrain decals screen mask (masks out objects that decals shouldn’t be rendered on top of). Color Pass also write occlusion factor for terrain decals so they can be lit more naturally. Decals are z-projected on top of the scene.

Most of the games now feature z-projected decals, so it’s nothing really fancy. But the same system that generates Terrain Mud Decals in MudRunner actually also generates 3 other types of decals: Terrain Wet Decals, Terrain/Water Oil Decals and Terrain/Water Wet decals! But that’s a topic for another day…

10/10/2017
Pavel.

last edited by Pavel

I am nerding out soo hard here,

Thank you so much for posting, this is really interesting!

Interesting info bro - thanks 😉

This is fascinating - interesting to see the CPU/GPU workload splits (I bet it took a long time to work this lot out!).

Am I right in understanding from this that if I drove up and down the same stretch of road a thousand times I'd start having perf problems, or do you lose the oldest tracks after a while (or convert them somehow into the base mesh)?

Thanks for those interesting insights! Looking forward for the next chapter … : - )

@pugwonk said in The Mud of MudRunner:

Am I right in understanding from this that if I drove up and down the same stretch of road a thousand times I'd start having perf problems, or do you lose the oldest tracks after a while (or convert them somehow into the base mesh)?

Oldest tracks fade out and disappear 🙂

So, this is the magic behind the mud, very interesting. Thanks for sharing this with us!

Whats up with the mud chunks on the tires? everything looks great but that.

Yeah, i think the way the mud looks sticking to the tires looks terrible. I like everything else though. I am not sure why it has been changed to look like this for Mudrunner, Spintires original did not have this.

last edited by Shady

I am little disappointed. I thought that if wheels are really covered with mud then they have slightly less grip. Seemed logical but now I see it is only visual detail 😕

@nix_909 said in The Mud of MudRunner:

Whats up with the mud chunks on the tires? everything looks great but that.

@shady said in The Mud of MudRunner:

Yeah, i think the way the mud looks sticking to the tires looks terrible. I like everything else though. I am not sure why it has been changed to look like this for Mudrunner, Spintires original did not have this.

Spintires had that in earlier build. It was removed but now for some strange reason it is brought back.

@pavel said in The Mud of MudRunner:

@pugwonk said in The Mud of MudRunner:

Am I right in understanding from this that if I drove up and down the same stretch of road a thousand times I'd start having perf problems, or do you lose the oldest tracks after a while (or convert them somehow into the base mesh)?

Oldest tracks fade out and disappear 🙂

I wonder if it is time based or if I make new tracks while exceeding some memory limit for possible track length.

last edited by CoSm1c gAm3r

Thank you for explaining that Pavel. Even tho I have no idea what the heck you did here, it very nice of you to take the time and explain this for everyone.

Looking forward to MudRunner!! Thank you!!

@pavel Thnks for sharing!!!! This is very interesting. Looking forward to the next chapter.

nice, showing how it is all rendered is cool, but i ask. how much difference if any is there in the friction or difficultly with the mud in the new game? at first in the original game you would actually get stuck, but then after people complaining about getting stuck and the mud was changed and not as difficult. can hardly even get the zil (B-130) or uaz (A-469) stuck anymore, unless there happens to be a rock or stick in the mud that will stop you.

personally i liked the original mud before it was changed, so will it be the same as it was originally? or the newer easier mud? or will it be different than the 2 previous mud versions?

@cosm1c-gam3r said in The Mud of MudRunner:

I am little disappointed. I thought that if wheels are really covered with mud then they have slightly less grip. Seemed logical but now I see it is only visual detail 😕

They do have less grip. But only graphics were talked about in this post, not the physics.

@cosm1c-gam3r said in The Mud of MudRunner:

I wonder if it is time based or if I make new tracks while exceeding some memory limit for possible track length.

There is a track length limit.

@8up-local said in The Mud of MudRunner:

personally i liked the original mud before it was changed, so will it be the same as it was originally? or the newer easier mud? or will it be different than the 2 previous mud versions?

Physics of mud in MudRunner is different from Spintires, better and improved 🙂

Thank you for sharing this with us Pavel. I remember when you posted something similar a few years ago , but the game wasn't that popular.What you did is amazing , a one of a kind game and a great editor too. I'm sure that Mudrunner will be a even greater succes than its predecessor.

Really interesting Pavel, I would like to see an article about how the water works as well.

Thanks for the overview on the rendering process. I remember reading your Gamasutra paper back in the days, which also mentioned a lot of the background.

@Pavel, is it possible to put visibility distance and LOD distance in a .xml file for tuning? I have a GTX980, which is much better than the recommend hardware and would like to decrease the fog and then increase visibility distance and LOD distance.

@8uplocal, I also hope it is not too easy. I remember when I first played Spintires and did not get 20m. That sucked me in and got me hooked. A few weeks ago I checked it again and could drive through everything with no problems, which was boring.

last edited by KcutsTog