Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

Terrain heightmap and shading problem


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 pulo   Members   -  Reputation: 144

Like
0Likes
Like

Posted 16 August 2012 - 02:03 PM

Hey there,

im currently working on some kind of terrain engine (RTS style). My first step was to load in a greyscale heightmap (BMP 24bit)
Posted Image

It is supposed to be very steep with some rough edges (unit blocker). Now if i just visualize this heightmap it looks like this:

Posted Image

wich is obviously not my intention. Is this normal for a grayscale heightmap to look like this?
My solution was then to just "normalize" the y-values by dividing them by "10". It looks kinda okayish then but far from that what i was expecting from the heightmap.

The next problem is the shading.. but maybe i should solve this kinda issue first.
Im not sure where my problem might be so it is kinda difficult to post the code. I guess i start by positing the code for the loading of the heightmap:

FILE* filePtr;
int error;
unsigned int count;
BITMAPFILEHEADER bitmapFileHeader;
BITMAPINFOHEADER bitmapInfoHeader;
int imageSize, i, j, k, index;
unsigned char* bitmapImage;
unsigned char height;
// Open the height map file in binary.
error = fopen_s(&filePtr, filename, "rb");
if(error != 0)
{
  return false;
}
// Read in the file header.
count = fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
if(count != 1)
{
  return false;
}
// Read in the bitmap info header.
count = fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
if(count != 1)
{
  return false;
}
// Save the dimensions of the terrain.
m_terrainWidth = bitmapInfoHeader.biWidth;
m_terrainHeight = bitmapInfoHeader.biHeight;
// Calculate the size of the bitmap image data.
imageSize = m_terrainWidth * m_terrainHeight * 3;
// Allocate memory for the bitmap image data.
bitmapImage = new unsigned char[imageSize];
if(!bitmapImage)
{
  return false;
}
// Move to the beginning of the bitmap data.
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
// Read in the bitmap image data.
count = fread(bitmapImage, 1, imageSize, filePtr);
if(count != imageSize)
{
  return false;
}
// Close the file.
error = fclose(filePtr);
if(error != 0)
{
  return false;
}
// Create the structure to hold the height map data.
m_heightMap = new HeightMapType[m_terrainWidth * m_terrainHeight];
if(!m_heightMap)
{
  return false;
}
// Initialize the position in the image data buffer.
k=0;
// Read the image data into the height map.
for(j=0; j<m_terrainHeight; j++)
{
  for(i=0; i<m_terrainWidth; i++)
  {
   height = bitmapImage[k];
  
   index = (m_terrainHeight * j) + i;
   m_heightMap[index].x = (float)i;
   m_heightMap[index].y = (float)height;
   m_heightMap[index].z = (float)j;
   k+=3;
  }
}
// Release the bitmap image data.
delete [] bitmapImage;
bitmapImage = 0;


Thanks for any help!

Ad:

#2 froop   Members   -  Reputation: 263

Like
0Likes
Like

Posted 16 August 2012 - 02:24 PM

Are you storing your heightmap in an 8bit / channel format by any chance? If so, you should try a 16 bit or heigher format.
There seems to be some kind of emboss effect on the heightmap which could be the reason the the "spiky" edges because of the highlights.

#3 pulo   Members   -  Reputation: 144

Like
0Likes
Like

Posted 16 August 2012 - 02:43 PM

The bitmap is 24 bit / channel.
And the emboss effect is intentional to form some kind of plateau, wouldnt have the grey variation to be more distinctive to form such spiky edges?

#4 lwm   Members   -  Reputation: 512

Like
0Likes
Like

Posted 16 August 2012 - 03:24 PM

In the first image you posted, it looks like the heightmap contains some baked lighting or ambient occlusion. Where we humans see a shadow from a neighboring mountain, your code generates a valley. You don't have to make the heighmap look good too human eyes.

my last project: Roa. working on something new now.


#5 pulo   Members   -  Reputation: 144

Like
0Likes
Like

Posted 16 August 2012 - 03:34 PM

Yes i know. but if i use:
Posted Image

its really spiky aswell. Maybe i have just a false understanding of how such a greyscale map should look like when rendered? Or is such a map supposed to look less spiky?
Thanks for any help so far!

EDIT: I guess its because i use a bitmap where the height values can only be 256 different values. I will try something different tomorrow.

Edited by pulo, 16 August 2012 - 03:39 PM.


#6 phil_t   Members   -  Reputation: 1029

Like
0Likes
Like

Posted 16 August 2012 - 04:12 PM

The above image looks very smooth - it shouldn't result in any spikes. I don't think it has to do with only having 256 values either. Can you show use what it looks like if you use the above image?

(your first image definitely looked like it would result in spikes, since there were "fringes" around all the level transitions)

What about if you just have a smooth gradient?

How are you triangulating your terrain?

Edited by phil_t, 16 August 2012 - 04:17 PM.


#7 FLeBlanc   Members   -  Reputation: 1834

Like
0Likes
Like

Posted 16 August 2012 - 04:40 PM

I set your original map as a displacement map on a plane in Blender and rendered it:

