Franco Pizzani did a breakdown of his procedural traffic system created fully in Houdini VEX.
My name is Franco Pizzani and I was born in Uruguay, although I have spent most of my life in Spain. Four years ago, when I was 27, I landed my first job in the video game industry after self-learning 3D. I worked at elite3D in Valencia, Spain, for about 3 years as a 3D artist and I had the opportunity to contribute to the development of titles such as Call of Duty: Infinite Warfare and Call of Duty: WW2 doing modeling, texturing and photogrammetry. Sometime during that period, I started to experiment with Houdini on the side, just for the fun of learning something new. However, at some point I realized that I wanted to take it further; therefore I’m currently enrolled in the Houdini FXTD program at Lost Boys Studios in Vancouver, Canada.
How the Project Started
For the third project in my Houdini course, we had to create a crowd system. The first few lectures covered basic concepts about crowds and how they behave. We also learned techniques like how to implement Reynolds Boids behaviors (separation, alignment, and cohesion) in VEX mainly by using pcopen to gather information from neighboring particles; furthermore, we discussed how to make them react one way or another depending on various parameters, so that was pretty cool. Then we learned more about the built-in Houdini crowds tools that can automatically do all that for you in conjunction with the crowd solver. While the crowd solver and the built-in tools are pretty amazing, I really wanted to experiment and push what I could do with VEX, so I thought a traffic system would be unique. In my mind, the traffic system, being something so mechanical, would give me the opportunity to be in charge of controlling and setting up every necessary attribute like vectors and forces to make the traffic system feasible. At the same time, simulated traffic systems don’t seem to be your typical Houdini project, so I saw it as a double win.
Base of the Project
The core of this project is a pop solver and a bunch of pop wranglers (the nodes where you write VEX) that basically read and set attributes. Then, these attributes are used to define states based on different conditions.
There are over 90 attributes being calculated at every frame, these range from what I call “primary attributes” that are kind of the raw data composed of direction vectors, distances, and information about other agents in the simulation. These primary attributes are then analyzed to set “state attributes”. These state attributes are used to define the state of the vehicles, which are either “accelerating”, “braking”, “stopped” or “traveling at top speed”.
A very simple example: if there is a vehicle in front of me that is in the same lane as me, its direction, therefore, is the same as mine, and the distance to the vehicle in front is lower than a predefined braking distance, then the attribute “braking at vehicle in front” will be true, triggering the “braking” state. When this state is triggered, there is a brake node in charge of reducing the force applied to my vehicle based on the distance, thus reducing my velocity and distance to the vehicle in front preventing it from ever colliding. If my vehicle is in the “braking” state and my speed equals 0, then it must mean that I’m stopped, so this triggers the “stopped” state. If a vehicle is braking or stopped but there is no reason to be doing that anymore, it will change to the “accelerating” state, applying force again. With a long list of “if else if” statements, I can change from state to state checking for different conditions using these “state attributes”.
VEX functions like pcopen and neighbor are your best friends when it comes to accessing other points and their attributes in the simulation. I tried to keep it as organized and modular as possible. Each frame the pop solver will execute these nodes from left to right and top to bottom.
Road & Traffic Structure
This intersection exists in real life (check out this video). It’s great to have good reference footage which prevents you from making decisions based on estimations that can backfire in the end. After looking at it a few times you get a good idea of what is going on. I really like it because it gave me the opportunity to implement not only vehicles moving but also vehicles respecting traffic lights, stop signs, yielding to others and even avoiding pedestrians. It took me a while to get the traffic light pattern right, but other than that it’s pretty straight forward… well except that they drive on the left, ha!
When I first started the project I prototyped the basics, made vehicles follow the road (in single lane) and brake/stop for each other. Later when I expanded it to have more than 1 lane per road, I saw that vehicles seemed to brake and stop for no reason when they had no other vehicle directly in front of them. After doing some debugging I found that they were stopping because I did not implement any logic to check whether the vehicle in front was in my lane or in the adjacent one, so a lot of the work really happened as it needed to.
It’s important to keep track of the events that are happening and their priority when checking for conditions to set states. A vehicle may be braking to stop at a red traffic light, but if before the traffic light there is a stopped vehicle, it must stop for it first.
I’m using splines to guide the vehicles. Each spline has a “road name” assigned and knows what are the splines that are connected to it. When a vehicle is traveling on a road, it reads these attributes from the spline, this way it knows where the next streets are and where they can go. If the options are 2 or more, each vehicle will randomly choose where to go next. This is the part of the project that I’m least happy with because setting up these splines requires some manual work to place them and assign the road attributes to each spline.
Managing the Traffic
I found that staying true to real-world units goes a long way because everything becomes more predictable and easy to visualize. For this reason, I built my roads and vehicles to real-world size.
The pop solver in Houdini uses velocity to move points; this velocity is measured in meters per second, so if the velocity of a vehicle is 25, it means that it is traveling at 90 kilometers per hour. Generally, you don’t want to manually mess with the velocities generated by the pop solver, instead, you feed it the force attribute, and the solver will calculate the velocity from it.
Since each vehicle knows their position and the position of the road they are following, a direction vector is calculated from the position of the vehicle to the road it is following, and then the force is applied in the direction of this vector. Once we are on the road, force is applied in the average direction of the road, this way vehicles not only go to the road but also follow it.
Other basic math concepts like dot product and cross product are used broadly. With dot product, we can easily determine whether we are behind or in front of another agent. If the dot product of my orientation vector and the direction to my neighbor vector is bigger than 0, then it means that my neighbor is in front of me. On the other hand, a result smaller than 0 means that it is behind.
When vehicles are sourced in the simulation, I immediately assign them a random “speedmax” attribute.
In my simulation, vehicles get a speedmax attribute between 11.1 and 17.7 meters per second, which equals to a speed between 40 and 64 kilometers per hour. The popsolver understands this speedmax attribute, and won’t allow the velocity to go above the specified value no matter how much force is being applied. This is an easy way to set the maximum speed that each vehicle can drive.
Now, having the roads at the real-world size, and vehicles traveling at a maximum speed of 64 kilometers per hour, it means that it takes some time for vehicles to enter and leave the simulation after traveling their roads. For this reason, I’m simulating a crazy 14000 frames, which equals to about 10 minutes of traffic flow and takes about 5 minutes to simulate. With the simulation cached, the retime node allows to easily speed up the simulation to the desired speed before rendering to save render time. The video speed is 5 times real-time speed.
Another important aspect is variation through randomness. Each vehicle gets random values for example for how close they need to be to a red traffic light before they start to break, how close they aim to stop behind the vehicle in front, or the time they take to start moving again after being stopped and they are clear to go. All this variation makes for a more realistic simulation.
Vehicles know at all times if there is something that they need to brake or stop for, and when the distance is below the threshold defined in the attributes above, they will slow down simply by reducing the force they are receiving, the solver does the rest by reducing their velocity. The vehicles source geometry is carefully set to be normalized to 1 meter long, this way I can assign a pscale attribute (size) to each vehicle based on real-world units. Since each vehicle knows their size (pscale) and the size of the vehicle they have to brake for, I can accurately calculate what is their distance from the front bumper to rear bumper.
Traffic Lights & Pedestrians
I think adding pedestrians made it look more complex than what it really is. I had everything I needed for them to work, the same logic that worked to detect other vehicles and brake/stop for them worked for pedestrians. It’s part of the beauty of Houdini: make 1 setup and reuse it by tweaking or adapting it a bit for different things. Pedestrians need more work because right now they act a bit crazy, they just mindlessly try to go to their destination, and they are not run over because vehicles always stop for them. Ideally, I would implement some logic to make them stop before crossing until there is a safe gap or the pedestrian light is green.
Traffic lights are a big part of the setup, without them, this intersection could be a mess under the high flow of vehicles.
I treated traffic lights as an entity that have a go or no go state. These are plugged in the simulation and each vehicle knows at all times the state and the distance to the closest light that would affect them. So with that, it’s very easy to add more traffic lights even in places that don’t make sense; if a vehicle is going to drive through a traffic light and it is red, it will break, stop, and resume movement once the light state allows it. The traffic light state pattern is predefined now, but I’m exploring ways to make them automatically change based on traffic flow.
How Many Objects Can the Simulation Handle?
I defined different pop sources for vehicles to enter the simulation. Each source has a spawn frequency, spawn count and spawn seed to give more variation. The values I have right now are kind of the sweet spot that matches my reference footage in terms of traffic flow.
In the following image, you can see what happens when I source four times more the number of vehicles at the same time. Traffic jam simulator!
I would say it scales up pretty well, but here is where those smart traffic lights could come into play to regulate this.
I can’t promise if or when I will be able to share this with people since school is quite intense and I have to dedicate my time to the next projects. On top of that, this system is still a WIP, with lots of things here and there to tweak and fix; but not only that, like I said before things like setting up the guides for vehicles to follow need some manual work at the moment, so I have to think about a solution to make that more procedural and straightforward to use before making this into an HDA. But I definitely would love to get this out there. Keep an eye out on the Houdini Artists Facebook group and my website as it would be posted in there!
Franco Pizzani, 3D Artist
Interview conducted by Kirill Tokarev