splatting pixel shader


15-12-2008 14:03:19


I have a question regarding the pixel shader used to blend multiple texture and coverage maps.

I have my own pixel shader code which I use to blend multiple textures and it looks something like this:

float4 a = tex2D(alpha, alphaUV);
float4 c1 = tex2D(texture1, colorUV);
float4 c2 = tex2D(texture2, colorUV);
float4 c3 = tex2D(texture3, colorUV);
float4 c4 = tex2D(texture4, colorUV);

float inverse = 1.0f / (a.r + a.g + a.b + a.a);

// Multiply with alpha texture
c1 *= a.b * inverse;
c2 *= a.g * inverse;
c3 *= a.r * inverse;
c4 *= a.a * inverse;

float4 texColor = (c1 + c2 + c3 + c4) * shade * l;

Basically I have the additional inverse computation which is used to clamp the color values.

Currently I have this pixel shader and its limited to only blending 4 textures, so I searched the net looking for a pixel shader that can handle more than 4 textures - and I saw the pixel shader code that comes with the sample source code:

float3 cov1 = tex2D(covMap1, iTexCoord0).rgb;
float3 cov2 = tex2D(covMap2, iTexCoord0).rgb;

iTexCoord0.x *= splatScaleX;
iTexCoord0.y *= splatScaleZ;

oColor = tex2D(splat1, iTexCoord0) * cov1.x
+ tex2D(splat2, iTexCoord0) * cov1.y
+ tex2D(splat3, iTexCoord0) * cov1.z
+ tex2D(splat4, iTexCoord0) * cov2.x
+ tex2D(splat5, iTexCoord0) * cov2.y
+ tex2D(splat6, iTexCoord0) * cov2.z;

I was wondering if I could get any explanation or explain the difference to me or how I can tweak my pixel shader code so that it can also handle up to 4 coverage maps to a total of 12 - 16 texture blends.



15-12-2008 16:44:29

Study the code more closely, you will see that it does the same as your code except that it does steps implicitly. I. e. you store results of each texture in a separate variable wheras the demo shader just uses the instructions directly in the final addition.

The one thing that the demo shader is missing is calculating the inverse. This is partly due to performance reasons, but mostly because it only works if you have a single pass. As soon as you get more than one pass, calculating the inverse in the shader is impossible and therefore you have to rely on the coverage maps to be scaled correctly - ETM takes care of that.

Apart from that, to use additional coverage maps and splatting textures, just add them like the ones already present. You need to be aware, though, that for a pixel shader level 2, the minimum number of supported texture units by all cards is 12.


16-12-2008 09:26:31


Thanks for your reply!

I'm fine with splatting 2 coverage maps. I modified my shader a bit, I was wondering if you can tell me if I did it just right.

float4 a= tex2D(alpha, alphaUV);
float4 a2 = tex2D(alpha2, alphaUV);
float4 c1 = tex2D(texture1, colorUV);
/* and so on */
float4 c8 = tex2D(texture8, colorUV);

float inverse = 2.0f / (a.r + a.g + a.b + a.a + a2.r + a2.g + a2.b + a.a );
c1 *= a.b * inverse;
/* and so on */
c8 *= a.a * inverse;

// Finally return the blended colors
return (c1 + c2 + c4 + c5 + c6 + c7 + c8)

Emphasis on the inverse's dividend value - had to put 2 so they all add up to 1 still.

Also another one, I was wondering why only the RGB values are being use and the alpha channel is being left out, is there a reason for this?

Many thanks.


16-12-2008 12:13:34

I believe the 2 is wrong - it still needs to be 1. The amount of all coverage channels must add to 1 at any location, and the inverse is therefore simply the inverse of the sum of all parts at that location. Multiplying by 2 makes no sense. Apart from that, can't spot a mistake.

As for the alpha channel, you can use it. In the ETM demo I use 3-channel textures which don't have the alpha channel - the advantage of that is that you can use a 3-channel map on a pixel shader 1 pass which has a texture limit of 4. Therefore you can create a fallback technique for older cards. If you don't need that, then there's no reason not to use the alpha channel.


17-12-2008 05:06:47

thanks so much.

you're right. things are brighter when the numerator is 2.