2.5.1 Textures: The basicsColored, blended and transformed shapes are nice and neccessary for all kinds of graphics but not quite sufficient for "real" game graphics. Let's texture them.To do this, we first need to generate a texture using image data which we can load from an image file. The shortest way to get a result is using the Load command: ZweiDe.Texture texTemp = ZweiDe.Texture.Load( ... );It accepts one to three parameters. What it definitely needs is a filename referring to a source file. ZweiDe uses DevIl for image file loading, so most common formats should be accepted. Optional parameters are additional texture flags and the noStretch boolean but we'll come back to them later. After loading a texture, drawing it is just as easy:
ZweiDe.DrawTexture( ... );
Pass the texture object and a position and the rest is done by ZweiDe. Internally, drawing a texture equals drawing a textured rect,
so anything you can do with a shape (Coloring, blending, transforming, ...) is also possible when drawing a texture!It is also possible to fill out a certain area using a texture using
ZweiDe.DrawTextureArea( ... );
This command works similar to DrawRect except it needs you to pass a texture and fill style (tiled or stretched?). Keep in mind that
stretching is not always an available operation; this topic is discussed in the chapters "Texture Flags" (->EdgeClamp) and "Stretching"!If you want a texture to be transformed - for example - around its center instead of its upper left, you'd have to call ZweiDe.SetHandle whenever drawing the texture. You can simplify this by calling a texture objects individual SetHandle method once after its creation; its individual handle will be automatically applied in addition to the global handle. texTemp.SetHandle( ... );These are the Texture basics which should be sufficient for most purposes. More advanced texturing topics will be explained in the following chapters. |
2.5.2 Textures: Texture FlagsYou may have noticed the two yet-unexplained parameters in ZweiDe.Texture.Load specifying texture flags and stretching; this chapters aim is - among others - to make you understand what excactly they are doing.Let's start with texture flags. Their purpose is to configure a textures general behaviour for example whether or not filtering is enabled when displaying the texture scaled or rotated. |
|
Click on one of the hover-images above to get a more detailed overview image comparing different filtering methods. |
|
The next texture flag is rather unspectacular and of no use in most cases. Though, in some cases, it is absolutely neccessary: The EdgeClamp flag. When adding
TexFlag.EdgeClamp
to the texture flags, the texture data is assumed to describe a finite image which is not intended to be tiled.
When exceeding a textures area, it is normally - without EdgeClamp - "wrapped around" and repeats itsself. With EdgeClamp, only the textures last pixel is repeated. So, EdgeClamp is not for tiled textures but it doesn't seem to have an effect on non-tiled textures either.. or does it? |
|
|
| Take a look at the right image. It has been upscaled 2x in a popular graphic program (a) and in ZweiDe using SetScale when drawing the same texture with (b) and without (c) EdgeClamp. |
|
|
|
When now looking at the left image - a bigger version of the non-EdgeClamp 2x upscaled image bit - you'll notice the left edge to be
grey instead of white; the same occurs with its lower edge: Grey instead of black. This is because without EdgeClamp, the |
|
|
texture is assumed to be infinite, repeating when exceeding its area. When smoothing is performed, the nearest pixels are
picked out and mixed together. At the non-EdgeClamp textures border or edge some of the nearest pixels are located at the textures opposite
border. And that's the point. This is why non-tile textures should always use the EdgeClamp flag. A little hint: You can globally specify what texture flags are used when leaving out the flags parameter on texture creation using ZweiDe.Texture.DefaultTexFlags = flags; |
||
2.5.3 Textures: Anisotropic filteringWe've now completed the texture flags. Now's time for anisotropy!You may have heard of anisotropic filtering. It is a technique that is used to prevent filtered textures drawn distorted or in a perspective being blurry. You can activate it using texTemp.AnisotropicLevel = level;while "level" is a value from 1.0 (no anisotropic filtering) to ZweiDe.Texture.GetMaxAnisoLeve(). You may also set a default value using ZweiDe.Texture.DefaultAnisoLevel = level;You can take a look at how anisotropic filtering looks like combined with different texture flags in the following list. Hover one of the images with your mouse cursor to see the same with 16x anisotropic filtering and click on one of the images to open a more detailed overview: |
2.5.4 Textures: StretchingEnough with anisotropic filtering, let's take a look at the third parameter of Texture.Load: The noStretch-boolean. So, what is "stretching" anyway?To explain this, we need to go down to the OpenGL level of ZweiDe. Most video cards - and so OpenGL - only support textures which have dimensions that are a power of two e.g. a width of 2^i and a height of 2^j while i and j are non-negative integers. Of course ZweiDe also supports any other texture dimensions, but to be honest: Non-power-of-two textures are simulated while internally still being power-of-two. Basically, there are two techniques to simulate being a power-of-two-texture in ZweiDe:
1. Generating a power-of-two-texture but using only the part that is needed to contain the non-power-of-two data
filling out the rest with specific color.
The noStretch parameter is how you choose between the two versions. So, what's the difference? Take a look at the following comparison:
2. Generating a power-of-two-texture and stretching the non-power-of-two data in order to fit the textures dimensions. |
|
|
The left image shows how a 100x100 grid texture is displayed when loaded unstretched. The right image shows how it looks like when trying to tile it. A rather strange "tiling" is the logic result. |
![]()
|
|
The left image shows the same texture when loaded stretched. Apparantly, it looks a little blurry - which is no surprise
because it has been upscaled to fit the power-of-two-texture and then downscaled again to fit its original dimensions when being displayed. The right image shows the tiling experiment. It looks just like a tiling should look like. |
![]()
|
|
Conclusion: Only use stretched textures if you don't need pixel perfection or it is absolutely neccessary to tile a non-power-of-two-texture. |
|
2.5.5 Textures: Texturing a shapeTired of DrawTexture and DrawTextureArea? You want to texture whatever shape you like? In this case, this is your chapter.Texturing a shape isn't harder than by-vertex-coloring - in fact, it works similar using the same stack technique. |
|
|
When texturing a shape, each vertex needs a UV coordinate; it describes a percentual position on the texture image that will
be located at the current vertex during rendering. Imagine rendering a textured rect that shall look like as if we'd drawn
the texture using DrawTexture. Its upper left vertex should be textured with the textures upper left, its upper right vertex should
be textured with the textures upper right, etc. etc. In UV coordinates: The rects upper left will get a UV of [0.0, 0.0], its upper right will get a UV of [1.0, 0.0], its lower rights UV will be [1.0, 1.0] and so on. |
|
| A UV coordinate of [1.0, 1.0] represents the actual textures lower right. However, if you have loaded a non-power-of-two texture without stretching it to power-of-two dimensions, [1.0, 1.0] exceeds the boundaries of your actually loaded image as this only covers a part of the internal texture area; while [1.0, 1.0] refers to the power-of-two container texture. What UV coordinate you need to target your image datas lower right is saved in the texture objects UVRatio property. This is also used in DrawTexture and DrawTextureArea in order to calculate the vertices UV coordinates. |
|
|
Specifying a shapes vertices UV-coordinates works similar to specifying their color when using by-vertex coloring: You add all your UV-coordinates to a "first in first out" stack and when the shape is drawn they are automatically used in the same order you've put them to the stack.
ZweiDe.AddUV( .. );
|
|
|
|
|






