• Advertisement
Sign in to follow this  

Bizarre Terrain Glitch

This topic is 1758 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a planet terrain shader that perturbs vertex positions by using a 3D fractal noise lookup on a sphere as the heightmap. It works perfectly 99.9% of the time except for these bizarre glitch vertices that occasionally appear. It looks like a spike in the middle of the otherwise normal mesh.


I've narrowed it down to the trilinear texture lookups into the noise. The glitch always only affects a single vertex, smaller than a texel in size. It occurs consistently for the same position lookup in the texture


Annoyingly, my fractal lookup is pretty basic and after looking over this code and trying dozens of things I remain stumped to the cause. I suspect the problem must be some sort of glitch in the vertex shader trilinear filtering; I see few other possibilities at this point. Anyway, here's some pics of the glitch and the relevant code for the vertex shader (I omit the pixel shader for brevity).


#define textureSize 80.0
#define halfTexelSize 0.00625
#define texelSize 0.0125

texture NoiseRealVertex;
sampler3D NoiseRealVertexSampler = sampler_state {
    Texture = <NoiseRealVertex>;
	AddressU = WRAP;
	AddressV = WRAP;
	AddressW = WRAP;
	MinFilter = Point;
	MagFilter = Point;
	MipFilter = Point;

// Vertex Noise Functions
float4 tex3D_trilinear(float3 t) 
	t -= halfTexelSize.xxx;
	float3 f = frac(t * textureSize);
	float4 x = float4(t, 0);

	float4 t000 = tex3Dlod(NoiseRealVertexSampler, x);
	float4 t100 = tex3Dlod(NoiseRealVertexSampler, x + float4(texelSize, 0, 0, 0)); 
	float4 t010 = tex3Dlod(NoiseRealVertexSampler, x + float4(0, texelSize, 0, 0)); 
	float4 t110 = tex3Dlod(NoiseRealVertexSampler, x + float4(texelSize, texelSize, 0, 0)); 
	float4 t001 = tex3Dlod(NoiseRealVertexSampler, x + float4(0, 0, texelSize, 0)); 
	float4 t101 = tex3Dlod(NoiseRealVertexSampler, x + float4(texelSize, 0, texelSize, 0)); 
	float4 t011 = tex3Dlod(NoiseRealVertexSampler, x + float4(0, texelSize, texelSize, 0)); 
	float4 t111 = tex3Dlod(NoiseRealVertexSampler, x + float4(texelSize, texelSize, texelSize, 0)); 

	return lerp(
			lerp(t000, t100, f.x), 
			lerp(t010, t110, f.x),
			lerp(t001, t101, f.x), 
			lerp(t011, t111, f.x),
	, f.z);
Edited by jsuffolk

Share this post

Link to post
Share on other sites

Your code is basically requesting 8 corners of a cube, and is calculating trilinear interpolation values between them. Where I think it might be going wrong is that you're calculating the interpolation values in the HLSL, but you're leaving the point sampling to the texture fetch instruction. I suspect there might be some subtle floating point rounding differences that mean that occasionally you're using interpolation values for one cube, but texture samples from another.


I think the solution would be to provide the texture fetch instruction with unambiguous coordinates to eliminate the possibility of a rounding error. Some pseudocode which could replace the first 3 lines of your tex3D_trilinear function:


// t = frac(t); // This line might help if magnitude of t is very large.
float3 topCorner = (t - halfTexelSize.xxx) * textureSize;
float3 topCornerFloor = floor(topCorner);
float3 f = topCorner - topCornerFloor;
float3 topCornerUV = topCornerFloor / textureSize;
float3 topCornerUV += halfTexelSize.xxx;  // This half texel offset means you're attempting to sample the centre of the texel instead of the top-left corner. I think it helps, but if it doesn't, then try without it, or try a 1/4 or 1/3 offset.
float4 x = float4(topCornerUV, 0);

Share this post

Link to post
Share on other sites

Worked like a charm!!! Thanks, that was a lifesaver after pulling my hair out for a few days! Next up: mipmapping the surface noise on the GPU and switching the surface from normal mapping to relief mapping!


Just in case you're curious the algorithm basically uses this GPU gems piece on a planetary scale in a manner similar to CDLOD. It works extremely well for planetary LOD with just 2 passes (1 CPU, 1 GPU), able to push > 3 million tris on a couple year old GPU with no framerate dips and can be adjusted for older hardware by changing the value of a single constant. Morphing's a little tricky, though.



Edited by jsuffolk

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement