Sunday, March 10, 2019

Procedural City: Animating Pedestrian Models

I finally got around to experimenting with adding animations to the 3D models of people in my procedural city. I ended up doing something pretty simple that at least looks better than no animation at all. Before I get into the details of what I tried, let me discuss the problem statement in more detail.

I've decided on the following constraints to pedestrian animation in 3DWorld, listed starting with the most difficult:
  • Must use free models found online (since I don't have the time/patience/skills to create them myself).
  • Must be done without buying or installing any fancy 3D content creation/animation tools.
  • No art skills required.
  • The same animation system must work with multiple models from different sources in different formats.
  • Must scale to a few hundred animated models of people with ~10k-50k triangles each.
  • Something that can be done in a few weeks or less.
  • Should look better (more realistic) than no animation at all. I sure hope this is the case!
That seems like a real challenge for creating high quality animations. I suppose the standard flow would be to join a 3D model website where I can download models of people with animation data. Then either use a third party model/animation loading library, or write this part myself. Then implement the animation system in a special shader with a lot of CPU side code support. That would be a lot of work and could take more than a month of late night programming. Is there an easier system that I can use to get started, one that works with the models I already have?

Let me throw in some simplifications/trade-offs that should make this problem easier, at the cost of reduced quality and realism.
  • I only need one simple walking animation.
That's a start, but it doesn't help much. How about:
  • Only the legs need to move. We'll just leave their arms sticking out to the sides for now.
 Okay, that's a bit easier, but still seems like a lot of work. Let's do something silly to really simplify things:
  • Only the hip joints are animated.
Yeah, I know, it's not going to be the greatest walking animation. But hopefully it will be better than no animation where people appear to slide on the ground or ride on invisible roller skates. What's the worst that can happen? Take a look and see - but don't judge it until you see the end. I started with some simple test animations.



The video shows me cycling through the various animations, which are named in white onscreen text. In case you didn't catch it, the animation names are: "The Slide", "The Bunny Hop", "The Flip", "The Twirl", "Marching", "Walk Like an Alien", and "Walking". The vertex shader code for all of these can be found here in my 3DWorld GitHub project. Sure, all of the animations look at least somewhat silly. That's the point, right now I'm just experimenting. I'll bet you laughed at the alien walk animation; I watched the video three times so far and laughed every time! That alien walk was actually one of my first attempts at rotating the legs at the hip where I was incorrectly scaling the hip location by the height of the model.

[Bonus: The pedestrians are actually crossing the roads safely and not getting hit by cars.]

The final walking animation doesn't look too bad. In fact, it looks almost normal when viewing pedestrians from a distance. It looks like someone walking very stiff-legged without bending their knees up close. I feel it's definitely better than no animation at all. I'm sure I can work to improve it, but I'm surprised at how good it looks already with so little work. All of this took only a few hours this weekend, and at least half the time was spent adjusting constants and transforms in the shaders. That's definitely time well spent. I didn't have to download new models, install 3D modeling tools, add new dependencies, parse a new file format, or implement a new shader flow. I'm not sure why I was so worried about this originally.

How did I implement this animation feature? There are in fact no animation files, no bone weights, no transform matrices. I'm passing the static model vertices that are loaded from disk straight to OpenGL for rendering. All of the magic is in the vertex shader, which applies transforms to the individual mesh vertices using a procedural algorithm. I suppose this is how procedural animation works.

The various body parts can be identified from the sign and magnitude of the (x,y,z) coordinates of each vertex. X = left/right, Y = up/down, and Z = front/back. The sign of the x-value will tell you which side (left or right) of the body you're on. Normalized y-values near 0.0 are the feet, and values near 1.0 are the head. This allows the shader to, for example, alternatively rotate the left and right legs about the hip by looking the the x and y values of each vertex. The hip joint is located around y=0.4 in all three models.

Each pedestrian has an animation time value. This time is reset to zero when stopped so that their body has a neutral (default) position. As each person walks, their animation time increases based on the product of velocity and elapsed realtime, producing a smooth animation timeline while in motion. The faster the person is moving, the faster the animation plays. A combination of fract() (fractional component) and sin() are used to turn linear time into a periodic walking cycle.

There's one thing wrong in the video. Can you spot it? The rotations I'm applying should also rotate the vertex normals. Without this, the lighting on the models is incorrect. I need to go back and fix that. Do I simply rotate the normals using the same matrix? [Update: It's fixed, but I didn't record a new video yet. Apparently you do just apply the same matrix to rotate the normals.]

I'll continue to work on improving pedestrian animation. It should be straightforward to add additional joints in this way. The knee joint is probably next to add. This a very slow and tedious way to put together animations, but it does require minimal code, and it works the same with all the models I have. Once I have enough models, it will probably be less work to add animations this way that it would be to animate each individual model using a real animation tool.

4 comments:

  1. Hey! Nice solution! You could probably get the arms to swing as well, using this same technique.

    ReplyDelete
    Replies
    1. I like this solution because it creates fun walking animations for non-people models as well, which are shown in later posts.

      I did experiment with arms. The problem is, this requires custom per-model constants. I can't simply detect arms as "any vertex between some range of z-values" because it will pick up parts of the torso as well. Instead I have to also include some check for distance from the vertical centerline of the model. But different models have their arms stretched out different amounts, so no single value works well everywhere. This also tends to pick up some parts of the shirt/jacket that shouldn't move.

      Delete
    2. Yeah, you'd need to do some more advanced topology analysis to isolate the arms. Seems possible though! The fingertips should be easy to find, then work inward following connected faces until you find the armpits. That's what I would try. I'm surprised you aren't using a similar approach to find the legs. I'd start at the feet and work upward until you found the crotch, the first place where the right and left meet up. That would give you a general solution which should work with people on stilts, and even cars (which would "walk" with just the lower section of their tires).

      Delete
    3. All of the animation logic is in the vertex shader, and it doesn't have access to the full triangle mesh. I could do it as preprocessing on the CPU, but some models have different body parts split up into different meshes/materials. So I would need some way to consider the entire model as a whole. I just haven't gotten to that yet.

      Delete