Skip to main content

Multi-texturing? Texture blending? Per-vertex textures? What am I doing?

12 replies [Last post]
morne
Offline
Joined: 2008-03-22

Please don't laugh at the noob :

I can blend per-vertex colours across a triangle, with the resulting colour blending smoothly between one dominant colour and the others.

Can I do the same with textures?

If not, how best to implement multiple textures on a single triangle, where each vertex is dominated by a different texture? Yes, it's the old grass-rock problem. Is this doable within a TriangleStripArray? Will I have to (as I suspect) do some sort of subdivision into smaller triangles, each wholly covered by a different texture? Will J3D at least blend the boundaries for me?

Thanks for any wise words,

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
lamer77
Offline
Joined: 2006-12-22

This is not possible in one pass using the fixed function pipeline. The d3d and opengl examples I've seen uses multipass rendering to do this. One pass for each splat. You can fake multipass rendering in Java3D using an OrderedGroup.

...or you could use a shader.

This seems like a good article on texture splatting, wich I'm sure you've already read :)
http://www.gamedev.net/columns/hardcore/splatting/page3.asp

morne
Offline
Joined: 2008-03-22

Since I appear to be hitting a wall, I've tried to solve the problem from another approach.

I'm now building 'bridging' textures that will sit between any four different terrain types and blend between them. They only have to be small since they cover just 1 square cell, but the downside is that, in order to tile with the larger terrain textures, they have to be created for each individual terrain junction.

Thanks to both aces and lamer77 for their help, though.

morne
Offline
Joined: 2008-03-22

Right. Moving from triangles to squares, this is what I think I need to do...

(Mask is a RGBA image. One corner 'radiates' red which fades according to distance from that corner. The other 3 corners are G, B and A).

Take texture T1. Set T1's alpha to mask's R. Replace the geometry texture with the result.

Take texture T2. Set T2's alpha to mask's G. Modulate the geometry's texture with the result.

etc for the third and fourth textures, and the mask's B and A channels.

I just can't find any combination or sequence of TextureAttributes.COMBINE_* options that will allow me to do this. There are almost no examples online, and
the documentation is a little lacking, frankly.

Is what I'm trying to do even possible? Or should I just overlay partially-transparent triangles to get the same effect?

lamer77
Offline
Joined: 2006-12-22

google for "texture splatting opengl". There might be few Java3D examples, but there should be alot of opengl examples. The TextureAttribute modes are based on opengl and converting opengl code to Java3D should be possible.

You should also read up on the Opengl spec:
http://www.opengl.org/sdk/docs/man/

You are looking for the glTexEnv function.

You could also use Shaders, if that is an option.

morne
Offline
Joined: 2008-03-22

> google for "texture splatting opengl". There might be
> few Java3D examples, but there should be alot of
> opengl examples. The TextureAttribute modes are based
> on opengl and converting opengl code to Java3D should
> be possible.

This is almost exactly what I need to do (splatting appears to be 1 masked texture upon a base texture, whereas I need 3 masked textures, each atop the previous), but as I mentioned earlier I'm now running short of... registers I guess.

This is the outline of what I want to do :

1) Make a triangle. It has no colour or texture yet.
2) Take 1 texture, and 1 alpha mask.
3) Combine them.
4) Write them to the triangle.
5) Take another texture, and another alpha mask.

This is where things break down. Unless I'm missing something, I can only specify

COMBINE_OBJECT_COLOR, COMBINE_TEXTURE_COLOR, COMBINE_CONSTANT_COLOR, or COMBINE_PREVIOUS_TEXTURE_UNIT_STATE

TEXTURE_COLOR I guess is the texture that exists on the geometry
OBJECT_COLOR would be the geometry's base colour
CONSTANT_COLOR would be a colour that I can specify

that leaves only COMBINE_PREVIOUS_TEXTURE_UNIT_STATE...

Ack, how can I put this? I think the pipeline I want is something like this :

Mask 1
Texture 1 : Combine with Mask 1, write to triangle
Mask 2 : Do nothing with this, just put it in the pipeline
Texture 2 : Combine with Mask 2 (using COMBINE_PREVIOUS_TEXTURE_UNIT_STATE), and then write to triangle.
Mask 3 : etc.
Texture 3 : etc.

> You should also read up on the Opengl spec:
> http://www.opengl.org/sdk/docs/man/

Their website says 'XSLT processing failed!'!

> You could also use Shaders, if that is an option.

Hmm. More to learn, more to get wrong. I'd prefer to exhaust the texture-layering possibilities first.

aces
Offline
Joined: 2003-07-17

>1) Make a triangle. It has no color or texture yet.
You can set some base color, as Object color can be used on blending.

>2) Take 1 texture, and 1 alpha mask.
>3) Combine them.

Did you consider using an ARGB texture ? You can join RGB and alpha mask in a single texture, in a trivial shift and logical AND operation.

[code]
// merge rgb and alpha values in a single argb pixel
argb = ( alpha & 0xFF) << 24) | (rgb & 0x00FFFFFF );
[/code]

BTW, some (old) video cards support just 2 or 4 texture units. You can cheat then by blending the textures on CPU, instead on GPU.

morne
Offline
Joined: 2008-03-22

> Did you consider using an ARGB texture ? You can join
> RGB and alpha mask in a single texture, in a trivial
> shift and logical AND operation.
>
> [code]
> // merge rgb and alpha values in a single argb pixel
> argb = ( alpha & 0xFF) << 24) | (rgb & 0x00FFFFFF
> );
> [/code]

Yep, but the alpha mask can be 1 of four possibilities, depending on which corner I need to fade. I'd prefer to offload the work onto the GPU because there more spare grunt there.

> BTW, some (old) video cards support just 2 or 4
> texture units. You can cheat then by blending the
> textures on CPU, instead on GPU.

I think I can still squeeze the whole process into 4 units : a 4-channel alpha mask, and 3 textures. Either I'm approaching this problem from the wrong direction, or the pipeline is flexible enough for my needs. I suspect the former.

Anyway, fickle as I am, I've changed approach...

morne
Offline
Joined: 2008-03-22

Word of the day is 'splatting'.

The search continues...

aces
Offline
Joined: 2003-07-17

You can use a single geometry, of any type.

* Load your textures;
* Define a TextureAttributes for each loaded texture. This will define the blending mode, among other things;
* Create a UnitTextureState for each texture;
* Put all UnitTextureState in a array ;
* Create an Appearance instance and set above array using setTextureUnitState(array) ;

See:
Java3D example MultiTextureTest

javadocs
http://www.jdocs.com/java3d/1.5.0/javax/media/j3d/TextureUnitState.html
http://www.jdocs.com/java3d/1.5.0/javax/media/j3d/TextureAttributes.html

morne
Offline
Joined: 2008-03-22

> You can use a single geometry, of any type.
>
> * Load your textures;
> * Define a TextureAttributes for each loaded texture.
> This will define the blending mode, among other
> things;
> * Create a UnitTextureState for each texture;
> * Put all UnitTextureState in a array ;
> * Create an Appearance instance and set above array
> using setTextureUnitState(array) ;
>
> See:
> Java3D example MultiTextureTest
>
> javadocs
> http://www.jdocs.com/java3d/1.5.0/javax/media/j3d/Text
> ureUnitState.html
> http://www.jdocs.com/java3d/1.5.0/javax/media/j3d/Text
> ureAttributes.html

I think this is what I'm currently doing. I get all the textures merged together all over the geometry. How do I tell one texture to be dominant in one corner, and another in the next corner, etc?

Here's something I hacked up; excuse the variable names :

[code]
TriangleStripArray moo = new TriangleStripArray(
4,
GeometryArray.COORDINATES |GeometryArray.TEXTURE_COORDINATE_2,
2, //texcoordcount
new int[] {0, 1}, //texcoordsetmap
new int[] {4}
);

moo.setCoordinate(0, new Point3d(0, 0, 0));
moo.setCoordinate(1, new Point3d(100, 0, 0));
moo.setCoordinate(2, new Point3d(0, 100,0));
moo.setCoordinate(3, new Point3d(100, 100,0));

moo.setTextureCoordinate(0, 0, new TexCoord2f(0, 0));
moo.setTextureCoordinate(0, 1, new TexCoord2f(1, 0));
moo.setTextureCoordinate(0, 2, new TexCoord2f(0, 1));
moo.setTextureCoordinate(0, 3, new TexCoord2f(1, 1));

moo.setTextureCoordinate(1, 0, new TexCoord2f(0, 0));
moo.setTextureCoordinate(1, 1, new TexCoord2f(1, 0));
moo.setTextureCoordinate(1, 2, new TexCoord2f(0, 1));
moo.setTextureCoordinate(1, 3, new TexCoord2f(1, 1));

Shape3D mooShape = new Shape3D();
mooShape.setGeometry(moo);

TextureAttributes ta = new TextureAttributes();
ta.setTextureMode(TextureAttributes.MODULATE);

TextureUnitState tusGrass = new TextureUnitState((new TextureLoader(grassTexture)).getTexture(), ta, null);
TextureUnitState tusWater = new TextureUnitState((new TextureLoader(waterTexture)).getTexture(), ta, null);

Appearance ap = new Appearance();
//ap.setColoringAttributes(new ColoringAttributes(0.0f, 1.0f, 0.0f, ColoringAttributes.SHADE_GOURAUD));
ap.setTextureUnitState(new TextureUnitState[] {tusGrass, tusWater});

mooShape.setAppearance(ap);
[/code]

I don't see anywhere to weight the importance or strength of one particular texture.

Another clue please?

aces
Offline
Joined: 2003-07-17

It´s hard to understand what you are trying to do, so I´ll give tips about multi texture itself.

Try to create different TextureAttributes, one for each texture, and set correct blending mode for *each* one . You can define the blending sources as COMBINE_PREVIOUS_TEXTURE_UNIT_STATE and COMBINE_OBJECT_COLOR.
You can weight colors and alpha by setting combine rgb factor an combine scale factor .

Take a look at TextureAttributes javadocs

btw, if you are going to use the same texture coordinates for all textures, there is no need to create new texture maps, neither redefine texture coordinates.

morne
Offline
Joined: 2008-03-22

> It´s hard to understand what you are trying to do

I'm sure you /know/ what I mean, but I just can't explain it correctly. Let's have another go.

Imagine a planar triangle, as part of a terrain.

If each vertex of the triangle represents a different type of terrain (for example, grass, rock and sand), the textures will blend into each other across the face of the triangle.

> Try to create different TextureAttributes, one for
> each texture, and set correct blending mode for
> *each* one . You can define the blending sources as
> COMBINE_PREVIOUS_TEXTURE_UNIT_STATE and
> COMBINE_OBJECT_COLOR.
> You can weight colors and alpha by setting combine
> rgb factor an combine scale factor .

I'm half way there. I've now modulated a texture's alpha map from a mask.

The problem now is that I don't have enough 'hands'. I can access the texture already on the geometry, and I can access the incoming texture, but I'd really like another 'hand' to hold intermediate results.

Argh.

> Take a look at TextureAttributes javadocs

Hah.

> btw, if you are going to use the same texture
> coordinates for all textures, there is no need to
> create new texture maps, neither redefine texture
> coordinates.

Yeah, I realize this now. I was just hackin' around.