PDA

View Full Version : How can I make my height maps load quicker.



Exempt
08-16-2013, 10:35 PM
I've got my heightMapLoad function working the way I want it side from how fast it is. As of now it takes around 30 seconds to load a 2048x2048 greyscale image. Any tips as to how I could improve the speed of this loader..


bool loadHeightMap(const char* fileName, std::vector<float>& heightMap,
std::vector<unsigned int>& index, float tileSize, float maxHeight)
{
//Load image with SDL
SDL_Surface* img = SDL_LoadBMP(fileName);
if(!img)
{
std::cout<<"Failed to load " << fileName << "." << std::endl;
return false;
}

//Store height values in heights vector.
std::vector<float> tmp;
std::vector<std::vector<float>> heights;
for(int i = 0; i < img->h; i++)
{
tmp.clear();
for(int j = 0; j < img->w; j++)
{
Uint32 pixel = ((Uint32*)img->pixels)[i * img->pitch / 4 + j];
Uint8 r, g, b;
SDL_GetRGB(pixel, img->format, &r, &g, &b);

tmp.push_back((float)r/255.0);
}
heights.push_back(tmp);
}

//Fill the vertex data and index data.
for(int z = 0; z < img->h; z++)
{
for(int x = 0; x < img->w; x++)
{
heightMap.push_back((float)x * tileSize);
heightMap.push_back((float)heights[x][z] * maxHeight);
heightMap.push_back((float)z * tileSize);

heightMap.push_back((float)heights[x][z]);
heightMap.push_back((float)heights[x][z]);
heightMap.push_back((float)heights[x][z]);


if(x < img->w-1 && z < img->h-1)
{
int top = z * img->w + x;
int bottom = (z+1)*img->w + x;

index.push_back(top);
index.push_back(bottom);
index.push_back(top+1);

index.push_back(top+1);
index.push_back(bottom);
index.push_back(bottom+1);
}
}
}

SDL_FreeSurface(img);
if(heights.empty())
{
std::cout<< "Failed to get height data." << std::endl;
return false;
}
if(index.empty())
{
std::cout<< "Failed get index data." << std::endl;
return false;
}
if(heightMap.empty())
{
std::cout<< "Failed to get vertex data." << std::endl;
return false;
}
return true;
}

edit: A small question that's been bugging me I just haven't search much for an answer yet... Is there some kind of default max view distance in opengl? I know when I load this 2048x2048 heightmap I cannot see the whole thing as the view is cut out at a far distance.

GClements
08-17-2013, 12:00 AM
Any tips as to how I could improve the speed of this loader..

Force SDL to provide the data in a known format so that you can omit the SDL_GetRGB() calls and access the data directly.
Generate the vertices as you read the pixels rather than using two sets of loops.
Use the .reserve() method to pre-allocate the vector's storage.



Is there some kind of default max view distance in opengl? I know when I load this 2048x2048 heightmap I cannot see the whole thing as the view is cut out at a far distance.
The maximum distance is set by the last argument (zFar or farVal) to a gluPerspective(), glOrtho() or glFrustum() call.

Setting this value too low will clip geometry which should be visible. Setting it to high (or, for a perspective projection, setting the near-plane distance too low) will reduce the precision of the depth buffer.

Exempt
08-17-2013, 07:52 AM
Ah yes I had ment to combine them loop and forgot before I posted this. I will look into the .reserve. I've never used it but seems simple enough... Although after I combine the loop it will only use the vectors I pass by reference. Would that stillbe a good idea?

GClements
08-17-2013, 09:08 AM
Ah yes I had ment to combine them loop and forgot before I posted this. I will look into the .reserve. I've never used it but seems simple enough... Although after I combine the loop it will only use the vectors I pass by reference. Would that stillbe a good idea?
Yes. If you know the final number of elements in the vector, having it allocate the correct amount of memory in advance will eliminate some copying.

Exempt
08-17-2013, 09:50 AM
I'm got it down to about 15 seconds to fully load the height map. Any other ideas how I could possibly improve it's speed?

On a side note the speed seemed to be the same using SDL_GetRGB or the method I use now to just access the red color from the format. I tried with and without the SDL_LockSurface(img); with no change in speed.

edit: I changed this again. I wanted to get rid of all the type casting and I changed r/255.0 so it's only done once.

bool loadHeightMap(const char* fileName, std::vector<float>& heightMap,
std::vector<unsigned int>& index, float tileSize, float maxHeight)
{
//Load image with SDL
SDL_Surface* img = SDL_LoadBMP(fileName);
if(!img)
{
std::cout<<"Failed to load " << fileName << "." << std::endl;
return false;
}

heightMap.reserve((img->h * img->w) * 6); //This is only 6 because I'm adding color into the vector.
index.reserve(((img->h-1) * (img->w-1)) * 6);

SDL_PixelFormat* fmt = img->format;
Uint32 temp, pixel;
float r;
SDL_LockSurface(img);
for(int z = 0; z < img->h; z++)
{
for(int x = 0; x < img->w; x++)
{
pixel = ((Uint32*)img->pixels)[z * img->pitch / 4 + x];

temp = pixel & fmt->Rmask;
temp = temp >> fmt->Rshift;
temp = temp << fmt->Rloss;
r = (Uint8)temp/255.0;

/*Uint32 pixel = ((Uint32*)img->pixels)[z * img->pitch / 4 + x];
Uint8 r, g, b;
SDL_GetRGB(pixel, img->format, &r, &g, &b);*/

heightMap.push_back(x * tileSize);
heightMap.push_back(r * maxHeight);
heightMap.push_back(z * tileSize);

heightMap.push_back(r);
heightMap.push_back(r);
heightMap.push_back(r);


if(x < img->w-1 && z < img->h-1)
{
int top = z * img->w + x;
int bottom = (z+1)*img->w + x;

index.push_back(top);
index.push_back(bottom);
index.push_back(top+1);

index.push_back(top+1);
index.push_back(bottom);
index.push_back(bottom+1);
}
}
}
SDL_FreeSurface(img);

if(index.empty())
{
std::cout<< "Failed get index data." << std::endl;
return false;
}
if(heightMap.empty())
{
std::cout<< "Failed to get vertex data." << std::endl;
return false;
}
return true;
}

GClements
08-17-2013, 01:01 PM
I'm got it down to about 15 seconds to fully load the height map.
Is that a debug or release build? There can be a lot of difference between the two when using templates. But even without optimisations, 15 seconds seems ridiculously slow.


Any other ideas how I could possibly improve it's speed?
Try to force SDL to provide a specific format so that you can avoid the shift/mask operations. You should be able to just read every third or fourth byte directly. Or skip SDL altogether and just read an 8-bpp BMP (or PGM) file directly.

Another possibility is to replace the .reserve() call with .resize() and copy directly into heightMap[n++] rather than using .push_back().

Exempt
08-17-2013, 01:05 PM
It is in debug build so maybe that's part of it... I'll look into reading the file directly but that'll be a new one for me lol.

...trying to compute normals is a pain.

GClements
08-17-2013, 01:12 PM
trying to compute normals is a pain.
For a height map, you'd typically use something like (pseudo-code):


vec3 vx = vertex[y][x+1] - vertex[y][x-1];
vec3 vy = vertex[y+1][x] - vertex[y-1][x];
vec3 normal = normalize(cross(vx, vy));

The only minor complication is dealing with the vertices at the edges of the grid.

Exempt
08-17-2013, 01:33 PM
Edit: The loaders takes about 2 seconds to load in release mode, I guess that's ok.



I've got several of the functions made in a vector class but what's giving me more trouble is something a lot more simple... -.-

I need to fill vec3 objects with triangles so I figured it'd be easy to use the heightMap vertex data + my index to recreate it. I wish I could think of a way to do this as I fill the vertex but it seems full triangles are a must.

This just doesn't work right at all... still in the process of trying to figure it out though.

void heightmap::computeNormals()
{
vector3d vec;

for(int i = 0; i < index.size()-3; i++)
{
std::cout<< heightMap[index[i]] << " " << heightMap[index[i+1]] << " " << heightMap[index[i+2]] << "\n";
//I'd just fill the vec3 with all the points in order so I could normalize them...
}
}


vector3d vector3d::crossProduct(const vector3d& vec2)
{
return (vector3d(y*vec2.z-z*vec2.y, x*vec2.z-z*vec2.x, x*vec2.y-y*vec2.x));
}

void vector3d::normalize()
{
float len = length();
if(len != 0)
{
x /= len;
y /= len;
z /= len;
}
}

GClements
08-20-2013, 09:12 AM
This just doesn't work right at all... still in the process of trying to figure it out though.


for(int i = 0; i < index.size()-3; i++)




for(int i = 0; i < index.size(); i += 3) {
// index[i], index[i+1], index[i+2] form a complete triangle