how to set up multitexturing?

Started by
50 comments, last by billybob 21 years, 9 months ago
i have this vertex: struct terrain_vertex{ FLOAT x, y, z; // The untransformed position for the vertex. FLOAT nx,ny,nz; FLOAT tu, tv; // The texture coordinates FLOAT weight; //the info for multitexturing the terrain, allows for 3 textures DWORD matrixIndices; FLOAT weight2; DWORD matrixIndices2; FLOAT weight3; DWORD matrixIndices3; }; and this fvf for it: #define TERRAINVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1|D3DFVF_XYZB3) now how do i use this setup? i got this far reading the dx documentation, but i can''t figure out how to use it. i realise it may be a larger than postable topic/tutorial, but does anyone know of a tutorial that teachs this?
Advertisement
Are you doing it with a shader or using texture stages? Even with a shader, you shouldn''t need that much information in your vstructure. If you''re planning to blend three textures with different texture coords, then all you need if your regular vertex structure but with three tex coords. No weights or matrix indices are needed. For multitexturing, I have no need for shaders so I base my code off of the multitexturing code found at www.Direct3D.net. Maybe you can find some use out of it.

---
My Site
Come join us on IRC in #directxdev @ irc.afternet.org
No, he has the blending stuff to specify how much of each texture to use at each vertex e.g 50% grass, 25%snow, 25% lava.
Leastwise, that''s what I figure from your earlier post. Let me know if you figure this out - I''m not getting anywhere!


Read about my game, project #1


John 3:16
What you seem to be trying to do in your code is indexed vertex blending. Unfortunately I think this is not what you actually want to do (weight-dependant texture stage blending). Indexed vertex blending modifies the vertex positions according to matrices you have set up.

In any case, the FVF you have specified is incompatible with the struct you have defined (I won''t explain now why unless you want me too ).

Right now I can only think of doing what you want to do is by using the specular register to pass on the blending factors for your textures to a vertex shader. The vertex shader can then pass this on to the pixel shader (it will have iterated it across the shape you are rendering as it would a diffuse colour). In the pixel shader you should then be able to use these factors to determine how much of each texture to use. The only thing about this is that you would have to be very careful to make sure that your three blending factors add up to 1.0f. Anything less and it would appear a bit transparent, anything more and you would see unexpected results as one of the textures would be masked out by the other textures.

Rory.
This topic shows the steps necessary to initialize and use a simple vertex shader that uses a position and multiple texture coordinates for multiple textures.

The first step is to declare the structures that holds the position and color as shown in the code example below.

struct XYZBuffer
{
float x, y, z;
};

struct Tex0Buffer
{
float tu, tv;
};

struct Tex1Buffer
{
float tu2, tv2;
};

The next step is to create a vertex shader declaration as shown in the code below.

DWORD decl[] =
{
D3DVSD_STREAM( 0 ),
D3DVSD_REG( D3DVSDE_POSITION, D3DVSDT_FLOAT3 ),
D3DVSD_STREAM( 1 ),
D3DVSD_REG( D3DVSDE_TEXCOORD0, D3DVSDT_FLOAT2 ),
D3DVSD_STREAM( 2 ),
D3DVSD_REG( D3DVSDE_TEXCOORD1, D3DVSDT_FLOAT2 ),
D3DVSD_END()
};

The next step is to call the IDirect3DDevice8::CreateVertexShader method to create the vertex shader.

g_d3dDevice->CreateVertexShader( decl, NULL, &vShader, 0);

Passing NULL to the second parameter of CreateVertexShader tells Direct3D that this vertex shader will use a fixed function pipeline.

After creating the vertex buffer and vertex shader, they are ready to use. The code example below shows how to set the vertex shader, set the stream source, and then draw a triangle list that uses the new vertex shader.

g_d3dDevice->SetVertexShader( vShader );
g_d3dDevice->SetStreamSource( 0, xyzbuf, sizeof(XYZBuffer) );
g_d3dDevice->SetStreamSource( 1, tex0buf, sizeof(Tex0Buffer) );
g_d3dDevice->SetStreamSource( 2, tex1buf, sizeof(Tex1Buffer) );
g_d3dDevice->SetIndices( pIB, 0 );
g_d3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, max - min + 1, 0, count / 3 );

what about this taken from the sdk documentation? it appears to do what i want, i think.
i am 99% sure i can do what i want with texturestages, i want each vertex to have a blend weight of 3 textures, and then draw them interpolating the blend weight across the triangles. i do not want to use shaders, as they aren''t supported on my friends card. i know its possible, i''ve seen it done in lots of games on my old voodoo3, obviously not using shaders. i''m pretty sure i would just do something like this:
pD3DDv->SetTexture(0, t_grass);
pD3DDv->SetTexture(1, t_rock);
pD3DDv->SetTexture(2, t_dirt);

i know i''m obviously missing a lot of code, but i only need some blend weights in the vertexes and some SetTextureStageStates, right?
quote:
i know i''m obviously missing a lot of code,

Absolutely...

quote:
but i only need some blend weights in the vertexes and some SetTextureStageStates, right?


