This is a rough breakdown of this project.
I started this modular environment set during a course, held by Kim Avaa (https://www.artstation.com/aava). I have since then scrapped it and started over again - I felt like I had made too many mistakes the first time. The biggest challenge was probably to plan the models so that they would snap together correctly and not break any tiling.
My personal goal with this project was simply to explore and learn a bit about environment art and modularity. The goal with the modular set was to make an easy-to-use asset pack that was generic enough to allow for any kind of level layout and not restrict a level designer.
From what I’ve gathered, my approach is a bit unorthodox. So I will break it down and hopefully teaching someone something new, while also opening up for feedback from others about my workflow.
Placeholder meshes and planning
I spent a lot of time on figuring out the dimensions of the basic meshes. At first, I made the walls 200x200 cm. But I noticed that it felt really off, from both an aesthetic and a modular standpoint. 200x200 wasn’t high or wide enough to fit playable characters; door openings became too narrow, the ceiling needed extra margins to not be too low, etc.
When I restarted the project, I went for a 300x300 cm and 25 cm thick wall instead - I found this to be a much better standard. Having established a standard also made it easier to improvise new pieces later on.
From the basic square ratio 300x300 wall piece, I proceeded to make other modular pieces that fit its’ measurements: 300x300 floor, stairs that ascended 300cm, trims that could cover the 25 cm and 50 cm seams between ceiling and floor, vaults with an inner radius of 150cm and 300cm, etc.
I then continued by creating some tiling materials in Substance Designer. I spent a lot of time on these, since they were going to be the base of the entire project and the style-defining content.
I aimed for exaggerated but realistic shapes; rounding the corners a bit extra, increasing the crack intensity, deeper slopes, higher values on the warp-nodes, etc. The goal-post was somewhere between a hand painted look and a semi-realistic style.
My general workflow for creating materials in Substance Designer is that I start with the macro shapes in the height map, then I add the micro shapes. I then use the completed height map as a base for both the base color and the roughness.
During this process, I always have the height map connected to a ‘normal sobel’ with a high intensity and a ‘hbao’ node - A sharp normal and AO helps visualise the height information much better and avoid any artifacts. This way I also have full control of the final AO through the entire process.
I usually start brick patterns with several tile-random nodes. Both share parameter values, only that one outputs a random grayscale square pattern and the other a randomly rotated gradient pattern. I run an edge detect and then warp all three patterns. These are good base patterns that I will reuse several times later in my graph.
After that, I used the flood-fill node and generate some more random gradients, which I then multiply over the warped edge detect. I then warp the result some more with a perlin noise. And that’s how I made the basic shape of the rocks.
Underneath is the height map for the sand: I “Slope Blur” a perlin noise on itself. Multiply it with a blurred cells noise. Invert it and then warp it (with another perlin noise as warp-input). I then run it through a highpass (to flatten the values a bit). Lastly, I multiply another warped cells noise (same perlin noise warp-input).
One more thing that I think is worth mentioning is the color. I grab different points of my height map, run them through dynamic gradient nodes. I then blend all of these different colors over each other, to create a non-uniform and layered base color.
I also made some small variations of all materials, for vertex-painting to add some diversity to the scene.
I used the height-maps that I made in Substance Designer in Maya to generate hi-poly models.
I’ll do a quick step-by-step on how to do this: I started with a high resolution plane.
1) Go into Animation mode. 2) Expand 'Deform'. 3) Browse down to 'Texture'. 4) Make sure it's set to Normal. 5) Click 'Apply'. 6) Make sure the a 'textureDeformerHandle' object is in the outline. 7) Click the checker in the Attribute Editor. 8) Choose File
The Attribute Editor should allow you to browse to your image by clicking the 'image name' folder. Then select your mesh (the plane in this case) and brows to your 'textureDeformer' in the Attribute Editor, where you can increase the strength of the map.
With the hi-poly complete, I started quad-drawing on top of it: I found that the most optimized and best looking approach was to do it by hand. Other ways would be to reduce the hi-poly with automated algorithms or simply push a (low-resolution) subdivided plane up against the live-surface hi-poly - I did not find these results good looking enough compared to the slower method. Since I was already planning on reusing the mesh for other models, I figured that it was okay to spend a bit of time on the initial base meshes.
I started with just a square, covering the entire hi-poly model.
I added an amount of vertical edges equal to the amount of brick cavities and lined them up. This is just to mark the general edge-flow and it will help all the way through the quad-drawing process.
After that, I inserted horizontal edges, every third right between the brick-rows, to make sure that the edges brought out the basic siluet.
With all this extra geometry, I started moving the vertical edges in-between every single brick. I iterated on the edge-flow: The red lines are where I noticed that I could rearrange, to have more relaxed polygons (to reduce over-drawing and other issues).
With the proper edge-flow, I added the last vertical edge-loops, adjusted them so that they were placed on top of the bricks (creating cavities between all bricks).
Lastly, I made sure that the mesh seamlessly tiled by moving around the edge-vertices by matching them with other adjacent copies of the model.
I had a similar process for the floor and ceiling base mesh.
I unwrapped all meshes by using the Planar UV-mapping method - This was not only the fastest, but the most accurate way to UV-map these meshes. The front needs to cover the entire UV space from 0 to 1, in a perfect square, to match up with the textures.
Note: Unlike the front side, the back and the sides don’t need any dedicated UV space because they are always supposed to be hidden.
With the basic meshes completed and UV mapped (Wall, Floor, Ceiling) I moved on to making variants and new meshes by cutting them up and by using the ‘Deform’-tools in Maya.
I used the available boolean operations to cut out shapes from the basic meshes:
Under “Deform->Nonlinear” I used the “bend” deformer, to wrap a wall mesh around itself and created one of the pillars in the set.
For other shapes, I combined several copies of the wall and cut them down to the correct size:
I made vaults by calculating the circumference of a circle with the radius of the floor’s length, divided by 4: (300*PI)/4 ~ 471.25
I used an external cube as a ruler to get the exact length right.
Note: I always made sure to retain the UV when stitching and cutting in the meshes.
With the approximate length on the mesh (471.25 cm), I deformed it into a quarter circle (with a radius of 300cm).
I then mirrored the mesh and merged them together - This way the vault won’t break tiling with the other walls on either side.
I made several different sized vaults this way, both up and down segments.
For the stairs and all the trims in the set, I did some quick sculpting in ZBrush. I basically just took cubes, dulled down the edges and then created some flat surfaces:
I made a few variations that I later used to build the stairs and other models.
The low-poly variants were made in Maya. Baked them all down on a single atlas and then I did the rest of the texturing in Substance Designer by just re-using my earlier materials.
I also did some deforming in Maya on these trims to make matching shapes to the cut-outs of the base mesh.
Anything I missed or should be more clear about?