Jump to content
  • Advertisement
Dan DAMAN

Efficient crowd (audience) rendering

Recommended Posts

I'm not completely sure, but I think GPU skinning was a pro feature(when that was a thing) and only in the DX11 subset.

Thank you for  IJobParallelForTransform. Nice one, although the docs were a blank page which was disappointing. I think the concept you may be searching for is instancing. Unity appears to have [ animation instancing ] which I havn't tried yet. Perhaps something there for this.

Share this post


Link to post
Share on other sites
Advertisement
2 hours ago, GoliathForge said:

I'm not completely sure, but I think GPU skinning was a pro feature(when that was a thing) and only in the DX11 subset.

Thank you for  IJobParallelForTransform. Nice one, although the docs were a blank page which was disappointing. I think the concept you may be searching for is instancing. Unity appears to have [ animation instancing ] which I havn't tried yet. Perhaps something there for this.

Thanks for the link, that looks like it could be quite useful for what I'm trying to do as well. I'll have to give it a look :)

As for the rather poor documentation on IJobParallelForTransform. I found this post quite helpful in actually understanding how the interface is meant to be used. I was pretty mystified about it until I found the article myself.

Share this post


Link to post
Share on other sites
5 hours ago, DerTroll said:

However, I don't know how much control Unity gives you to realize such a crowd system.

You can write your own shaders from scratch, then stream arrays of bytes from the CPU into GPU buffers that the shaders can read from... so you can make anything, given enough effort :D

22 hours ago, Dan DAMAN said:

I have some concerns about the sheer size of texture data that would produce but it's worth a shot.

21 hours ago, Acosix said:

Making a dynamic audience is hard to do and while the newest games don't have the flat picture audience of FIFA of the year 2000

Sprites don't have to look "flat", or take up too much memory. If done properly, they look just as 3D as a mesh does, but if you get too close for the texture resolution you can see they have jagged texel edges unlike a mesh... If you pre-render them from every possible viewing angle, for every possible animation pose, then yeah, that's a lot of memory... Instead, render the sprites from scratch each frame from the limited number of viewing angles that you actually need, in the poses that you need. The tech that I've worked with will only use a small number of different characters/archtypes (a particular model + a particular animation) say, 32 characters... and then only a limited number of viewing angles, say 4. This gives you a sprite atlas of 32 columns and 4 rows.

If you've got 10k people in the crowd, that means you've got 10000/32 = ~312 instances of each character around the place. For each character, find 4 representative viewing angles that will give the best representation / least error for all the visible instances of that character. You can give more weight for nearby instances, and allow larger error on far away instances, too. Record which "representative viewing angle" is the best match for each instance of the character -- this is the "row" in the sprite atlas that this instance should use this frame. The "column" is fixed as the character type for the instance.

Then you just render your 32 characters in 4 poses each (128 skinned models) into the 128 cells of your sprite atlas. Then you draw (up to) 10k quads as a single mesh, each one referencing one of the 128 cells in the sprite atlas.

In my implementation, we stored a very compressed G-Buffer in the sprite atlas (17bit color, 11bit normal, 4bit alpha) so that each instance could be lit correctly, and we have drawn stadiums with up to 100k seats, plus extra crowd members standing around the place too, on mobile hardware :)

Share this post


Link to post
Share on other sites
2 hours ago, Hodgman said:

In my implementation, we stored a very compressed G-Buffer in the sprite atlas (17bit color, 11bit normal, 4bit alpha) so that each instance could be lit correctly, and we have drawn stadiums with up to 100k seats, plus extra crowd members standing around the place too, on mobile hardware :)

Congratulations and thumbs up. What regards efficiency, this would be very effective audience rendering process.

Share this post


Link to post
Share on other sites
Posted (edited)
13 hours ago, Hodgman said:

Instead, render the sprites from scratch each frame from the limited number of viewing angles that you actually need, in the poses that you need. The tech that I've worked with will only use a small number of different characters/archtypes (a particular model + a particular animation) say, 32 characters... and then only a limited number of viewing angles, say 4. This gives you a sprite atlas of 32 columns and 4 rows.

[more good advice elided]

I took a peek at some of the sports games on your website and they look really good! :)

To clarify are you rendering the 32 characters to the sprite atlas in-game or using an atlas with pre-rendered images? I'm thinking for Unity something like 32 objects containing one skinned mesh and 4 cameras each. After a quick check it looks like I can use Viewport Rect in each camera to render multiple cameras to one texture.

