Recently trying to get back into terrain rendering and I'm having a bit of trouble with improving my texturing and unsure of where I should focus my efforts. I've been reading a lot of white papers by DICE's Johan Andersson(the various techniques like PN Triangles, slope based blending, Wang tiles, etc), but I'm not sure where to start. Right now I have a 257 x 257 coherent noise generated heightfield and I'm using a simple 2D texture array to texture things based on height. The banding is pretty autrocious and the fact that my UVs are set like they are 1 texture stretched over a quad is causing all the resolution to be lost in my texture samples (at least that's my current theory...).

Would increased tessellation (i.e. via a hull/domain shader) help? Or what would you recommend I would get the biggest bang for my buck as I go forward with this system?

Pic of current banding and shader code below.

//-------------------------------------------------------------------------------------- // Constant Buffer Variables //-------------------------------------------------------------------------------------- cbuffer WVPConstantBuffer : register( b0 ) { matrix World; matrix View; matrix Projection; } cbuffer DebugLight : register ( b1 ) { float3 LightDir; } Texture2DArray txDiffuse : register( t0 ); Texture2D txNormal : register( t1 ); SamplerState samLinear : register( s0 ); //-------------------------------------------------------------------------------------- struct VS_INPUT { float4 Pos : POSITION; float2 Tex : TEXCOORD0; float3 Normal : NORMAL; float3 Tangent : TANGENT; }; struct VS_OUTPUT { float4 Pos : SV_POSITION; float3 Tex : TEXCOORD0; float3 Normal : NORMAL; float3 Tangent : TANGENT; }; //-------------------------------------------------------------------------------------- // Vertex Shader //-------------------------------------------------------------------------------------- VS_OUTPUT VS_Terrain(VS_INPUT input ) { VS_OUTPUT output = (VS_OUTPUT)0; output.Pos = mul( input.Pos, World ); output.Tex = float3(input.Tex, input.Pos.y);// Cache off the world height value. output.Pos = mul( output.Pos, View ); output.Pos = mul( output.Pos, Projection ); // Normal/Tan to World space. output.Normal = mul(float4(input.Normal, 0.0f) , World).xyz; output.Tangent = mul(float4(input.Tangent, 0.0f), World).xyz; return output; } float4 PS_Terrain( VS_OUTPUT input ) : SV_Target { float4 darkDirt = txDiffuse.Sample(samLinear, float3(input.Tex.xy, 1.0f)); float4 lightDirt = txDiffuse.Sample(samLinear, float3(input.Tex.xy, 2.0f)); float4 grass = txDiffuse.Sample(samLinear, float3(input.Tex.xy, 3.0f)); float4 rock = txDiffuse.Sample(samLinear, float3(input.Tex.xy, 4.0f)); float4 snow = txDiffuse.Sample(samLinear, float3(input.Tex.xy, 5.0f)); float4 color; float heightPercentage = input.Tex.z / 100.0f; // current max height allowed, need to move this to a constant buffer. float blend = 1.0f - input.Normal.y; // slope value between the layers. Closer to 90 degrees, we blend more of that texture. if(heightPercentage < 0.05f) { color = darkDirt; } else if (heightPercentage < 0.15f) { color = lerp(darkDirt, lightDirt, blend); } else if(heightPercentage < 0.4f) { color = lerp(lightDirt, grass, blend); } else if(heightPercentage < 0.7f) { color = lerp(grass, rock, blend); } else { color = lerp(rock, snow, blend); } // Normal mapping. float3 normalMapVal = txNormal.Sample(samLinear, input.Tex.xy).rgb; // From Texel to -1.0f -> 1.0f normalMapVal = 2.0f * normalMapVal - 1.0f; float3 N = input.Normal; float3 T = normalize(input.Tangent - dot(input.Tangent, N)*N).xyz; float3 B = cross(N, T).xyz; float3x3 TBN = float3x3(T, B, N); // Transform from tangent space to world space. float3 bumpedNormal = normalize(mul(normalMapVal, TBN)).xyz; color *= dot(bumpedNormal, -LightDir.xyz); color.a = 1.0f; return color; }