Forward rendering and scene objects

Now it’s time to get serious about ramping up production. I’ve brought up wanting to make a level editor before so here’s how I plan on making it. The rendering pipeline isn’t as good as I thought it out to be. Part of this has to do with how XNA’s content processors create models and meshes for your game, but another part of it is with how I am handling the instancing of meshes.

The problem with the code is more in the access of a particular object in the scene than the creation of them. I don’t see myself implementing a loading/saving system for scenes without fixing this first.

First, an aside: For a while I knew that the pine tree model I used in previous screenshots had some issues with how it was made, and I was right. It had a suspiciously high poly count. For reasons unknown to me, it had literally hundreds of overlapping polygons in several areas. Fortunately, they were hiding underneath the canopy, so removing them didn’t really change its overall appearance.

And remove them I did. I may not be a modeling wiz with Blender, but I know enough to “buff out” meshes in need of some clean-up. I set a temporary shortcut to delete the faces with a single click, and to much mouse abuse, cut the mesh down from almost 2700 polys to under 900. So a 67% reduction! After the fix, the test scene had a decrease in 25ms render time, and the model could now actually be usable for the game. Also, I improved the alpha testing so the mipmap textures look good in all distances, as well as added a kind of texture splatting that draws a different texture on steep slopes.


A new way to draw

I started to introduce the forward renderer to my engine. This will provide an option for low-performance hardware, when deferred rendering is too slow. Currently, it only supports one directional light, no shadows, and the terrain shader isn’t complete yet. But it’s already testable. I ran my test scene, and it averaged about 6 6.25 to 6.67 milliseconds per frame, while the deferred renderer (with shadows and terrain disabled) took 9 to 10 milliseconds per frame. Plus, having hardware MSAA is always nice.

It didn’t come without some significant rework to the scene rendering code, but this was actually a good thing in disguise. Older versions of the engine used the mesh’s GBuffer effect to draw the objects in the Scene Renderer. But it’s of no use if I didn’t want deferred rendering. I could’ve added some forward rendering shaders to the effect file, but then it will no longer be just a GBuffer effect, but a mash of many different rendering schemes. I chose to keep forward rendering in its own effect, and instead have the SceneRenderer set the current effect you wanted to use, and have that (hopefully) render the scene correctly with it.

There are two overloads to drawing a model in the Scene Renderer: one with a GBuffer effect and one with a custom effect. As I removed the requirement of a G-Buffer, the overloads now only made sense in a different way: the custom effect didn’t set parameters for camera settings, while the “standard” previously G-Buffer-only overload did. Now my rendering system is a bit more generic, and determined by whether you pass the camera vars to directly to the Scene Renderer or not.

So in the end, only the Shader classes are responsible for supplying the effects, and to send the appropriate one to the Scene Renderer. With this, implementing the forward shader class was a lot easier, and I created a new Render Profile to use it with.

Object pooling

After I brought instancing to the engine, the methods were altered to directly change the last instance (by default) or given a number, the instance that belongs to that number index. That isn’t going to cut it for the long run.

For a user interface, you shouldn’t need to care where the source of the object’s mesh is coming from. It’s more intuitive to think of them as separate entities. So I will introduce the ModelEntity  class, which define a position, rotation and scale, and most importantly, will have a reference to exactly one instance of the model. They just need a different name so you can tell which is which.

The current code to make two identical models would be:

scene.Model("fighterShipModel"); // Makes a ship model as it didn't exist yet

The Model method lazy loads, so if the model didn’t exist yet, load its content and create the first instance. But if it already exists, just return that instance. This has a hidden bug/gotcha… if you only typed the second line, you’d get two ships regardless! Also, the only way to access the instances individually is if you remember the order in which you made them or assign an InstancedModel object explicitly. Otherwise it’s lost in the void forever (to be more accurate, stuck in the origin).

With the new Entity system, the new code should read something like this:

scene.AddModelEntity("yourFighterShip", "fighterShipModel");
scene.AddModelEntity("comradeFighterShip", "fighterShipModel");

Maybe I will just call the method AddEntity instead, it could be much clearer. Only the rendering code needs to access everything about the model’s mesh data. So with that said, my scenes would benefit from additional object pools. Not necessarily for improved performance, but to group the data differently for use in different contexts. An Entity only needs to know about its position, rotation, color, what model represents it, etc. It won’t be simply a part of a list of mesh data instances.

So, the next thing to do, is have a pool of Entities for easy lookup by name. Optionally, let the program name the entity for you, because it may not be very important. An example is many rock meshes copied hundreds of times as part of the scenery. They are still individual models, but you don’t care what each one is called. All you would care is that they are rocks and you can select them and move them around. Eventually, when the physics system is in place, they can have their own collision shape as well. Fortunately BEPU would make this simple because it has a straightforward way of accessing all of them.


7 thoughts on “Forward rendering and scene objects

  1. Currently I’ve got my Forward/Deferred/Shadow shaders organized by object type; i.e. one for models, one for instances, one for terrain, and one for primitives (bounding boxes and the like). I’ve thought about splitting them out to Forward/Deferred only shaders as well, but haven’t done it. I may do this also, though. I’m currently in one of those periods of retrenchment where things have gotten a bit out of hand and refactoring is needed…

    • That’s how I split up my shaders when it comes to terrain versus everything else, part of the reason terrain isn’t working yet with forward rendering. I would like to eventually choose what renderer to use by specific object, sort of like what you have. But for now it’s just applied all at once to the whole scene.

    • It’s at 8000 units. I compensate somewhat for this by moving the near plane from 1 to 2.5. I haven’t noticed any z-buffer problems looking far out yet, only the shadows start to look a bit weird.

      • 8000 units as in distinct points on the heightmap or 8000 world units? I have 8000 heightmap units creating a 80000 world unit map length, but my far plane is at around 15000. But I render far less, so I need to tighten it up. Do you render the entire map, from start to finish, or do you hide distant parts of the land.

      • That’s 8000 world units. The map is 2049^2 points (the extra point needed to make it 2048 squares long), and scaled, I think, by a factor of 3. I render as far as the clipmaps go, so I would have to manually state how many to draw depending on the size of the map. If the clipmap areas exceed the size of the map you start seeing some serious rendering glitches.

  2. Hm, that’s definitely much further out than mine (about 2000). At that distance I lose a lot of shadow detail in close, things like thin antenna shadows and whatnot. Your shadows definitely look better. Perhaps I need to revisit mine at some point.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s