I'm stuck for my terrain rendering shader code for DX9. I'm looking to implement triplanar texturing, a texture atlas, and blending between different textures. Right now it's working. The problem is that it requires a total of 18 Tex2dLod calls and that is killing my frame rate. Also blending only works between textures which are next to each other in the texture map, if they are further apart it would go through all the different textures between them. Below is the relevant code from the pixel shader.

PSO PS(VSO input) 
PSO output; 
float4 texWeights = input.TexWeights;

int tile1 = clamp(floor(texWeights.x),0,1);
int tile2 = tile1 + 1;
float blendMultiTexture = max(0,texWeights.x - tile1); 

float3 blendWeights = abs( input.TBN[2].xyz );   // Tighten up the blending zone:  
blendWeights = (blendWeights - 0.2) * 7;  
blendWeights = max(blendWeights, 0);      // Force weights to sum to 1.0 (very important!)  
blendWeights /= (blendWeights.x + blendWeights.y + blendWeights.z ).xxx;
float2 coord1 = input.WSPosition.yz * TexScale;  
float2 coord2 = input.WSPosition.zx * TexScale;  
float2 coord3 = input.WSPosition.xy * TexScale;  

TileTexCoords texCoords;
float2 texSize = float2(480,480);
float MIP = min(GetMipLevel(coord1, texSize),4);
texCoords = GetTextureCoordinates_Gutter(coord1 ,tile1,tile2); 
float4 col1 = lerp(tex2Dlod(AlbedoSamplerX,float4(texCoords.UVA,0,MIP)),tex2Dlod(AlbedoSamplerX,float4(texCoords.UVB,0,MIP)),blendMultiTexture);    
float2 bumpFetch1 = lerp(tex2Dlod(NormalSamplerX,float4(texCoords.UVA,0,MIP)).xy,tex2Dlod(NormalSamplerX,float4(texCoords.UVB,0,MIP)).xy,blendMultiTexture) * 2.0f -1.0f;
float3 bump1 = float3(0, bumpFetch1.x , bumpFetch1.y);
float2 specFetch1 = lerp(tex2Dlod(SpecularSamplerX,float4(texCoords.UVA,0,MIP)).xy,tex2Dlod(SpecularSamplerX,float4(texCoords.UVB,0,MIP)).xy,blendMultiTexture);
float3 spec1 = float3(0, bumpFetch1.x , bumpFetch1.y);

texCoords = GetTextureCoordinates_Gutter(coord2 ,tile1,tile2); 
float4 col2 = lerp(tex2Dlod(AlbedoSamplerY,float4(texCoords.UVA,0,MIP)),tex2Dlod(AlbedoSamplerY,float4(texCoords.UVB,0,MIP)),blendMultiTexture);
float2 bumpFetch2 = lerp(tex2Dlod(NormalSamplerY,float4(texCoords.UVA,0,MIP)).xy,tex2Dlod(NormalSamplerY,float4(texCoords.UVB,0,MIP)).xy,blendMultiTexture) * 2.0f -1.0f;
float3 bump2 = float3(bumpFetch2.y , 0, bumpFetch2.x);
float2 specFetch2 = lerp(tex2Dlod(SpecularSamplerY,float4(texCoords.UVA,0,MIP)).xy,tex2Dlod(SpecularSamplerY,float4(texCoords.UVB,0,MIP)).xy,blendMultiTexture);
float3 spec2 = float3(specFetch2.y , 0, specFetch2.x);

texCoords = GetTextureCoordinates_Gutter(coord3 ,tile1,tile2); 
float4 col3 = lerp(tex2Dlod(AlbedoSamplerX,float4(texCoords.UVA,0,MIP)),tex2Dlod(AlbedoSamplerX,float4(texCoords.UVB,0,MIP)),blendMultiTexture);
float2 bumpFetch3 = lerp(tex2Dlod(NormalSamplerX,float4(texCoords.UVA,0,MIP)).xy,tex2Dlod(NormalSamplerX,float4(texCoords.UVB,0,MIP)).xy,blendMultiTexture) * 2.0f -1.0f;
float3 bump3 = float3(bumpFetch3.x, bumpFetch3.y, 0);
float2 specFetch3 = lerp(tex2Dlod(SpecularSamplerX,float4(texCoords.UVA,0,MIP)).xy,tex2Dlod(SpecularSamplerX,float4(texCoords.UVB,0,MIP)).xy,blendMultiTexture);
float3 spec3 = float3(specFetch3.x, specFetch3.y, 0);

Are there ways to reduce the number of tex2Dlod calls here while retaining all the features? Is there a way to cheat and only sample the textures twice instead of three times for triplanar texturing? Is there any way to get a non interpolated value from each of the surrounding vertexes so that any 2 textures can be blended together? Are there methods to approximate the specular map from the normal and diffuse maps so that I can do without the specular map sampling?


  • \$\begingroup\$ Your specular textures have 2 channels, it looks like? You could pack those into the alphas of the diffuse and normal maps. Also, why do you use tex2Dlod instead of tex2D? Why do you need to set the mip level yourself instead of letting the hardware select it? \$\endgroup\$ – Nathan Reed Mar 21 '13 at 20:42
  • \$\begingroup\$ The packing sounds like a great idea, thank you. I set the mip level manually because of the texture atlas. Without it black grid lines appear. I followed the solution from this article. \$\endgroup\$ – emf Mar 21 '13 at 21:04
  • \$\begingroup\$ Hmm, ok, that makes sense about the mip levels. I don't understand "blending only works between textures which are next to each other in the texture map", though; you're not blending UVs, surely? You're sampling the three textures then blending the sampled color/normal/specular values? \$\endgroup\$ – Nathan Reed Mar 21 '13 at 21:23
  • \$\begingroup\$ Right now each vertex has a terrain value. This terrain value gets interpolated when it gets passed to the pixel shader. The blended texture is based on this interpolated value. So for example if one vertex is 5 and another vertex is 2 the pixels in between would range from 2 to 5. A pixel with a value of 3.5 for example would be a 50/50 blend between texture type 3 and texture type 4. So there will be textures 3 and 4 between these 2 vertexes when ideally I only want textures 2 and 5 to be blended together. \$\endgroup\$ – emf Mar 21 '13 at 22:11
  • \$\begingroup\$ For the blending, you might want to have a system more like skinning, which has separate bone indices and blend weights per vertex. You'd have texture indices instead of bone indices, and they'd have to be the same for all three verts on each triangle. You'd have to duplicate verts some places on the mesh in order to switch which three textures were used from one triangle to the next, but this could be preprocessed. \$\endgroup\$ – Nathan Reed Mar 21 '13 at 22:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.