I am creating a geomip-mapped terrain. So far I have it working fairly well. The terrain tessellation near the camera is very high and gets less so the further out the geometry is. The geometry of the terrain essentially follows the camera and samples a heightmap texture based on the position of the vertices. Because the geometry tessellation is very high, you can at times see each pixel in the texture when its sampled. It creates obvious pixel bumps. I figured I might be able to get around this by smoothing the sampling of the heightmap. However I seem to have a weird problem related to some bilinear sampling code. I am rendering the terrain by displacing each vertex according to a heightmap texture. To get the height of a vertex at a given UV coordinate I can use:
vec2 worldToMapSpace( vec2 worldPosition ) {
return ( worldPosition / worldScale + 0.5 );
}
float getHeight( vec3 worldPosition )
{
#ifdef USE_HEIGHTFIELD
vec2 heightUv = worldToMapSpace(worldPosition.xz);
vec2 tHeightSize = vec2( HEIGHTFIELD_SIZE_WIDTH, HEIGHTFIELD_SIZE_HEIGHT ); //both 512
vec2 texel = vec2( 1.0 / tHeightSize );
//float coarseHeight = texture2DBilinear( heightfield, heightUv, texel, tHeightSize ).r;
float coarseHeight = texture2D( heightfield, vUv ).r;
return altitude * coarseHeight + heightOffset;
#else
return 0.0;
#endif
}
Which produces this (notice how you can see each pixel):

Here is a wireframe:

I wanted to make the terrain sampling smoother. So I figured I could use some bilinear sampling instead of the standard texture2D function. So here is my bilinear sampling function:
vec4 texture2DBilinear( sampler2D textureSampler, vec2 uv, vec2 texelSize, vec2 textureSize )
{
vec4 tl = texture2D(textureSampler, uv);
vec4 tr = texture2D(textureSampler, uv + vec2( texelSize.x, 0.0 ));
vec4 bl = texture2D(textureSampler, uv + vec2( 0.0, texelSize.y ));
vec4 br = texture2D(textureSampler, uv + vec2( texelSize.x, texelSize.y ));
vec2 f = fract( uv.xy * textureSize ); // get the decimal part
vec4 tA = mix( tl, tr, f.x );
vec4 tB = mix( bl, br, f.x );
return mix( tA, tB, f.y );
}
The texelSize is calculated as 1 / heightmap size:
vec2 texel = vec2( 1.0 / tHeightSize );
and textureSize is the width and height of the heightmap. However, when I use this function I get this result:
float coarseHeight = texture2DBilinear( heightfield, heightUv, texel, tHeightSize ).r;

That now seems worse :( Any ideas what I might be doing wrong? Or how I can get a smoother terrain sampling?
EDIT
Here is a vertical screenshot looking down at the terrain. You can see the layers work fine. Notice however that the outer layers that have less triangulation and look smoother while the ones with higher tessellation show each pixel. Im trying to find a way to smooth out the texture sampling.