No... You don''t need blend weights in the vertices... or if you insist, place it in the alpha component of the vertex diffuse colour and do a multipass render (not multitexture in single pass!).

What you are talking about is vertex based blending or alpha interpolation on vertices!!! You can achieve this only by using the alpha component of the diffuse colour in the vertex (you mentioned you don''t want to use shaders!).
Textures are set for the entire triangle and cannot have different properties for each vertex (unless ofcourse if u use a shader)...

what you need to do is to understand the SetTextureStageState thoroughly...you may be able to use these in some places...

There is no problem so complicated that it cannot be complicated further.
Yeah. That sounds right. I'm just not sure how you can set some kind of blending using SetTextureStageState and the vertex information.

OK. Having read the docs, I think I see what you need to be doing. Unfortunately I'm still not totally sure it can be done for three textures in one pass. Two textures in one pass is simple, but three is much harder.

Basically you will be needing to use the lerp texture operation for the alpha op in your 2nd and third stages. ie

SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_LERP);

This uses the factor that you send in to D3DTSS_COLORARG0 to determine how to blend the stages. The only problem is that you will need to find a way to set this value to something useful. It can only be things like the vertex's diffuse colour, or the specular colour. So basically you'll need to specify the blending factors by putting them in the diffuse and specular colours I think.

For texture stage 0, you just set the texture straight away to 100% your first texture (eg grass). Then for texture stage 1 (your snow), you use LERP as the alpha operation, and specify D3DTA_DIFFUSE as your D3DTSS_COLORARG0. So you use the diffuse colour to determine the blending factor.

So for a vertex who's (pretend) colour is white, it will use all of the grass texture, if it is black, it will use all snow texture, and if it is grey, it will blend between them.

So finally for your texture stage 2 (rock or whatever), use LERP again as the operation, but this time use D3DTA_SPECULAR as the D3DTSS_COLORARG0. Now you use your vertex specular in the same way to blend between the grassy-snowy texture and the rock.

Here's a pseudo-code snippet (off the top of my head so might not be perfect).


    IDirect3DDevice8* pDevice;// Set the textures for each stagepDevice->SetTexture(0, grass);pDevice->SetTexture(1, snow);pDevice->SetTexture(2, rock);// Stages. Leave the ColorArg1 and 2 as defaults. This uses// the current stage's texture as colorarg1, and the previous// stage's output as colorarg2// Stage 0. Just output the grass texturepDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);// Stage 1. Use the vertex diffuse colour to determine the blendpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_LERP);pDevice->SetTextureStateState(1, D3DTSS_COLORARG0, D3DTA_DIFFUSE);// Stage 2. Use vertex specular colour to determine the blendpDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_LERP);pDevice->SetTextureStateState(2, D3DTSS_COLORARG0, D3DTA_SPECULAR);    


You might want to see what exactly the D3DTA_TEMP flag does (in place of D3DTA_SPECULAR and D3DTA_DIFFUSE). That looks like it might help you out, but the docs are very sparse.

I'm sure you might get some funny effects depending on you settings, but to get (roughly) a mix of all three textures you would set the vertex diffuse colour to (0.5, 0.5, 0.5, 0.5) and the specular colour to (0.3, 0.3, 0.3, 0.3) or something like that. I guess you would have to play around with it for a while. One coll thing about this system is that you can blend the colours separately. So if you want to make the rock appear blue, just use (I am using ARGB format) (0.0, 0.0, 0.0, 0.3)

I think this is enough to get you going for a bit. I am pretty sure this is a step in the right direction. You will probably be able to modify this to be a lot better with experience (as I have almost none with the fixed function pipeline!)

Rory.


[edited by - protopornopants on July 26, 2002 9:47:53 AM]
Yeah, looked in the SDK docas, and the blending stuff is for matrices. Think we''re stuck with single passes or

TextureState1:
Texture=Grass.
ColourArg=Texture.
AlphaArg=constant (set to be completely solid)

TextureState2:
Texture=Rock.
ColourArg=Texture.
AlphaArg=VertexDiffuseColour

TextureState3:
Texture=Snow.
ColourArg=Texture.
AlphaArg=VertexSpecularColour

This does what you want I think, and CAN be done in a single pass. Sorry about lack of proper code, you know where to look
You can also still use the colour components of diffuse and specular for lighting or whatever I think if you''re careful not to screw with the alpha.

Does result in a large vertex FVF (3 texcoord sets, diffuse & specular), but if you build vertices dynamically this isn''t such a problem. I think you want the first texture to be solid, otherwise it''ll blend with the background colour you clear the backbuffer to.

What does anyone think to this method? Ok? Completely flawed?


Read about my game, project #1


John 3:16
Hmm well I''ve looked at single-pass multitexturing and sure you can do it, but on which graphics cards - thats the question.
Geforce 2 MX etc will only do 2 textures at once, so you would need a multipass backup there.
Also different graphics cards have different limitations of what texturestagestates can be used at each level, so you wanna check that too.
I wanted to blend 2 textures and a vertex color in 1 pass and the extra color info just made in impossible - a 2 pass render made it simpler and cleaner.
The vertex shader solution though looks cool - anyone used this?
A

This topic is closed to new replies.

Advertisement