|
Texturing Basics ExampleFor most applications, textures provide the majority of the visuals. Where geometry is too costly for every single fine detail, textures can make do to give quite an acceptable alternative. Where textures really fall down is when you are starting to get up really close to the objects. That flat nature really shows out. Multitexturing was developed to allow the end programmer to tweak the visual effects by combining a series of textures together to generate a single image that is then applied to the object. Multi-texturing is most well known as the technique for "bump mapping" - making a surface look rough when it really isn't. In this tutorial we'll show you how to set up your own application to use multi-texturing techniques.
If you would like to see the complete version of this code, it can be found in
the examples/texture directory. There are two demo applications -
Setting up the applicationThe demo starts, once again with a slight variation of the Hello World demo. In order to demostrate multitexturing effectively, the first demonstration is going to use a box for the geometry and offset the viewpoint so you can see it as though looking down on it from an angle. The second demo, where we show off bump mapping will just use a big flat rectangle. Since this is a texturing example, we need to load a heap of textures for the various stages. If you're not familiar with how to create a basic texture instance in Aviatrix3D, then please wander back to the Texturing Basics tutorial. Note: Multitexturing requires hardware support. The number of stages that can be combined together is dependent on the hardware and is different across video cards. Aviatrix3D does not provide fallback mechanisms for when you request more stages of multitexture than the local hardware supports. What's the difference between Single and Multi Texturing?
In the previous tutorial you got an
overview of the classes available to texturing. In normal texturing, you
create a single texture instance and apply that to the object. At the
Aviatrix3D level, that means only creating a single instance of
Multitexturing works as a pipeline. Each texture represents a step in a
sequential series of operations. Each stage uses the output from the previous
stage and the texture in this stage as it's input, along with some instructions
on the way to combine it. At the OpenGL level, this is relatively simple to
understand as each stage is just one command followed by another in a state
machine. At the scene graph level dealing with objects rather than state, the
ideas become a little more muddled. In the Aviatrix3D view of the world, each
instance of
The A Simple two-stage demoThe first demo shows setting up a basic two-stage test that multiplies two textures together. The base texture is just a white texture with some text on it. This is then modified by a second texture that adds colour to the first, and ignores the underlying object colour. Our application starts by loading up two textures - the base and colour, so let's assume we have two variables for these: TextureComponent2D[] base_img = loadImage("textures/modulate_base.gif"); TextureComponent2D[] filter_img = loadImage("textures/modulate_red.gif"); Texture2D base_texture = new Texture2D(); base_texture.setImages(Texture.MODE_BASE_LEVEL, Texture.FORMAT_RGB, base_img, 1); Texture2D filter_texture = new Texture2D(); filter_texture.setImages(Texture.MODE_BASE_LEVEL, Texture.FORMAT_RGB, filter_img, 1);
Now we need to set up
The first stage supplied to the pipeline has two options for the input - the
object's basic colour (derived from material and lighting values). There's no
option for using the previous texture, because there is no previous texture for
the first stage. However, we do have the option to completely ignore the
object's colour, which is what we want to do for this demo. To ignore the
values from the previous stage (in this case the underlying object colour), set
the mode to TextureAttributes base_ta = new TextureAttributes(); base_ta.setTextureMode(TextureAttributes.MODE_REPLACE);
For the second stage, the input texture is just a plain red square texture. The
idea is to take the plain texture of the first stage and change it's colour to
red. To do this, we use the TextureAttributes filter_ta = new TextureAttributes(); filter_ta.setTextureMode(TextureAttributes.MODE_MODULATE);
Now you must combine them all together. To do this, you'll need to create an
array to hold the two instances of TextureUnit[] tu = new TextureUnit[2]; tu[0] = new TextureUnit(); tu[1] = new TextureUnit(); tu[0].setTexture(base_texture); tu[1].setTexture(filter_texture); tu[0].setTextureAttributes(base_ta); tu[1].setTextureAttributes(filter_ta); And, once again, apply this array to the object's appearance: Appearance app = new Appearance(); app.setTextureUnits(tu, 2); Dealing with texture coordinatesAn important, yet frequently forgotten part of multitexturing is making sure that the textures are actually used correctly. When using single textures, you have to set the correct texture coordinates for the vertices of the geometry. As you may remember, if you don't set them, the default values are all zeros, meaning you end up with a single coloured object. Working with multitexture is no different. Each stage is a texture, which implies that for each stage you need to supply texture coordinates, otherwise the default values are used, and the results look really odd.
Specifying texture coordinates for multitextured geometry is exactly the same
as providing a single set of coordinates. You've probably noticed that the
float[][] tex_coord = { { 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1 } { 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1 } }; In some cases you want to have the same texture coordinates used for more than one of the stages. One option is to provide the same array twice. However, this is inefficient as you now pass the same data twice down the bus to the video card. An alternative way of accomplishing the same task is to use the texture set mapping ability of Aviatrix3D. What this does is allow you to only specify an array of data once, and tell the renderer that you'd like to replicate that data for more than one strip. To do this, you need to create another int array, where the values in the array point to the source texture coordinate array to use. For example, to use the same array for both the first and second texture units, use the following: int[] tex_maps = { 0, 0 }; geometry.setTextureSetMap(tex_maps, 2); Any ordering can be used, for example: int[] tex_maps = { 0, 1, 2, 0, 0 }; geometry.setTextureSetMap(tex_maps, 5); which uses 5 texture units, with the first three units using their own array, while units 3 and 4 share the first array. Finally, with all the pieces in place - textures, stage setups, and texture coordinates, you can run the application to get something that looks like this:
Using Texture CombinersNow that you have a basic understanding of the way Aviatrix3D texture units work, it's time to move onto the more interesting capabilities. One of the widest uses of multitexturing is to create effects that only use parts of the texture to change the output. For example, instead of using geometry to control the shape of a complex object, use a texture that describes the outline and use it to "cut out" the desired shape, while still leaving the original colour of the object. In simple terms, this means using one stage of the texture pipeline to only effect the alpha channel, while leaving the RGB values untouched. One of the most well-known uses of texture combiners is bump mapping, sometimes known as Dot-3 bump mapping. This gives the surface a look of being rough as it modulates the incoming light by a percieved surface normal that is defined by another texture.
To use texture combiners in Aviatrix3D, you start with the same setup as for
the previous example - you'll need a
As noted earlier, there are two methods on TextureAttributes base_ta = new TextureAttributes(); base_ta.setTextureMode(TextureAttributes.MODE_COMBINE); Now that is out of the way, you can start to set the combine modes that you would like to make use of. This tutorial won't go into the specifics of this though, as there are plenty of tutorials about what they are and how to use them. For example, to set up for a bump map stage you would use the following code (the blend colour value can be ignored if you like): TextureAttributes base_ta = new TextureAttributes(); base_ta.setBlendColor(0.5f, 0, 0, 0); base_ta.setTextureMode(TextureAttributes.MODE_COMBINE); base_ta.setCombineMode(false, TextureAttributes.COMBINE_DOT3_RGB); base_ta.setCombineMode(true, TextureAttributes.COMBINE_REPLACE); base_ta.setCombineSource(false, 0, TextureAttributes.SOURCE_CURRENT_TEXTURE); base_ta.setCombineSource(true, 0, TextureAttributes.SOURCE_CONSTANT_COLOR);
The first argument of the The code above gives you the basic black and white bump map, so now you'd like to put some colour back into it. To do that, just add an extra unit that combines the real colour with the basic shading that is the output from the first stage. To do that, you really don't need to use texture combiners, just the normal texture modes will do - in this case just a modulate to add the colour values in. TextureAttributes filter_ta = new TextureAttributes(); filter_ta.setTextureMode(TextureAttributes.MODE_MODULATE);
And finally the Texture2D base_texture = new Texture2D(); base_texture.setImages(Texture.MODE_BASE_LEVEL, Texture.FORMAT_RGB, base_img, 1); Texture2D filter_texture = new Texture2D(); filter_texture.setImages(Texture.MODE_BASE_LEVEL, Texture.FORMAT_RGB, filter_img, 1); TextureUnit[] tu = new TextureUnit[2]; tu[0] = new TextureUnit(); tu[0].setTexture(base_texture); tu[0].setTextureAttributes(base_ta); tu[1] = new TextureUnit(); tu[1].setTexture(filter_texture); tu[1].setTextureAttributes(filter_ta); Which leads to the final look like this:
|
[ Home ]
[ License ]
[ javadoc ]
[ Online Examples ]
[ Download ]
[ j3d.org ] [ Aviatrix3D ] [ Code Repository ] [ Java3D ] [ OpenGL ] [ Books ] [ Contact Us ] Last Updated: $Date: 2010-05-01 04:20:35 -0700 (Sat, 01 May 2010) $ |