Switching to using jobs has improved the framerate to 50-60FPS for most of the time. Here's a little clip with all the characters doing random bored animations. The GIF has a much lower framerate than the game. Artwork is really early still which is why that horrible Z-fighting is happening. I haven't actually modelled anything for that corner and simply have a huge cube there. This one uses ten skinned meshes to seed animations into the audience.

audience.gif.7c4ec7318cb3e6ff71532047649dab12.gif

Edited by Dan DAMAN

Share this post


Link to post
Share on other sites
Posted (edited)
33 minutes ago, Dan DAMAN said:

I took a peek at some of the sports games on your website and they look really good! :)

To clarify are you rendering the 32 characters to the sprite atlas in-game or using an atlas with pre-rendered images? I'm thinking for Unity something like 32 objects containing one skinned mesh and 4 cameras each. After a quick check it looks like I can use Viewport Rect in each camera to render multiple cameras to one texture.

Switching to using jobs has improved the framerate to 50-60FPS for most of the time. Here's a little clip with all the characters doing random bored animations. The GIF has a much lower framerate than the game. Artwork is really early still which is why that horrible Z-fighting is happening. I haven't actually modelled anything for that corner and simply have a huge cube there. This one uses ten skinned meshes to seed animations into the audience.

My idea here is to use Hodgman's approach for real-time action.

Here's a little challenge when you are ready to further improve your quality of audience rendering:

You can combine the approach with the camera-based display: you can use a temporary camera - one which moves from slow motion replays, or static camera, which just rotates, without movement, like the one in the camera clip above, for the stadium introduction.

Basically you transfer the animation positions/types into camera mathematics information.

Why is this related to the actual audience rendering?
Because it is a method to temporary partly unload the entire audience information, as in being loaded at all times, and when the slow-motion replay or stadium-show introduction ends, you reload the entire audience information into the RAM.
After you have successfully implemented this, you just use 60 FPS for the video-clip, which is enough for every basic High Definition-based movie/Youtube videos(576p to 720p).
If you need a higher frame-rate you just return the delta-time setting to the desired frame-rate.

On a side note:
I'm a C++ programmer and haven't until recently noticed, this is a Unity-developed game.
So basically, correct if I am wrong on anything which differs between C++ and C#,  but I'm personally convinced it should work in C# too.

Edited by Acosix

Share this post


Link to post
Share on other sites

I worked on the crowd rendering for NBA Inside Drive 2004 for the Xbox OG.  Here's what we did:

  1. Made about 10 3D models of various low-rez, animated audience members.
  2. Divided up the stadium into seating sections.
  3. Made a tool to automate placing camera-facing sprites for each seat in every section.
    1. bonus: The sprites were ordered inside the mesh from front row to back so that the front sprites would z-occlude back seats within a single draw call
  4. Every frame we'd pick one on-camera seating section and render about 20 sprites using the 10 models.  All sprites were rendered as if the person was positioned in the center of the section relative to the current camera.  Those 20ish sprite would be used to populate the whole section until the section was next updated.  So, the perspective wasn't perfect for any of them.  But, it was good enough to look good.  And, the sprites would be reused for a few frames, but the update rate vs. performance was easy to tune.
  5. bonus: The audience all wore grey shirts with 50% gray alpha.  They were rendered into the sprite sheet with alpha blend disabled and the background cleared to (0,0,0,0).  So, the background had alpha=0, the shirt texels had alpha=127 and the skin/pants had alpha=255.  The shader was set to colorize texels with alpha = 127 and we used alpha blend disabled and alpha test (discard) enabled for alpha < 64.  The Xbox had a pixel shader instruction "multiply, multiply, select" that let us colorize the shirts differently per audience member in a single shader cycle.

 

Share this post


Link to post
Share on other sites

For now I'm going to take a crack at something like the solution that Hodgman described since it fits nicely with stuff I'm already familiar with doing (render textures, generating meshes etc

On 7/5/2019 at 3:10 PM, corysama said:

bonus: The audience all wore grey shirts with 50% gray alpha.  They were rendered into the sprite sheet with alpha blend disabled and the background cleared to (0,0,0,0).  So, the background had alpha=0, the shirt texels had alpha=127 and the skin/pants had alpha=255.  The shader was set to colorize texels with alpha = 127 and we used alpha blend disabled and alpha test (discard) enabled for alpha < 64.  The Xbox had a pixel shader instruction "multiply, multiply, select" that let us colorize the shirts differently per audience member in a single shader cycle.

That sounds really cool and like a good way to get more variety in appearance so it's not obvious that the audience is only N characters duplicated however many times needed.

I think with this I'm pretty good to implement a final solution for this and either post it up here or throw a blog up for it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!