There was some positive response to my previous article, that makes me want to release the updates as soon as possible but I also want to provide substantial updates not single small things.

Hopefully this update will provide enough interesting content for people to come back again.

Small correction

In the previous article I mentioned that the 3rd render target of the GBuffer contains probably metalness but it looks like it actually contains specular color. Originally I didn’t see any colors and didn’t really understand why all 3 of the RGB channels contained the same data but that was probably because there were no colored speculars. This weapon shows a lot more different colors in this buffer.

Also forgot to add my favorite texture I found while looking through the rendering of the game. This one definitely deserves to be mentioned as it shows the sometimes chaotic nature of game development where there’s always some loose ends 🙂 ❤

Improve me!

Transparent composition and antialiasing

While trying to check how the half sized transparent buffer is upscaled and how the game does antialiasing I noticed something curious happening. I needed a scene that had way more contrast to clearly see what’s going on and luckily I managed to catch a frame where the player’s gun was moving a bit between the frames.

Before transparent rendering

Before the transparent buffer is composited back, the buffer seems to contain already a fully rendered frame, since this frame doesn’t have any harsh edges it is reasonable to conclude that this is the previous frame data.

After compositing current frame transparents

When the current frame transparents are added, we can notice some broken edges. The reason for this is that the gun moved a little bit to the right. Some clouds are rendered transparent but they are cut off at the horizon (which is opaque) so the composition doesn’t change the lower part but it already renders over the gun mesh from the previous frame, using the current frame’s depth buffer.

After adding current frame opaque

A few drawcalls later the opaque meshes also get composited. There doesn’t seem to be any specific reason for ordering them this way. It would make sense to composite the transparent buffer onto the current frame opaques but this is not how it’s done and I would be curious to know why.

After TAA

After the full frame is ready, a TAA pass smooths out the edges. I was wondering about this earlier because I didn’t see any antialiasing happening but the reason I missed it is that right after this drawcall the downsampling for the bloom pass is started and I missed this one drawcall.

Lens flare

Usually I don’t try to dissect single effects but there are many ways one can do lens flare so I was curious which one was chosen here.

Lens flare in the final composite

The lens flare is pretty subtle in most cases but it’s a nice effect. It’s quite difficult to show it on a screenshot so I’m not gonna spend too much effort on this.

Lens flare in the bloom buffer

After some searching I found the drawcall that adds this effect, and it turns out it’s a drawcall after the final upsample stage of the bloom. The effect is much more apparent in this buffer.

Lens flare geometry

Looking at the geometry, the lens flare is relatively simple. There are at least 6 quads involved that make up the final result on the screen but there’s no series of smaller quads stepping closer and closer to the sun position. We can conclude that this is a pretty standard solution although some developers choose to render the lens flare directly on the scene rendertarget while others calculate the effect as a postprocess.

Terrain rendering

In all open world games one of the most interesting challenges is rendering the terrain. I thought it might be interesting to look at this aspect here as well but to be honest I was a little bit disappointed.

First looking at a terrain patch it looked like there might be some tessellation going on. The way the terrain was deforming while moving also made it sound plausible that there is some extra displacement. Also the game uses tessellation quite extensively on PC so it only makes sense to use it on the terrain too.

Maybe my settings are wrong but I found that the game renders all terrain patches without tessellation. It uses this 32*32 regular grid on every terrain patch. There’s no LOD whatsoever.

Looking at the terrain patch post vertex shader, it’s visible that most vertex pairs were collapsed to form an almost perfect 16*16 grid except in some places where a some value (probably the curvature of the terrain) warrants more precision.

The deformation I mentioned before is probably thanks to reading mipmaps of the terrain heightmap when the terrain is further from the camera.

Raytracing shenanigans

And now the part that everyone was waiting for 🙂

Data streaming

One of the most interesting parts about any DXR implementation currently is how they deal with data. Most importantly how much data is loaded into the acceleration structures and how is it being updated. To check this I did two captures and compared the acceleration structures in NSight.

Player inside ship

In the First capture I was standing inside the broken ship you can see around the middle of this image. Only the closest objects were loaded except for the big cliffs at the edge of the map.

Player moved to the top-left of this view

On this second capture I moved further away from the edge of the map, towards the top-left of this image. The ship and everything around it are still loaded but some new objects got loaded as well. What’s interesting is that I can’t really make out any tile structure. Objects might be loaded/removed from the acceleration structure based on distance and visibility (bounding box size maybe?). Also the top right seems to be more detailed even though I moved further away from it. It would be interesting to hear more about this.

Terrain and under

There’s a few things to be said in the DXR implementation of Metro: Exodus that are related to the terrain.

First of all a curious aspect is that the acceleration structures don’t contain any terrain mesh (except for some special cases). These monsters are actually running on the terrain in the game, but looking at the data in NSight it looks like they are flying. This raises an interesting question if the GI implementation is capable of taking the terrain into consideration somehow (maybe using the heightmap and the terrain material) or not.

The following I would have never noticed if the terrain wouldn’t be missing. At the start of the level just looking around the acceleration structure in NSight I noticed some meshes under the terrain.

It’s quite common for artists to place debug meshes on the level for different reasons but usually these are removed before shipping the game. In this case not only these meshes made it into the final product but they are also part of the acceleration structure.

Other than the ones mentioned above I found a few more meshes spread out under the terrain. Most of them are not worth a special mention, but this one is also very curious, it’s a character standing almost directly under the starting point of the level. It has it’s own box of shame too 🙂

Finally one curiosity is that there are meshes in the acceleration structure that look one sided facing outwards of the level. Unless they are treated as double sided there’s very little chance for these to contribute anything to the game’s visuals. Even if they are double sided, they are so far away from the playable area that they probably just extend the acceleration structure. It’s interesting to see that these are not filtered out.

On this image also visible one of the special cases of a “terrain mesh” in the bottom right corner between the train and the building.

Skinned headlessness

I’ve talked about issues with skinned meshes before but there’s something else I noticed on this level.

First of all this monster demonstrates both errors I noticed before in one image. Still very curious what could cause these issues.

The other thing I noticed is that these small bat-like creatures have no heads in the acceleration structure.

Another example, notice the hole where the head should be. I haven’t found a single case where the head would be present.

The same type of creature in the rasterization view. Notice that the head is clearly visible.

And the wireframe of the head.

Final words

This is it for today, I hope you enjoyed this second look under the hood of Metro:Exodus.

I’m gonna continue looking into the rendering of the game but I won’t make another article unless there’s some specific parts people are interested in or I find something more I consider worthy of sharing.

Let me know if there’s anything you would like to hear more details about!