I am trying to do some heightmapping with XNA. Basically I'm making a grid of verticies and then linking them together with indicies to form triangles. I then read the red values from all the pixels in a JPG or PNG, divide by 255, multiply by a scale factor and then set each of the 'y' value in the verticies in the grid to that value...giving a rudimentary heightmap. My problem is that the geometry does not look quite right in the render. Also if there is a better way to generate terrain I would like to hear it...because I can't go beyond a 512x512 heightmap without exceeding XNA's max triangle count. I know terrain generation with heightmaps is an ancient problem...and I'm hoping there is a better way to do what I am trying to do. Any ideas on how to make this work better (at least from a geometry standpoint).
The code I use to generate the triangle grid and read the heightmap image:
private float[] getVertexYFromImage(Texture2D displacementmap, int scalefactor)
{
Color[] pixels = new Color[displacementmap.Width * displacementmap.Height];
displacementmap.GetData<Color>(pixels);
float[] vertexy = new float[pixels.Length];
for (int cursor = 0; cursor < pixels.Length; cursor++)
{
//use red channel to get vert height (y)
vertexy[cursor] = ((float)pixels[cursor].R / 255.0f) * scalefactor;
}
return vertexy;
}
private void getTerrainVertexIndexData(int size, int spread, out int[] indexData, out VertexPositionNormalTexture[] vertexData)
{
vertexData = new VertexPositionNormalTexture[size*size];
indexData = new int[3 * (2 * (size - 1)) * (size - 1)];
//get height data for verticies
float[] vertexYData = getVertexYFromImage(displaceT2D, 32);
//build grid of verts
int xcounter = 0;
int zcounter = 0;
int x_vert = 0;
float y_vert = 0;
int z_vert = 0;
float u = 0.0f;
float v = 0.0f;
for (int index = 0; index < vertexData.Length; index++)
{
y_vert = vertexYData[index];
vertexData[index].Position = new Vector3(x_vert, y_vert, z_vert);
vertexData[index].Normal = new Vector3(0.0f, 0.0f, 0.0f);
vertexData[index].TextureCoordinate = new Vector2(u, v);
if (xcounter < (size - 1))
{
x_vert = x_vert + spread;
xcounter++;
}
else
{
x_vert = 0;
xcounter = 0;
z_vert = z_vert + spread;
zcounter++;
}
u = (float)xcounter / (size - 1);
v = (float)zcounter / (size - 1);
}
//build indixies to form triangles from verts
int icounter = 0;
int cursorx = 0;
int skipcounter = size-1;
for (int currentstrip = 0; currentstrip < (size - 1); currentstrip++)
{
for (int count = 0; count < (size - 1); count++)
{
indexData[icounter] = cursorx;
indexData[icounter + 1] = cursorx + 1;
indexData[icounter + 2] = cursorx + 1 + (size);
icounter = icounter + 3;
indexData[icounter] = cursorx;
indexData[icounter + 1] = cursorx + 1 + size;
indexData[icounter + 2] = cursorx + size;
icounter = icounter + 3;
cursorx++;
}
if (cursorx == skipcounter)
{
cursorx++;
skipcounter = skipcounter + size;
}
}
//calculate normals for smooth shading
for(int counter=0; counter<indexData.Length / 3; counter++)
{
Vector3 firstvec = vertexData[indexData[counter * 3 + 1]].Position - vertexData[indexData[counter * 3]].Position;
Vector3 secondvec = vertexData[indexData[counter * 3]].Position - vertexData[indexData[counter * 3 + 2]].Position;
Vector3 normal = Vector3.Cross(firstvec, secondvec);
normal.Normalize();
vertexData[indexData[counter * 3]].Normal += normal;
vertexData[indexData[counter * 3 + 1]].Normal += normal;
vertexData[indexData[counter * 3 + 2]].Normal += normal;
}
for (int counter = 0; counter < vertexData.Length; counter++)
{
vertexData[counter].Normal.Normalize();
}
}