logo80lv
Articlesclick_arrow
Research
Talentsclick_arrow
Events
Workshops
Aboutclick_arrow
profile_loginLogIn

Creating a Procedural Wall Generator for Unreal in Houdini

Fabien Burger revealed the working process behind the Elden Ring-inspired brick wall generator, demonstrated the entire Houdini workflow behind the project, and explained how the generator was integrated into Unreal Engine 5.

In case you missed it

You may find these articles interesting

Introduction

Hello! I am Fabien Burger, an Environment and Technical Artist currently open to opportunities. After playing and observing Elden Ring’s environments full of ruins, I wondered if I could find a procedural way to create ruins and started working on it. The goal was to find a solution that would not rely on simulation to allow fast iterations and would be optimized for games.

In my research, I came across Ekaterina Surikova’s HDA breakdown, some of the techniques described below are heavily inspired by hers.

Bricks Placement

Following Simon Verstraete’s tutorial, I prepared three simple bricks having the same size but different bevel seeds. UVs are unwrapped using Labs Auto UV and Labs Texel Density, so I don’t need to do it later on hundreds of bricks, which would slow the process.

Then, the walls. I wanted the tool to be used with either one, two, three, or four walls. It was one of the hardest things to handle, as each scenario would sometimes need its own solution. In this case, I would use a Switch node and apply a different solution for each. The process below corresponds to the mode using four walls.

The idea is to start making walls completely full of bricks and then only keep the ones on the corner and those affected by the damage. This is done by creating lines containing points, where every point will then be replaced by a random brick out of the three.

From a given length value for the wall, I need to round this value up or down depending on the number of bricks it can contain. That way, I can avoid unwanted spacing or penetration between the bricks.

The round expression will round a number to the nearest integer.

From a given length of 5.5 meters, the value is clamped down to 5.4 meters to precisely match the bricks' size of 0.3 meters and avoid spacing. In an Attribute Wrangle, I delete the last point to end up with the 18 points needed.

I now have one line with the right size and number of points, pointing on the X-axis. In order to get the bricks oriented in the right direction when copied to the line points, I set the points normal in an Attribute Wrangle with "@N = set(0,0,1);".

For the wall height, I make another line pointing on the Y-axis (up and down in Houdini). The same logic from above is applied but with the brick height this time. The last point is deleted in an Attribute Wrangle. To get a brick pattern, I use a group range to group one point out of two and offset them to half the length of a brick on the X-axis. Then, I copy the line pointing on the X-axis to points from the line pointing up.

All these lines correspond to only one surface out of the eight I need. To get the second surface, I duplicate them and move the copy on the Z-axis to the value of the wall width. The same process is then repeated to create the other lines needed, with the normals oriented in the appropriate direction. For the interior surfaces, I subtract two times the wall thickness value to the wall length.

When everything is ready, I use an Add node with "Delete Geometry But Keep the Points" enabled, then randomly copy my bricks to the points.

Using two Bound nodes, one on the exterior lines and one on the interior then subtracting each other with a boolean, I get the base geometry needed for the walls. I will use it later.

Damaging the Walls

To damage the walls, I create geometry that will help group the bricks I need to delete and those I need to keep. I use two kinds of shapes for this.

The first one is used to create an open gap on the wall. It starts with a cube of the same height as the wall. I make different group selections that will be scaled later with Transform nodes to modify the shape.

These shapes are then duplicated and placed at the center of each of the four walls. With a Boolean Node, they are used as a subtract to my geometry walls to preview the damage.

The second kind of shape used for the damage is length and width based. Starting from a cube, I randomly offset the bottom points on the Y-axis, group the bottom right and bottom left points, and offset them with a Soft Transform, affecting the whole geometry. The top points are flattened up, I don’t want them affected by the Soft Transform.

Repeating the process three times, and subtracting these shapes to my walls, I have everything needed to get a slope damage effect.

Removing Bricks

I now want to use my created shapes as a bounding object to group the bricks affected. I use a Group Expand node with a negative value to only keep the bricks that were fully contained by the shapes, and I delete them. With another Group Expand with a positive value this time, I select the bricks on the edges, the ones I want to keep.

I want the affected bricks on the edge to be rotated to make them look more interesting, but their rotation pivot needs to be appropriate whether they are on the left or the right side of the gap. This is one of the trickiest parts of the process to handle, as bricks from the wall length, width, exterior, and inside need to be treated differently. You may notice that I handle the “gap” and “slope” shapes separately for this, the edge bricks affected by the slope shapes are simply rotated based on their center.

I use half of the bounding box of my gap shapes to split the bricks between left and right. For more precision, I offset and inflate (Peak node) the bricks and use it as a bounding object to get the bricks on the very edge. I repeat the process for the eight groups needed.

When every group is ready, I randomly rotate every brick between 0 and 40 degrees based on their respective appropriate pivots using bbox min or max expressions. I do it one group at a time using different Transform nodes in a For-Each Connected Piece using Metadata.

To keep the bricks in the corners, I use the base geometry walls, delete all but the bottom primitives and copy thin boxes to their points. This new geometry is a bounding object for the bricks.

I randomly keep around 10 percent of the unaffected bricks so the flat parts of the wall look more interesting later.

To summarize, there are four different kinds of bricks kept: the ones affected by the gap shapes, the slope shapes, the corners, and a few random ones.

