Scrap Mechanic
48 คะแนน
An Analysis of a Single Frame in Scrap Mechanic: MKII
โดย Syntaxxor
With the Survival update, there have been some updates to the rendering engine. So I figured I'd rewrite my rendering process analysis!
5
3
2
   
รางวัล
ชื่นชอบ
ชื่นชอบแล้ว
เลิกชื่นชอบ
Introduction
We play games all the time without considering just how long it takes to make each image we're looking at. Be it 30, 60, or even 144 frames per second rendered, there's an impressive amount of work our computers go through to render a single image. To demonstrate, we will be looking at this relatively simple image from Scrap Mechanic:
Assisting me in this is a handy little tool called RenderDoc[renderdoc.org], which captures all the different functions run that result in the rendering of the scene.
I have written on the subject previously, which you can find here:
https://steamcommunity.com/sharedfiles/filedetails/?id=1518302031
However, I was incorrect about a few things there and the rendering engine has seen a few improvements since that guide. So, as such, I have written this new guide. Let's begin!

NOTE: I expect the viewer to have at least a rudimentary knowledge of rendering terms, such as mesh and texture. If you don't know what those are, Google it.
Sun Depth Map
The very first thing the rendering engine does isn't anything rendered to the screen, but is still an essential part in making any game look good: Rendering a sun depth map. What do I mean by this? Well, put simply, it's a texture that shows how far away everything in the scene is from a specific point and angle. This depth map has 4 layers, each further away from the last. This makes it relatively easy to have high detail in the shadows close to the player, but also have lower detail in shadows far away. Note that all depth maps in this guide have been brightened to show detail.
Keep these images in mind, as they aren't actually used until much later. From what I understand, they are simply rendered at this point because the graphics card can have this rendering process going on in the background while it starts rendering the next step.
Wire Pre-Render
The next thing that is rendered is the wire tool icons. This is something I neglected to show in my previous guide.
The first thing that is rendered is the bearings:
Then, it renders all the other types of wiring points such as engines, lights, thrusters, switches, etc:
And finally, it draws the wires between these things:
In addition to these things, it renders to a stencil buffer. This is simply a texture that shows where something was rendered. This is important to ensure that nothing is drawn over the wires.
Depth Pre-Pass
The next step is very simple. All it draws is a "depth pre-pass," or rendering specific objects to the depth buffer. This is done for a few things in the scene so that the engine can tell if it can pass with just not rendering certain objects due to them being completely hidden by the few things rendered here, such as the terrain.
And with that, we can finally get to some more proper rendering.
Main G-Buffer Pass
Finally, we reach the bulk of the rendering. In the wire pre-render phase, I was able to show each individual draw call. However, if I were to do this here, I'd be showing hundreds of images. So, I'll have to settle with showing the most important parts.
The first thing drawn is the player. He looks a bit lonely in the emtpy void:
Next is the bushes, tree leaves, pebbles, and grass. This struck me as a bit odd because it's floating treetops. The trunks aren't rendered until later:
After that, all the blocks that are completely opaque are drawn. Notice how the areas indicated by the wire stencil buffer aren't visible:
Next, all the opaque parts are drawn. Our scene is starting to fill out more:
Then it finally renders the tree trunks, as well as other solid objects in the scene like the pipes and the water tank far in the back:
Then, the ground itself is finally drawn. No more standing in the empty void:
And finally, blocks that are masked (meaning they draw or don't, but you can't see through them and have them tint the scene) are drawn:
And with that, our main pass is complete. Now, you might be wondering, "Why does it look so flat and ugly?" Well, that's because this is still a preparation phase. In fact, there have been a few other textures being rendered to at the same time.
One of these is a normal texture. This is simply a texture that contains the direction each surface is pointing:
Next is an ASG texture. That technically stands for "Alpha, specular, and glow," but from what I could figure out, that's not quite what it's rendering here. I still don't entirely understand this texture, but it's still used in lighting for some reason or another:
And there's also a depth map:
Now that we have all this information, the engine can actually get to rendering the stuff we're familiar with!
Lights
Direct Lighting
Right off the bat, the rendering engine draws sunlight using the sun depth map from way earlier, as well as the information calculated in the G-Buffer pass:
It also produces an interesting texture which shows what surfaces are shadowed and what surfaces aren't. This comes in handy later. Trust me:
Next, it draws in all the lights which don't cast shadows:
And then the ones that do cast shadows:
There weren't lights like this in the previous version, and it's interesting how they do it. It uses a depth map like the sun, but it's a bit different in that it has one depth map for each direction it faces. It also doesn't render these every frame because that'd take a long time. Instead, it renders these when they're created or blocks in the scene are changed I show 3 directions of one of these because it's the only one that actually had understandable detail:
During this pass, it also renders more of this ever-so-important shadow buffer:
Ambient Occlusion
Next, it calculates ambient occlusion. First, it figures out a rough idea of what places should be darkened. This tends to be a bit noisy and ugly, though:
In order to smooth it out and make it look much better, it uses what's called a "seperable Gaussian Blur." This just means that first, it blurs the image horizontally, and then blurs vertically:
This is done because to blur, it has to sample the texture at specific points around the pixel. If it tried doing this all at once, it'd be sampling tons more pixels than doing it in two passes. So it's a lot faster.
After this, it applies the ambient occlusion as well as a few other effects like fog, sky, and the wires. You might notice that ambient occlusion doesn't darken any place that isn't in shadow. The engine does this because if it darkened where the sunlight is, it'd look very unrealistic. This is thanks to the shadow buffer which showed it where to apply the ambient occlusion and where not to:
Finally, it adds the skybox for some nicer looking sky that isn't just a clear color:

The scene is coming together quite nicely now! There are still a couple more things that need to be rendered in, though.
Transparency
You might have noticed, but there still aren't any transparent objects in the render. The engine waits until now to render these. The first thing it does is add the transparent objects to the depth buffer:
And then it simply draws the transparent objects to the screen:
Post-processing
While the scene is almost complete, there are still a few more things to take care of. First is anti-aliasing to make sure it doesn't look as pixelated. There are several implementations of anti-aliasing. Scrap Mechanic opts to use FXAA, which is one of the easiest to implement. All it has to do is a single pass to smooth out the image a bit:
Next is Depth of Field to blur distant objects and make the scene feel a bit bigger. This is done with a separable Gaussian blur again:
And the last thing it does is two things at once. The first step here is making a dark copy of our current scene texture to specifically mask out the especially bright surfaces:
Then, it adds god rays. This is done here instead of someplace else simply because it's convenient and both of these will be added to the main scene after a blur:
As I implied, now comes one last separable Gaussian blur:
Finally, this is added to our scene texture and the scene is complete!
Conclusion
And that's how it's done! I hope you found this guide useful. If you have any questions, criticisms, or requests, feel free to use the comments section. And just remember: Something that probably took you multiple minutes to read and me a couple hours to write is done by your GPU at least 60 times a second.
33 ความเห็น
Syntaxxor  [ผู้สร้าง] 10 ส.ค. 2024 @ 11: 57am 
@Opoodoop We all had our coping mechanisms back in 2020
Opoodoop 10 ส.ค. 2024 @ 11: 44am 
you're crazy man
Piano Man 9 ก.พ. 2022 @ 6: 47am 
same
Syntaxxor  [ผู้สร้าง] 31 ม.ค. 2022 @ 1: 37pm 
I program. A lot.
Piano Man 31 ม.ค. 2022 @ 12: 40pm 
wtf do you do in your free time this is just........ amazing
Yamnuska 23 ม.ค. 2022 @ 10: 53pm 
dang i never realized how much work is done just for one frame, that's amazing
Syntaxxor  [ผู้สร้าง] 25 ต.ค. 2021 @ 6: 12pm 
What, the analysis of everything your computer goes through to draw stuff, or the strange, surreal comments below?
Maple 25 ต.ค. 2021 @ 5: 53pm 
Why do I feel like I witnessed something ungodly
n1ck_dts 19 ต.ค. 2021 @ 9: 16am 
:longhaul: :hungry: :train1: :train2: :train3: :steamthumbsup: :espresso:
n1ck_dts 19 ต.ค. 2021 @ 9: 15am 
as in nasa space x elon musk?? lol