Sign in to follow this  
all_names_taken

blending 4 textures with shaders

Recommended Posts

I'm trying to make a 4-texture blending shader. So far I have the below code, which unfortunately gives the same blending factor for all vertices and all textures. I'd like to somehow pass the desired blending factor for each texture on a per-vertex basis and do a different blending for each vertex. 1. how do I pass the data to the shader? I don't want to use vertex constants - because these alpha values will change from vertex to vertex using constants would require too many DrawPrimitive calls, so I guess it must be in the vertex buffer. One idea I had was to use a single color DWORD, and somehow in the shader extract and use the r as blending factor for texture1, the g as factor for texture2, etc. Can I do that? If not, how do I do it instead (do I have to pass 4 color DWORDs for each vertex in the vertex buffer)? 2. how do I make it so the shader uses the values to blend the textures properly after having passed the data to the shader? 3. how do I make it so the rendering is done in a single pass instead of 4? Here's an attempt at effect file (which doesn't take into account different blending for different vertices, and unfortunately still uses 4 passes instead of one):
texture tex1;
texture tex2;
texture tex3;
texture tex4;

// Our effect technique...
technique BlendFour
{
	pass Pass0
	{
		AlphaBlendEnable = False;
		Texture[0] = <tex1>;
		ColorOp[0]   = SelectArg1;
		ColorArg1[0] = Texture;
		ColorOp[1]   = Disable;
	}
	pass Pass1
	{
		AlphaBlendEnable = True;
		SrcBlend  = SrcColor;
		DestBlend = DestColor;
		Texture[0] = <tex2>;
		ColorOp[0]   = SelectArg1;
		ColorArg1[0] = Texture;
		ColorOp[1]   = Disable;
	}
	pass Pass2
	{
		AlphaBlendEnable = True;
		SrcBlend  = SrcColor;
		DestBlend = DestColor;
		Texture[0] = <tex3>;
		ColorOp[0]   = SelectArg1;
		ColorArg1[0] = Texture;
		ColorOp[1]   = Disable;
	}
	pass Pass3
	{
		AlphaBlendEnable = True;
		SrcBlend  = SrcColor;
		DestBlend = DestColor;
		Texture[0] = <tex4>;
		ColorOp[0]   = SelectArg1;
		ColorArg1[0] = Texture;
		ColorOp[1]   = Disable;
	}
}








Some code from the top of the .cpp file:
struct VertexStruct
{
   D3DXVECTOR3 position;
   DWORD color;
   D3DXVECTOR2 texcoord;
};
#define D3DFVF_D3DVertexStruct (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)






And here's the render code from the .cpp file (so far it only renders a hardcoded test quad):
	shader->SetTechnique("BlendFour");
	shader->SetTexture("tex1", Texture21);
	shader->SetTexture("tex2", Texture22);
	shader->SetTexture("tex3", Texture23);
	shader->SetTexture("tex4", Texture24);

	UINT totalPasses;
	shader->Begin(&totalPasses, 0);
	for(UINT pass = 0; pass < totalPasses; pass++)
	{
		shader->BeginPass(pass);
		dxDevice->SetStreamSource(0, vbuffer, 0, sizeof(VertexStruct));

		// Set the vertex stream declaration.
		dxDevice->SetFVF(D3DFVF_D3DVertexStruct);

		// This will draw everything in the buffer.
		dxDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

		shader->EndPass();
	}
	shader->End();







[Edited by - all_names_taken on June 6, 2006 5:22:23 AM]

Share this post


Link to post
Share on other sites
Well, using a shader should help. Something like:

return tex2D(Samp1, tex) * blend.x + tex2D(Samp2, tex) * blend.y ...

where 'blend' is an input to the shader that may be in the colour channel.

Share this post


Link to post
Share on other sites

You can pass the per vertex color to the pixel shader and the pixel shader can interprete the incoming data as it wants. So if you have the 4 component color value, the pixel shader could use the components as it desires (ie. blend 4 textures together). So 1 dword per vertex is enough for blending values.

It sounds like that you don't have too much experience with shaders. I'd suggest you to study a little DX SDK samples. Just to get the basics. Shaders are pretty simple to start with. Especially if you use effect files.


To blend 4 textures in a pixel shader you can do something like:

float4 color = tex1 * in.color.r + tex2 * in.color.b + ...

Yes, all in one pass if your hardware supports it.

Share this post


Link to post
Share on other sites
Quote:
Original post by Demus79

You can pass the per vertex color to the pixel shader and the pixel shader can interprete the incoming data as it wants. So if you have the 4 component color value, the pixel shader could use the components as it desires (ie. blend 4 textures together). So 1 dword per vertex is enough for blending values.

It sounds like that you don't have too much experience with shaders. I'd suggest you to study a little DX SDK samples. Just to get the basics. Shaders are pretty simple to start with. Especially if you use effect files.


To blend 4 textures in a pixel shader you can do something like:

float4 color = tex1 * in.color.r + tex2 * in.color.b + ...

Yes, all in one pass if your hardware supports it.


Thank you, that answers all my questions I think. I made a new version now, with the key line being the following in the pixel shader:
ps_out.color = tex2D(Sampler1, IN.texture0) * IN.color.r
+ tex2D(Sampler2, IN.texture0) * IN.color.g
+ tex2D(Sampler3, IN.texture0) * IN.color.b
+ tex2D(Sampler4, IN.texture0) * IN.color.a;

The vertex shader does nothing apart from emulating the fixed function pipeline functionality, while the pixel shader takes the values from 4 different textures and blends them... I could compile it with shader 2.0 (but not with 1.1) but can't test run it until I get to a computer that has shader 2.0 :)

This is the entire code of the new effects file:

float4x4 worldViewProj : WorldViewProjection;

texture tex1;
texture tex2;
texture tex3;
texture tex4;

sampler Sampler1 = sampler_state
{
Texture = (tex1);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
sampler Sampler2 = sampler_state
{
Texture = (tex2);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
sampler Sampler3 = sampler_state
{
Texture = (tex3);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
sampler Sampler4 = sampler_state
{
Texture = (tex4);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};

struct App2Vs
{
float3 vertexPos : POSITION;
float4 color : COLOR;
float2 texture0 : TEXCOORD0;
};

struct Vs2Ps
{
float4 vertexPos : POSITION;
float2 texture0 : TEXCOORD0;
float4 color : COLOR0;
};

struct Ps2Screen
{
float4 color : COLOR;
};

Vs2Ps VertexShaderEffect(App2Vs IN)
{
Vs2Ps vs_out;
vs_out.vertexPos = mul(worldViewProj, float4(IN.vertexPos, 1));
vs_out.color = IN.color;
vs_out.texture0 = IN.texture0;

return vs_out;
}

Ps2Screen PixelShaderEffect(Vs2Ps IN)
{
Ps2Screen ps_out;

ps_out.color = tex2D(Sampler1, IN.texture0) * IN.color.r
+ tex2D(Sampler2, IN.texture0) * IN.color.g
+ tex2D(Sampler3, IN.texture0) * IN.color.b
+ tex2D(Sampler4, IN.texture0) * IN.color.a;

return ps_out;
}

technique BlendFour
{
pass Pass0
{
Lighting = FALSE;
VertexShader = compile vs_1_1 VertexShaderEffect();
PixelShader = compile ps_1_1 PixelShaderEffect();
}
}









One thing I'm wondering about which doesn't depend on my testing, is if I can replace the sampler structures by something simpler. Can I define MIN, MAG and MIP filter in one place (I'll most likely use same filtering for all textures during blending, after all) and just pass different textures each time?

[Edited by - all_names_taken on June 6, 2006 9:59:23 AM]

Share this post


Link to post
Share on other sites
In pixel shader 1.1 you can't sample different textures with the same coordinate set. You need to use IN.texture0, IN.texture1, etc. IIRC D3DTSS_TEXCOORDINDEX should work for that, so you shouldn't need to physically duplicate the texture coords in the vertex.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this