Layered Rendering (easy)

Blitz3D Forums/Blitz3D Tutorials/Layered Rendering (easy)

Pointer(Posted 2008) [#1]
This shows an alternative to using the EntityOrder function for layering objects. The EntityOrder function has the disadvantage of disabling the Z buffer, so it can only be used for very simple objects, such as sky cubes or flat HUD elements. But it is unsuitable for complex shapes, like weapon models for First Person Shooters.

Two examples where this will be useful (there is far more, see hints and tips below):

In most FPS games, the player's weapon is always drawn over the world geometry, so that it does not clip into objects when the player gets too close to a wall or something. This effect cannot be achieved by using EntityOrder, as this will totally mess up the appearance of the weapon model.

Another example are cockpits in flight sims, space shooters etc. Let's say you have ha game where you can fly around a landscape, and you have a cloud layer. For simplicity, the clouds are simply an infinite plane with a cloudy texture on it. If you now fly through the cloud layer, you can see the clouds clip through the cockpit, which looks very ugly. You want the cockpit to always appear in front of the outside world. Using EntityOrder will mess up the appearance of the cockpit, though, as the Z buffer is disabled.

The solution to these problems is what I call "layered rendering". The key to this method is the function "CameraClsMode" (you can find it in the online help, in the "Camera" category). Using this function, you can tell Blitz that it should not clear the screen before rendering. Thus, we can render the world normally, and then render other stuff atop of it, so it will always stay in front of the world.

This method is easy to integrate into existing projects, and the following text will focus on the changes you have to make as compared to the traditional rendering method.

In the code below, you will find the following line:
CameraClsMode hCamera, False, True

This tells the camera to not clear the screen before rendering, but to clear the Z buffer normally. Put this somewhere before the main loop.

You now need to organize your world into layers. The easiest way to do this is to create a Pivot for each layer, and then make your entities childs of these pivots. That way, you can easily hide and show entire layers, to draw them one by one. You should hide all layer pivots before the main loop. You can use as many layers as you like or need.

The main loop needs only slight restructuring. Normally you just call UpdateWorld and RenderWorld and then Flip. This is the part which has to be changed, as follows:

1. Call UpdateWorld.
2. Clear the screen manually by calling Cls. You can skip this if you have the screen covered with geometry at all times, which is the case in most games.
3. For each layer:
3.1. Show the layer pivot, using ShowEntity.
3.2. Call RenderWorld.
3.3. Hide the layer pivot, using HideEntity.
4. Call Flip.

And that's it! Very easy, isn't it?

You can do far more with this technique. Here are some additional hints and tips:
- You should adapt the near and far clipping planes for each layer, as to improve the accuracy of the Z buffer (use the CameraRange function). Thanks Beaker, for pointing this out.
- You can draw layers above 2D stuff. Simply put the 2D drawing between the layers in the main loop.
- This method is compatible with alpha blending. So you can have transparency in your near layer, showing the farther layer correctly filtering through. Even 2D graphics will be filtered by alpha blending. This leads to cool possibilities: You can create fades for 2D graphics, by rendering the 3D fader plane above the 2D stuff. Cool for 2D HUDs. You could also use a fancy 3D mouse pointer above a 2D GUI system. Generally, combining 2D and 3D graphics is far more flexible if you are using this layered approach.
- AntiAliasing also works with layers.

And now for the example program. It is very simple and straight-forward. It shows three rotating objects at different distances from the camera. But they are rendered in reverse order, using layered rendering. Note that each of these objects relies on the Z buffer to render correctly.



Beaker(Posted 2008) [#2]
Couple if things worth discussing.

If you render in this order:
HUD
Gun/Cockpit
Collidable 'near' mesh world/level
Eyecandy 'distant' mesh (mountains/tall buildings etc)
Sky box/sphere/plane

..and use CameraRange to stop any overlapping/penetration (and z-buffer glitching), you should gain some speed due to z-fail.

Rendering the other direction:
Sky box/sphere/plane
Eyecandy 'distant' mesh (mountains/tall buildings etc)
Collidable 'near' mesh world/level
Gun/Cockpit
HUD

.. is easier to implement but will create much more overdraw.

Any comments?

------------------------------

It was considered very bad practise before DX9/10(?) to combine 2D and 3D every frame, because it caused a lot of slowdown/stalling on [most?] gfx cards. Now that 2D is emulated using 3D in latest DX anyway, does this mean that we can go ahead and mix 2D/3D again?


Pointer(Posted 2008) [#3]
Cool, a comment from the creator of Fontext-ness! I still use Fontext, it's great :-) Thanks for making it free. I bought it years ago, but I really appreciate this generous offer.

But now, on to business:

I think that speed gain due to Z-fail is very minimal on any half-decent hardware, except if you are using pixel shaders. As B3D can't do these anyway, you practically don't win anything.

In addition, this method requires a distinct difference in distance between layers. You can't prevent a long rifle clipping into a very near wall, for example. So the only thing that you can gain is improved Z buffer precision.

Or am I wrong here? Maybe I just misunderstood your point. In that case, please, enlighten me!

But I agree that it is advisable to adjust the clipping planes for each layer, to improve Z buffer accuracy, and added this tip to my original post.