Vishal Ranga, a Senior Level Artist, shared his workflow, explaining how to set up a customizable rock shader in Maya, ZBrush, Substance, and Unreal.
In case you missed it
You may find these articles interesting
Hello everyone, I am Vishal Ranga, Senior Level Artist at Ubisoft Pune, working on Riders Republic, a recently announced MMO. I have been in the games industry for about 6 and a half years now and have worked on different roles connected to AAA levels, environment art, and tech.
During my first job, I got the opportunity to use Unreal Engine very early, even before it was public. Back then, the only way of learning Unreal was through heavily experimenting with all its features with the limited documentation that was available at the time. This phase of ‘learning by practice’ to make things look good, inspired me to explore the depths of environment art development.
Over the years, by working with the smallest indies to the biggest game studios, on different kinds of projects, facing many kinds of situations, pipelines, constraints, etc; I learned a great deal about level art development and production processes.
Idea and Purpose
I believe the first and most important step for any project of any scale is the "definition of purpose". I would not start something without having at least an idea of what I want to do and why I want to do it. I would like to explain the purpose in detail.
Rock Shader - Quick Pipeline: This started as an experiment. Rock assets are one of the most common and necessary items on almost all outdoor levels. They come in various shapes, sizes, forms, colors, physical properties, surface details, etc and they differ based on biomes. Having so many varying properties, rocks can become troublesome to produce and manage for a game project. I’m thinking about the number of assets, textures, memory, etc.
Making modular rocks can help when instanced and used in-game world, but if we try to push it further by scaling the asset, we will see that the textures are getting stretched and start to look pixelated. This is not pleasant to look at, so we end up making another bigger rock asset which takes time to make and adds to the number of textures and draw calls.
I was thinking, if we could address this scaling limitation, we could maybe skip making new assets for ‘size’ variations and focus on making new ‘type’ variations rather. Also, if we could make a shader that uses tiling textures and still could add in other details, it could save a lot of time and texture memory.
Before I jump in, I tend to have a clear idea of the approach, methods, and tricks I would want to use to achieve my desired result. I strongly believe in making highly practical and reusable assets. Making an artwork quickly and efficiently using the best methods is as important as making it look amazing. Because practically, this is how things happen in an actual game development situation. With all these requirements in mind, I plan to keep things as simple and easy as possible:
- Make a modular rock, designed to serve multiple situations
- Make a Base Rock Material – can swap color map with grayscale and sample from it. Give the option to switch to baked normal
- Make detail textures – one to overlay entirely and one to blend in as per object scale mask
- Make a unique mask as per 3D mesh to add in AO, edge, and other details like scratches or cracks
- Add a top layer feature in Unreal material
Making a Rock Asset in ZBrush and Maya
When it comes to sculpting rocks, I tend to follow the standard methods but keeping modularity in mind for almost every stroke. I think about the bigger picture of what kind of shapes I could potentially build using one single rock asset and plan my sides and details accordingly, while also maintaining a silhouette, decent enough to use the rock on its own.
As for actual sculpting, I started with a sphere, dynameshed it, scaled it non uniformly, and then used ClipCurve brush to make a rough shape. I used the Move Brush, Standard Brush, and ClayBuildup to give it a bit more shape. Then, I started using the Trim Smooth Border brush, which did most of the heavy lifting to get the macro shapes and to give it the rock feel. While at it, I also used other brushes, that are shown on the picture down below, where I saw fit. Once I got a decent silhouette from all angles, I started adding some mid-sized details. For this, I used some of my own Substance Designer rock heightmaps as alphas hooked into Displace brush. Finally, I carved out some cracks and re-touched those areas with a trim smooth border to give a breakage feel.
Sometimes I also used a bit of free brush set provided by Yunus, available on Flipped Normals' website.
If you want a super useful tutorial on how to sculpt rocks, check out Damien Peinoit’s tips and tricks. While you are there, you might want to grab some of his free brush packs, they could come in handy for adding quick details on your rock sculpts.
For making the low-poly version, I simply used Decimation Master setting a custom target polycount of 2k, so I will get around 4k polys in the engine. For the UV’s I used UV master to quickly get decent UVs as a start. Then I took the low-poly one to Maya and first ran a clean-up and then added smoothing groups. It is important to check the UVs and make any necessary changes to them because ZBrush's UV Master is not always reliable. Fortunately for my rock, all I did was run an unfold and it was good to go. In case I wanted to add any directional effects, e.g. flowing water, I would typically cut the UVs and rotate them as per my shader requirement.
Rock Mask and Base Material in Substance Painter
Now that the rock mesh is ready, I can bake the details in Substance Painter and make the channel-packed mask texture, which is needed for the shader in Unreal. To make this mask, I add 3 fill layers with the only color switched on and used Red, Green, and Blue colors respectively, representing the RGB channels in a texture map. Each of these fill layers is applied to the rock model based on independent masks.
Red Channel – Edge details derived from editing curvature map using mask editor and breaking it a little using a grunge noise.
Green Channel – AO details derived from editing baked AO map using dirt generator and toning it down a little.
Blue Channel – Scratch and cracks details are added by scattering and editing a crack texture using mask editor.
Note that the blend mode must be set to "add" linear dodge and an empty black layer should be at the bottom of the stack.
I wanted to reuse this painter workflow, so I made a smart material out of it. This way, next time all I need to do is import my 3D model, bake the model information, just drag my smart material into the layer stack and it is done. It is ready to export, but if tweaks are needed, it can be done very quickly.
Although I could have used the Substance Designer, my requirement was not to make a super highly detailed rock material. I just needed a base where I can get a decent color, normal, and roughness map; on top of which more colors and normal detail will be layered in. So, in Substance Painter, I mixed a few fill layers having different colors, roughness, and height values – based on heavily modified masks. This resulted in a decent rock base material.
Detailed Textures in Substance Designer
For making the detailed textures, Substance Designer was the best choice because it is very quick and efficient to manipulate and blend multiple noises in well-directed ways. Three very simple node-setups gave all the necessary 4 outputs. I simply mixed a couple of noises, some custom generated, some direct substance grunge, and ran a few "Slope Blurs" and "Non-Uniform Directional Warps" to get nice detail grayscales and normals.
Unreal Material Editor
Now that I have everything I need to work with, I jump into Unreal and import my low-poly rock asset along with all my texture maps. Always make sure to convert all 16bit images to 8bit before importing them into Unreal. I set up a quick showcase scene with a white backdrop, floor, skylight, sunlight, and post-process volume. I added a height fog and set the start distance quite far, just to kind of blend the floor with the backdrop, while not affecting the rock.
Let us look at the material setup, layer by layer. First is the base layer, where I just hook up the base textures and have given basic controls like tiling, tint, normal intensity, and a switch whether to use baked normal or the base material’s normal map.
Then I layered in the "Object Mask: details, using the three individual channels of the ‘Rock Mask’ texture. Three lerps are used to color each channel when plugging in a 3 vector in B input; and R, G, and B channels plugged in the first, second and third alpha inputs respectively. An optional feature to use one of the channels as normal information is provided as well. This can be switched on and off.
For the first detail pass, I am sampling a color based on Detail grayscale and blending it over the previous color output using a Lerp. The alpha of the lerp is the same grayscale, except its power is raised by 5 to give slight variation. The detail normal is blended directly on top of the previous normal output using BlendAngleCorrectedNormals. The tiling and intensities of color and normal can be controlled.
The second detail setup is based on the object’s scale. This is the part that was made to hopefully address the texture scaling issue. Using the Object Scale node, I took an average of X, Y, and Z scale of the object averaged the value and plugged it into the UVs. There is also a multiplier to control the tiling of this mask if needed. This setup will scale the mask whenever the object is scaled up or down.
The mask is used to sample an additional color variation and also used as the alpha for blending the new color and a new detail normal into the shader. This time the new detail normal is not overlayed all over, but only applied where the "scale mask" is. The normal intensity and the tiling can be controlled independently.
I added another little feature where the rock gets slightly tinted when instancing. Additionally, I gave an option to multiply the AO over the final color. Both features are totally optional, but it gives a subtle variation when instancing in world. This was more of an experiment for me.
I finally added a procedural "Top Layer" feature, wherein in this case, moss appears only on the top-facing side of the object. I used an old moss function that I already had and just piped it into the shader. There are already hundreds of tutorials for this, but in addition to the usual setup, I also exposed the height map from the moss function and multiplied it with the final mask. This trick gave me smooth transitions between rock and moss.
Material Instance Features:
In this video, I show all the processes involved from ZBrush to Unreal. I export the high and low poly from ZBrush, take it to Maya for clean-ups and adding smoothing groups, then in Substance Painter, I add my smart material and then bake the mesh details to get normal map and packed mask, then I take them to Unreal, make an instance, change the mask, and apply it to the object. You can see from my system clock; this entire process took only 5-6 minutes.
Here I show how the shader’s features are used to set up a rock in Unreal. I also show how I set up a very quick scene for my Artstation render.
In this video, I show the top layer features.
Some more examples with the same shader and same rocks instanced:
Further Improvements and Conclusion
While working on this, I had a few more ideas that popped up in my mind which I may try to work on in the future. Some of them are:
- Distance-based normal tiling – this can make the material more complex but can be an interesting feature.
- I don’t really need to use the first detail grayscale – it did improve the shader a little, but it was just too little for the perf cost it takes.
- If perf is really a concern, I can replace the base layer’s color map with greyscale and sample a color out of it. This will work out for certain types of rocks that are not too crazily detailed.
- Tessellation feature can be added for the top layer to make it fluffier. Can be made distance-based to save on perf.
- Currently, to get the snow effect, I am just making the moss white and playing with the top layer mask. This can be replaced by an actual snow function with nice features of its own.
To conclude, I must say this little project was fun to do and I did learn a few new things during the process. I know I have not explained everything in detail. If anyone has any questions, do not hesitate to reach out to me on Artstation. You can also check out my other projects there and maybe even discuss games or art with me, I would love that.
I want to genuinely thank 80 Level for reaching out and giving me this super cool opportunity to share my process with all the amazing artists around the world. Thanks a lot for reading. I sincerely hope some of this was helpful for you, if it was, I am super happy.