So I have a heightmap for my terrain. The heighmap is a texture, and I have tessellation control and evaulation shaders in glsl (using OpenGL 4.0 atm) that tessellate them into smaller squares. I've tried to write code that looks at differences in neighboring pixels in the heightmap to determine the normals at a given point. The result of this is the attached image. It looks like it more or less works, but there's undesirable artifacts. I've drawn a black circle around one such artifact in the close up screenshot to the right.
Here's my tessellation evaluation shader that computes vertex positions and normals:
#version 400 core
uniform sampler2D texture0;
uniform sampler2D texture1;
uniform mat4 cameraProjectionMatrix;
uniform mat4 cameraMatrix;
uniform vec3 cameraPosition;
uniform vec3 cameraRight;
uniform vec3 cameraUp;
uniform vec3 cameraDirection;
layout(quads, equal_spacing, ccw) in;
in vec3 ES_Position[];
out vec2 UV;
out vec3 FS_Position;
out vec3 lightingNormal;
vec3 getNormal(float cellSize,float totalPixels,vec2 uvCoords)
{
float heightL = texture(texture0,uvCoords - vec2(1.0/totalPixels,0)).r*10.0;
float heightR = texture(texture0,uvCoords + vec2(1.0/totalPixels,0)).r*10.0;
float heightD = texture(texture0,uvCoords - vec2(0,1.0/totalPixels)).r*10.0;
float heightU = texture(texture0,uvCoords + vec2(0,1.0/totalPixels)).r*10.0;
vec3 tangent = normalize(vec3(cellSize,heightL - heightR,0));
vec3 bitangent = normalize(vec3(0,heightD - heightU,cellSize));
return normalize(cross(tangent,bitangent)) * vec3(1,-1,1);
}
void main()
{
float totalMapSize = 400.0;
float totalPixels = 2048.0;
float cellSize = totalMapSize/totalPixels;
vec3 highXPos = mix(ES_Position[3],ES_Position[2],gl_TessCoord.y);
vec3 lowXPos = mix(ES_Position[0],ES_Position[1],gl_TessCoord.y);
FS_Position = mix(lowXPos,highXPos,gl_TessCoord.x);
vec2 uvCoords = FS_Position.xz/totalMapSize;
float actualHeight = texture(texture0,uvCoords).r*10.0;
FS_Position.y = actualHeight;
gl_Position = cameraProjectionMatrix * cameraMatrix * vec4(FS_Position,1.0);
UV = gl_TessCoord.xy * vec2(0.33,1);
vec3 norm = getNormal(cellSize,totalPixels,uvCoords);
//vec3 normL = getNormal(cellSize,totalPixels,uvCoords - vec2(1.0/totalPixels,0));
//vec3 normR = getNormal(cellSize,totalPixels,uvCoords + vec2(1.0/totalPixels,0));
//vec3 normD = getNormal(cellSize,totalPixels,uvCoords - vec2(0,1.0/totalPixels));
//vec3 normU = getNormal(cellSize,totalPixels,uvCoords + vec2(0,1.0/totalPixels));
//vec3 normX = mix(normL,normR,0.5);
//vec3 normY = mix(normD,normU,0.5);
//vec3 normAround = mix(normX,normY,0.5);
//lightingNormal = mix(norm,normAround,0.5);
lightingNormal = norm;
}
Trying to approximate what pixel it is on and finding the supposedly neighboring pixels using floating point math seems hacky, I feel like there's a better way to do this that will eliminate those artifacts and make it look a lot more smooth.