Ludovico Antonicelli did an excellent overview of the way he approached the creation of the intricate rock master material for Echoes From Cryo project.
Hello, CG fellows and Hi 80 level! My name is Ludovico Antonicelli, I’m a 3d artist and I finished my studies with a group project called Echoes from Cryo, a game demo running in UE4. I will be happy to share some info about a variety of technical aspects I cared about during the development.
During the production I tried to produce reliable assets, that could be controlled by a robust material network. I worked a lot on the flexibility of every asset I produced, because I knew that spending time to make a easy manageable asset would have made the difference once in the tweaking phase. It has also allowed us to reuse the same assets in different places along the demo, both decreasing production time and increasing performance.
Gathering the knowledge took me a while between reading documentation and experimenting, but since it was a thesis project and not a commercial one, I figured out that this was the right moment to “go out of my comfort zone” and experiment with new techniques.
Rough Shader Setup
One of the challenge I’ve worked on for the outdoor environment is the master material used for the rocks we did using photogrammetry. Note that although in the article I will often spoke of photogrammetry, this material could work for any kind of rock asset.
Before get started with the actual material building I always like to study which key features the material should have, and if they could be optimized in some way.
According to our pitch, the rocks assets should be able to be:
- Highly customizable: because of the shortage of the number of assets I could produce;
- Detailed: because even with photogrammetry-produced rocks you will need a sort of detail-map (will explain this later);
- Tessellated: to profit from the baked heightmap and increasing realism;
- Wet: because of the presence of a waterfall and a river;
- Covered of snow: Because the bioma involves snow;
- Blendable with terrain: To avoid ugly seam of compenetration.
Then I roughly layout how the master material should be built.
As you can see I grouped the rock, wetness and snow material in 3 different functions, them are blended using a mask. For the wetness mask I just used vertex paint, the snow mask is a bit more complex so I will explain that later.
The rock function take as input 3 textures: the Albedo map, the Normal map, and a Mix map of grayscale AO, Roughness and Height.
N.B. /* When using assets gained with photogrammetry you should always remove all of the baked light from your Diffuse texture before using it in a game engine. You can find a little de-lighting guide I wrote here. */
Inside the rock function I created the network needed to control all the rock properties, like albedo color or roughness value. Using the input node I can call those in the master material to be parametrized.
I can’t explain every step of this, but there are some features that are worth talking about.
Albedo brightness: You would not need this adjustment parameter if having a perfect albedo, but when using photogrammetry, the brightness of the color texture depends directly on the light conditions when capturing your subject, so a bit of adjustments inside the engine is always needed. To work around this problem upstream, you can shoot your photos in a indoor studio, or using different techniques like HDR & greyball de-lighting, but this was not my case.
Detail normal : this is a tileable normal map used to create some little detail on the surface. Why is this needed? Because if I wanted to achieve this detail with only one normal map (being able to get it through the photogrammetry) it should be too big (like 4k or 8k) to get the same detail as if using a tiled detail normal blended with the baked one.
At GDC 2016 DICE did a very detailed breakdown of their photogrammetry pipeline, from whom in the past I learned a lot. If you follow this link from slides 75 to 91 they talk about texel density and performance issue. I don’t want to repeat what the masters have said, so if you are interested in how they used detail maps just read the whole thing.
To blend the baked normal with a detail normal you have to add them excluding the detail blue channel. I also used the Object Radius node to maintain the coordinates relative to the Rock radius. This way I can scale the rock without having to check the texel density again.
Normalized Heightmap: An 8 or 16 bit heightmap store grayscale values where 0 to 0.5 grays displace in inner/negative direction, and 0.5 to 1 in the outer/positive direction. If we want to increase the displacement values multiplying the height texture for a scalar parameter the mesh will inflate because all of the grayscale values will increase in the positive direction.
However, if we use a linear interpolate node to normalize the texture on -1 to +1 values we get a correct displacement effect. Remember to multiply your result to a VertexNormal node to displace the vertex along his normal
This may seem very obvious to some readers, but I haven’t found a proper guide about that on the web, so I decided to include it on this article.
Some of you will have noticed that, both for the detail normal and the displacement features, I used boolean switch to exclude them from my shaders. This is because I don’t want those properties to be present for every instance of my master material. For example, I can use a heavy instance for the nearest level of detail, and a lighter one for the farther ones.
Unreal has a vertex paint tool with which you can easily paint on meshes. I used that to paint the mask that define where the rock should be wet.
For the snow material I first searched some attempts by other UE4 artist to make a convincing snow surface in UE4. I found the landscape work of Jacob Norris to be the best example of it, so I created a simplified material function that shares the same PBR properties.
The snow blend mask is made up of 3 elements:
- Variation mask: a world-space texture is used to create snow variation, controlled by a single amount parameter;
- Up-facing mask: this is used to place snow on top of meshes. To mask the up-facing part l pick the blue channel of mesh world space coordinates using the VertexNormalWS node. This is controlled by a Parameter Collection, which I will talk later on. This mask is relative only to the triangle’s normal, but most of the time you will got a normal map to fake surface normal direction. To influence the up-facing mask with the high poly rock baked normal map, we got to transform the texture vectors from tangent to world space, then the resulting blue channel is added to the up-facing mask.
The Up-facing mask is also used to control the tessellation multiplier inside the snow function. This way I can control the tessellation for the snow and for the rock function, independently.
- Vertex paint mask: added in the end of the mask chain to add and subtract snow. To do that with the same vp channel you have to play a bit with math.
These masks are blended together to get the final snow mask. In the example below I used a checkerboard instead of vp to let you see the final result.
Right before the output node I put a Dither temporal anti-aliasing node to the Pixel depth offset pin. This will get rid of the compenetration seam of the rocks and the terrain. However I don’t recommend to use it for game purpose, as it can be pretty expensive for the performance.
The master material at this point should look like this.
In this project, we got pretty much all of the assets covered by snow. That means having a snow component in all of the 5 master material of the environment (rocks, landscapes, background mountains, trees and small foliage). Using a snow material function turned out to be the best option, but if me or the art director wanted to make small tweaks, to change the color or the snow amount, that would mean to do that for all the instances (too much time!).
So what I did is replacing some snow material parameters with Parameters Collection, you can create them from the content browser. In the Parameter Collection window you can add multiple scalar and vector parameters, and these can be placed in material editor or blueprints to perform changes in real time without the need to recompile the shader, exactly like standard parameters.
The thing that makes them peculiar, is the fact that they can be used to control multiple materials at once. I placed them in all of my master materials that have the snow element. This way we could manage a bunch of snow properties globally. (like color and displacement amount).
Usually Parameter Collections are used to perform level design changes at runtime, for example in a open world game them could control the height of the snow which increases with the passing of time.