Posted Image

It's really spiky. Some things you can do to mitigate the spikiness:

1) Reduce the vertical scale of your terrain. It won't eliminate the spikes, but it might make them less noticeale:
Posted Image

2) Run a blur filter across the source, to reduce high-frequency noise and smooth out some of the spikes:

Posted Image

Generally speaking, you want to avoid having high-frequency noise in your heightmaps. In the case of your fractal noise heightmap earlier, you could re-generate it with fewer octaves. Or run a blur pass across it.

#8 pulo   Members   -  Reputation: 144

Like
0Likes
Like

Posted 17 August 2012 - 04:02 AM

Thanks for any comment so far. Here a screenshot with the "normal" heightmap:

Posted Image

And this is how i triangulate the loaded heightmap:

// Calculate the number of vertices in the terrain mesh.
m_vertexCount = (m_terrainWidth - 1) * (m_terrainHeight - 1) * 6;
// Set the index count to the same as the vertex count.
m_indexCount = m_vertexCount;
// Create the vertex array.
vertices = new VertexType[m_vertexCount];
if(!vertices)
{
  return false;
}
// Create the index array.
indices = new unsigned long[m_indexCount];
if(!indices)
{
  return false;
}
// Initialize the index to the vertex buffer.
index = 0;
// Load the vertex and index array with the terrain data.
// Load the vertex and index array with the terrain data.
for(j=0; j<(m_terrainHeight-1); j++)
{
  for(i=0; i<(m_terrainWidth-1); i++)
  {
   index1 = (m_terrainHeight * j) + i;		  // Bottom left.
   index2 = (m_terrainHeight * j) + (i+1);	  // Bottom right.
   index3 = (m_terrainHeight * (j+1)) + i;	  // Upper left.
   index4 = (m_terrainHeight * (j+1)) + (i+1);  // Upper right.
   // Upper left.
   vertices[index].position = D3DXVECTOR3(m_heightMap[index3].x, m_heightMap[index3].y, m_heightMap[index3].z);
   vertices[index].normal = D3DXVECTOR3(m_heightMap[index3].nx, m_heightMap[index3].ny, m_heightMap[index3].nz);
   indices[index] = index;
   index++;
   // Upper right.
   vertices[index].position = D3DXVECTOR3(m_heightMap[index4].x, m_heightMap[index4].y, m_heightMap[index4].z);
   vertices[index].normal = D3DXVECTOR3(m_heightMap[index4].nx, m_heightMap[index4].ny, m_heightMap[index4].nz);
   indices[index] = index;
   index++;
   // Bottom left.
   vertices[index].position = D3DXVECTOR3(m_heightMap[index1].x, m_heightMap[index1].y, m_heightMap[index1].z);
   vertices[index].normal = D3DXVECTOR3(m_heightMap[index1].nx, m_heightMap[index1].ny, m_heightMap[index1].nz);
   indices[index] = index;
   index++;
   // Bottom left.
   vertices[index].position = D3DXVECTOR3(m_heightMap[index1].x, m_heightMap[index1].y, m_heightMap[index1].z);
   vertices[index].normal = D3DXVECTOR3(m_heightMap[index1].nx, m_heightMap[index1].ny, m_heightMap[index1].nz);
   indices[index] = index;
   index++;
   // Upper right.
   vertices[index].position = D3DXVECTOR3(m_heightMap[index4].x, m_heightMap[index4].y, m_heightMap[index4].z);
   vertices[index].normal = D3DXVECTOR3(m_heightMap[index4].nx, m_heightMap[index4].ny, m_heightMap[index4].nz);
   indices[index] = index;
   index++;
   // Bottom right.
   vertices[index].position = D3DXVECTOR3(m_heightMap[index2].x, m_heightMap[index2].y, m_heightMap[index2].z);
   vertices[index].normal = D3DXVECTOR3(m_heightMap[index2].nx, m_heightMap[index2].ny, m_heightMap[index2].nz);
   indices[index] = index;
   index++;
  }
}
// Set up the description of the static vertex buffer.
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the vertex data.
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;
// Now create the vertex buffer.
result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
if(FAILED(result))
{
  return false;
}
// Set up the description of the static index buffer.
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the index data.
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
// Create the index buffer.
result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
if(FAILED(result))
{
  return false;
}
// Release the arrays now that the buffers have been created and loaded.
delete [] vertices;
vertices = 0;
delete [] indices;
indices = 0;

The heightmap i first posted is getting generated through some code. Maybe i could leave the image output / input step away and just calculate the heights directly for my terrain. The code im using is pretty much a recreation of some java code for terrain generation for a game wich worked fine there (there was no image used to create the terrain, it was directly created by the code it seems).

Edited by pulo, 17 August 2012 - 05:01 AM.


#9 ankhd   Members   -  Reputation: 383

Like
0Likes
Like

Posted 20 August 2012 - 06:57 AM

you may want to average the heights of the height map.
you could averages each heightmap pos with its eight neighbor pixels. Note
that if a pixel is missing a neighbor, you just don't include it in the average
that is, edge pixels don't have a neighbor pixel.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS