Tuesday, July 12, 2011

Top Models

With everything I've done, it's about time I start getting to drawing something to the screen. Before I can do that, however, I have to define what it is I'm going to draw. The shapes that describe the different objects in the world are called models.

Models in three dimensions are composed of triangles, and these are made up of three vertices each. A vertex (singular of vertices) is described by several numbers. We need it's position in space, which is described by three coordinates (x,y,z). We also need the normal for each point, which is used in lighting calculations and describes a direction perpendicular to the surface. This vector is also described by three values and should be of unit length. We can specify a color for each point, which requires the color values (RGB, greyscale, transparency, etc). Finally we have the texture coordinates, which describe the point on a texture that should be mapped to the vertex. Since textures are flat, this is accomplished with just two values, (u,v).

This means there are between nine and twelve values that describe a point. These values can be stored on the video card itself, using structures called Vertex Buffer Objects. Each of these objects can be called with a unique ID called names, which OpenGL provides when you request the creation of one.

This means that my model structure in the program needs to know only the VBO name.

When storing the vertex positions, however, I am doing it in the model coordinates. Depending on how I translate them to screen coordinates, I could scale or stretch the model. If I want to draw a large sphere and a small sphere, I could either store the points for each, or I could store the points for a unit sphere and scale it to the size I want.

Additionally, I could have a model consisting of different sub-models. For example, one could create a snowman out of reusing the model for a sphere, instead of a single model storing the values of two spheres. In order to do this, the snowman model would need the name of the basic sphere and two matrices, to scale and translate the spheres to the correct shape and position.

This implies two kinds of models. The primitives would consist of the Vertex Buffer Object names, while the complex ones would consist of a list of primitives and matrices applied to them. Of course, the list could also contain other complex models itself. These two types could be condensed if we stored both properties in every model. Primitive types would hold empty lists, while the VBO names for complex types would be null.

The models would then be stored in the model cache, and different entities would call them when needed.

I would still need methods for loading the vertex information into the buffers, however, and for drawing them. But this is something to look into in another post.


  1. "With everything I've done, it's about time I start getting to drawing something to the screen. Before I can do that, however..."

    And then another long post with no pictures. This made me laugh, you tease.

    However, this is the kind of programming post that I can sink my teeth into! I don't need to know OpenGL to understand this. Fun!

    Question for you: On a complex model with a gajillion triangles in it, how do they wrap textures across so many triangles? I'm thinking of the 'necklaces' that you might see in Dragon age which warp and stretch as the person moves their head because the necklace is a texture, not a model.

    But really, we can make it simple - how do we get a unified texture to be applied across two triangles?

  2. As I described above, each vertex has a texture coordinate. A texture coordinate corresponds to a point inside the texture.

    For a given texture, you apply a coordinate system going from 0 to 1 along each axis. Thus, the texture coordinate (0.5, 0.5) corresponds to the exact middle of the texture, and the texture coordinates (0,0),(1,0),(1,1) and (0,1) correspond to the four edges (textures are always rectangular).

    To apply a texture across two adjacent triangles, you apply the same texture coordinates to the shared points. So if you have a square (composed of two triangles), you apply to each corner one of the corners of the texture. Now each triangle will hold half the texture, split across the diagonal.

    The stretching occurs because the texture coordinates are pinned to the edges of each triangle, but nothing forces the triangles to stay the same shape. If you stretch the corners of the square so that you end up with a diamond, you'll be stretching the texture into the same shape. As a result, it warps.