With a For-Each Connected Piece using Metadata, I randomly offset and rotate all of the bricks with a small value to add variation.

Wall Geometry

I handle the sides separately from the top and thickness of the walls. To damage the flat textured walls, I use the deleted bricks, which are more precise than the shapes. The bricks are inflated with a Peak node, voxelized and recomputed as geometry with a Remesh To Grid node, and serve as subtracting geometry to my walls with a boolean. In this case, using a UV Project for each wall is the cleanest way to do the UVs.

For the thickness of the wall, I retrieve and isolate the affected geometry from the boolean, remesh it, smooth it, select the unshared edges with a Group node and convert this group to points. I use this point group as a mask for the two mountain nodes I put on top of each other, one large and one smaller to make it look more organic. I remesh again to a lesser polygon density and unwrap the UVs at the right texel density. To add some nice details, I expand the point group, and color them black to act as a mask for the scatter from which I copy random bricks to.

With all these different elements merged together, I end up with the final geometry for the tool!

Houdini Controller

To avoid going back and forth between the hundreds of nodes contained in the graph, I put a Null node and use the Edit Parameter Interface window to create parameters. All the interesting parameters in the graph are linked to this controller.

Waiting for Houdini to cook the final mesh between every parameter change is not convenient for the tool user, so I put a switch node at the end of the graph with both the preview and final meshes plugged in. I link it to an integer parameter in my controller, which is by default set to the preview mode. You may also notice that I applied colors to the final mesh, they will act as a Vertex Color mask in Unreal to avoid using multiple materials.

Creating the HDA

In order to convert our Houdini graph into a tool and use it inside Unreal, I need to create a Houdini Digital Asset.

I select all of the graphs, press Shift + C to create a subnet, then right-click to select “Create Digital Asset”. I make sure to remember where I save the file because this is the one that will be imported later in Unreal. This is non-destructive, as we can still work on the graph after creating an HDA.

Now I need to create parameters for our HDA, these are the ones the tool user will have access to. Right-click on the HDA node and “Type Properties…” to access the Parameters tab. Instead of recreating all the parameters from my controller by hand, I can copy them to our HDA Parameters.

Integration to Unreal Engine

The HDA created in Houdini can be used inside Unreal Engine using the plug-in Houdini EngineWhat this plug-in does is read and cook the nodes inside the HDA and then deliver the result to Unreal. Make sure to have it enabled then create a Houdini Session inside Unreal.

The HDA file can be imported inside Unreal Engine’s Content Browser like any other file. 

When dropped into the scene, the HDA loads and we find our HDA Parameters in the Details panel. We cook the final result when we are satisfied with the shape.

Creating the Collisions for Unreal

In order to have accurate procedural collisions, I need to split the geometry into different pieces for Unreal to generate multiple colliders. 

I start by creating a shape that separates my four walls. I use a Convert Line Node, group the side edges, and Dissolve them. Polypath then I set the normals oriented away from the center of each piece in order for the Peak to work, then finally PolyExtrude it.

Creating thin boxes at one and two-thirds of the height of my wall and unioned with the corner shapes above, I split the collision geometry into different pieces.

Using SideFX’s documentation about Unreal collisions, we learn that we need to group our geometry and name our group right by respecting the naming convention depending on the type of collider we want Unreal to generate. In Houdini, I make sure to delete the prim groups that are not needed anymore. Using a Connectivity node set to Primitive with the Attribute “name”, giving a number for each piece, then a Partition node with “collision_geo_ucx_`@name+1`” as Rule, one group per piece is created with the right name.

In Houdini, this collision geometry is merged with our final result at the very end of the network, but in Unreal it is recognized and turned invisible to help generate the colliders.

Here is an example between one and multiple colliders given to Unreal:

Houdini Tips

There are Houdini expressions that I use all the time to avoid relying on manually typed values and keep the workflow procedural, like bbox(0, D_XSIZE), bbox(0, D_XMAX), bbox(0, D_XMIN), and centroid(0, D_X). 0 means it is reading the bounding box and centroid values from the current node. Sometimes it is useful to obtain these pieces of information from another node, so you replace 0 by “../NodeName”, don’t forget the quote marks! These expressions are extremely useful in procedural modeling to scale and move objects or create groups.

Pick and apply a color pattern to your nodes (green for UVs, purple for groups…), and stick with it. That will help you read your network when de-zoomed, especially if like me, you like to keep the entire tool in one geometry network. Also, keep your network clean by using Object Merge referencing Null nodes.

My advice to keep learning Houdini at an intermediate level is to be curious and watch tutorials even on topics that do not seem useful to you at the moment. It will open your field of solutions and possible workflows for future problems.

Conclusion

I had a lot of fun working on this tool and there is room for improvement in the future, like adding debris on the floor or letting the user input custom bricks. I find the combination of Houdini and Unreal extremely powerful and I love seeing what amazing things artists do with it, especially for games. Huge thanks to 80 Level for the opportunity to share my work and thank you for reading. I hope you find this breakdown instructive and feel free to DM me if you have any questions!

Fabien Burger, Tech & Environment Artist

Join discussion

Comments 0

    You might also like

    We need your consent

    We use cookies on this website to make your browsing experience better. By using the site you agree to our use of cookies.Learn more