Wednesday, February 27, 2013

Isometric Render Order

I’ve had significant difficulties in the finer minor details of isometric view blowing a lot of my time and I feel that as this problem is repeated half a hundred times with every single game that is isometric I’m going to try to explain it as well as I can.

I already had a post before about isometric camera. It showed a triangular prism. That’s not correct (because otherwise all objects on the same plane would not be equidistant from the camera and thus you’d get “perspective”).

Let's do that diagram again:

A z buffer is a buffer used when rendering to determine what pixel should be rendered there. So while it is rendering, it records the distance of a particular pixel rendered on the screen to the camera and saves it in the z buffer. When some other object wishes to render in the same spot, it's z coordinate value is checked against what is in the buffer and if it is higher then it replaces the previous pixel (if they're the same value, then the behaviour might be funky).

The stuff you can see is inbetween the near clipping distance and the far clipping distance. The ratio of the two determines the accuracy of something called the "z buffer" in the graphics engine. With Ogre3d, your z buffer accuracy is a number of slices in between those two distances.

That viewable area is called the viewing frustum.

Now then, onto what I want to happen there.

In a particular square I’d like to have, in order of first item is the most hidden and the last item is the most visible:

  • Terrain
  • Grass
  • Bush or Tree
  • Unit
  • Structure

Between squares I’d like to have, in order of first item rendered is blocked by any later item,

For x = 0 to x = max
 For y = max to y = 0
  Render x,y
 End Loop
End Loop

Notice that the inner loop for y is reversed. Imagine a square. Now rotate it 45 degrees. Notice that the top point is (0, max). Notice the bottom point is (max, 0). Switching the order of the inner loop will get what we want.

The loop doesn’t exactly render squares in the exact order that you might write down by hand. But it’s key to remember two points. A square blocks images of squares with a lower x value, or a higher y value. The loop preserves this.

Imagine a 3x3 square. Drawing by hand in the correct order you’d likely do the diagonals

  • (0,2)
  • (0,1) (1,2)
  • (0,0) (1,1) (2,2)
  • (1,0) (2,1)
  • (2,0)

The loop does:

  • (0,2) (0,1) (0,0)
  • (1,2) (1,1) (1,0)
  • (2,2) (2,1) (2,0)

But it satisfies the rules, so we’re okay.

Now, for actual implementation issues, the question is what magical class holds all the data such that it can make these render decisions? Well, I have GameState. This contains all information about terrain and units. But it’s not actually arranged in a way for the graphics rendering to work properly (I have graphics updating inside the updateTick of particular units).

I can change that though.

I can add a method to GameState->createScene() and render everything in the correct order. Then units will update their entities (and this hopefully doesn’t change the render order in Ogre).

Okay, now that I’ve described what I want, let’s see how to do it in Ogre3d.

What we want:

//we could even have a z-level loop if we were doing something akin to dwarf fortress or gnomoria
For x = 0 to x = max
 For y = max to y = 0
  Render terrain
  Render grass
  Render bush or tree
  Render unit
  Render structure
 End Loop
End Loop

We have to create the materials and for these materials, you have to define a rendering “pass”. I use these to get what I want for now:

scene_blend = alpha_blend
depth_check = off

I’ll start with what I want.

In a particular square I’d like to have, in order of first item is the most hidden and the last item is the most visible,

  • Terrain
  • Grass
  • Bush or Tree
  • Unit
  • Structure

Between squares I’d like to have, in order of first item rendered is blocked by any later item,

For x = 0 to x = max
 For y = max to y = 0
  Render x,y
 End Loop
End Loop

Notice that the inner loop for y is reversed. Imagine a square. Now rotate it 45 degrees. Notice that the top point is (0, max). Notice the bottom point is (max, 0). Switching the order of the inner loop will get what we want.

The loop doesn’t exactly render squares in the exact order that you might write down by hand. But it’s key to remember two points. A square blocks images of squares with a lower x value, or a higher y value. The loop preserves this.

Imagine a 3x3 square. Drawing by hand in the correct order you’d likely do the diagonals

  • (0,2)
  • (0,1) (1,2)
  • (0,0) (1,1) (2,2)
  • (1,0) (2,1)
  • (2,0)

The loop does:

  • (0,2) (0,1) (0,0)
  • (1,2) (1,1) (1,0)
  • (2,2) (2,1) (2,0)

But it satisfies the rules, so we’re okay.

Now, for actual implementation issues, the question is what magical class holds all the data such that it can make these render decisions? Well, I have GameState. This contains all information about terrain and units. But it’s not actually arranged in a way for the graphics rendering to work properly (I have graphics updating inside the updateTick of particular units).

I can change that though.

I can add a method to GameState->createScene() and render everything in the correct order. Then units will update their entities (and this hopefully doesn’t change the render order in Ogre).

No comments:

Post a Comment