Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 10 of 10

Thread: How can I make my height maps load quicker.

  1. #1
    Intern Newbie
    Join Date
    Aug 2013
    Posts
    32

    How can I make my height maps load quicker.

    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..

    Code :
    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.

  2. #2
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    491
    Quote Originally Posted by Exempt View Post
    Any tips as to how I could improve the speed of this loader..
    1. Force SDL to provide the data in a known format so that you can omit the SDL_GetRGB() calls and access the data directly.
    2. Generate the vertices as you read the pixels rather than using two sets of loops.
    3. Use the .reserve() method to pre-allocate the vector's storage.


    Quote Originally Posted by Exempt View Post
    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.

  3. #3
    Intern Newbie
    Join Date
    Aug 2013
    Posts
    32
    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?

  4. #4
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    491
    Quote Originally Posted by Exempt View Post
    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.

  5. #5
    Intern Newbie
    Join Date
    Aug 2013
    Posts
    32
    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.
    Code :
    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;
    }
    Last edited by Exempt; 08-17-2013 at 09:36 AM.

  6. #6
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    491
    Quote Originally Posted by Exempt View Post
    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.

    Quote Originally Posted by Exempt View Post
    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().

  7. #7
    Intern Newbie
    Join Date
    Aug 2013
    Posts
    32
    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.

  8. #8
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    491
    Quote Originally Posted by Exempt View Post
    trying to compute normals is a pain.
    For a height map, you'd typically use something like (pseudo-code):
    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.

  9. #9
    Intern Newbie
    Join Date
    Aug 2013
    Posts
    32
    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.
    Code :
    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...
    	}
    }

    Code :
    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;
    	}
    }
    Last edited by Exempt; 08-17-2013 at 02:54 PM.

  10. #10
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    491
    Quote Originally Posted by Exempt View Post
    This just doesn't work right at all... still in the process of trying to figure it out though.
    Code :
    	for(int i = 0; i < index.size()-3; i++)
    Code :
    	for(int i = 0; i < index.size(); i += 3) {
    		// index[i], index[i+1], index[i+2] form a complete triangle

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •