0
\$\begingroup\$

I'm brand new to OpenGL and have been trying my hand modern openGL. A few days ago, I tried to create some terrain by using a heightmap stored in a grayscale 32-bit BMP. I am using SDL 2.0, GLEW and OpenGL 3.3.

So, I wrote the following height map loading function:

bool terrain::loadHeightMap( const char* path ) {
Uint32 start = SDL_GetTicks(); // track the start time for performance testing

/* Load the heights from file */
std::cout << "Loading heightmap '" << path << "'..." << std::endl;
SDL_Surface* img = SDL_LoadBMP( path );
if( !img ) {
    std::cout << "Failed to load heightmap: " << path << ". SDL Error: " << SDL_GetError() << std::endl;
    return false;
}

// get the width and the height of the terrain
width = img->w;
height = img->h;

// get the bytes per pixel to verify that it is a 32-bit BMP file
int bpp = img->format->BytesPerPixel;
if( bpp != 4 ) {
    std::cout << "Could not load heightmap " << path << ". BMP is not 32-bit." << std::endl;
    return false;
}

// stores the vector for the current row.
std::vector<float> temp;

Uint8 g,b; // junk values, which we need for the SDL_GetRGB() below. We define them here so that they do not need to be re-defined on each loop.

for( int y=0;y<height;y++ ) {
    for( int x=0;x<width;x++ ) {
        /* Here p is the address to the pixel we want to retrieve */
        Uint8 *p = (Uint8 *)img->pixels + y * img->pitch + x * bpp;
        Uint8 r;
        SDL_GetRGB( *(Uint32 *)p,img->format,&r,&g,&b); // get the red pixel data
        temp.push_back( (float)r/255 );
    }
    heightMap.push_back( temp );
    temp.clear();
}

SDL_FreeSurface( img );

/* In order to give the vertices to openGL we need to create a vector */
std::vector< glm::vec3 > vertices;
for( int y=0;y<height;y++ ) {
    for( int x=0;x<width;x++ ) {
        vertices.push_back( glm::vec3( y,heightMap[x][y],x ) );
    }
}

/* Pass the data to openGL */
glGenVertexArrays( 1,&vertexArrayID );
glBindVertexArray( vertexArrayID );

glGenBuffers( 1,&vertexBufferID );
glBindBuffer( GL_ARRAY_BUFFER,vertexBufferID );
glBufferData( GL_ARRAY_BUFFER,vertices.size()*sizeof(glm::vec3),&vertices[0],GL_STATIC_DRAW );

/* Compute normals */

std::cout << "Successfully loaded heightmap in " << SDL_GetTicks()-start << " ms." << std::endl;

return true;

}

I then used the following rendering code to render it:

/* Compute the model, view, projection and MVP matrices */
glm::mat4 Projection = glm::perspective( 60.0f,80.0f,0.01f,1000.0f );
glm::mat4 View = cam->getMatrix(); // view matrix
glm::mat4 Model = glm::mat4(1.0f); // model matrix
glm::mat4 MVP = Projection * View * Model; // Model-View-Projection matrix

glUniformMatrix4fv( matrixID,1,GL_FALSE,&MVP[0][0] ); // set the MVP matrix as the current active matrix

glUseProgram( shaderID ); // set the terrain shader to the current shader

/* First attribute buffer : vertices */
glEnableVertexAttribArray( 0 );
glBindBuffer( GL_ARRAY_BUFFER,vertexBufferID );
glVertexAttribPointer(
    0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
    3,                  // size
    GL_FLOAT,           // type
    GL_FALSE,           // normalized?
    0,                  // stride
    (void*)0            // array buffer offset
);

/* Render the terrain mesh */
glDrawArrays( GL_TRIANGLES,0,width*height );

glDisableVertexAttribArray(0);

However when I render it and move around with my camera I get strange line formations instead of filled in surfaces... The height map is correct as when I move around I can see the relief I loaded in the BMP file, but no faces! Could this be because I should be using something other than GL_TRIANGLES in the glDrawArrays() function?

what I see on my monitor

Here is my vertex shader:

#version 330 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;

out vec2 UV;

// Values that stay constant for the whole mesh.
uniform mat4 MVP;

void main(){
// Output position of the vertex, in clip space : MVP * position
vec4 v = vec4(vertexPosition_modelspace,1); // Transform an homogeneous 4D vector,     remember ?
gl_Position = MVP * v;
}

Here is my fragment shader:

#version 330 core

// Ouput data
out vec3 color;

void main(){
    color = vec3(0,1,0);
}

Any suggestions would be appreciated :)

\$\endgroup\$
0
\$\begingroup\$

You can't just generate the points in a grid and hope OpenGL figures our what you want. When you use glDrawArrays you need to pass the whole triangles (or whatever primitive you're drawing) to it. So either change your loop to:

for( int y=0;y<height-1;y++ ) {
    for( int x=0;x<width-1;x++ ) {
        vertices.push_back( glm::vec3( y,heightMap[x][y],x ) );
        vertices.push_back( glm::vec3( y+1,heightMap[x][y+1],x ) );
        vertices.push_back( glm::vec3( y+1,heightMap[x+1][y+1],x+1 ) );

        vertices.push_back( glm::vec3( y,heightMap[x][y],x ) );
        vertices.push_back( glm::vec3( y+1,heightMap[x+1][y+1],x+1 ) );
        vertices.push_back( glm::vec3( y,heightMap[x+1][y],x+1 ) );
    }
}

Obviously you also need to set glDrawArrays to glDrawArrays( GL_TRIANGLES,0,(width-1)*(height-1)*6 );. Of course you can make it a lot more efficient if you use GL_TRIANGLE_STRIP but I just thought I'd give you the minimal amount of things to change first.

Or you can add an index buffer and use glDrawElements or the like

\$\endgroup\$
  • \$\begingroup\$ I was thinking about this for a while before you answered, and realised that the reason it is drawing lines is because all the vertices are lined up straight and therefore when openGL interprets them as triangles it just connects them up. I understand your approach to it now, and with GL_TRIANGLE_STRIP it works quite nicely. Thank you! \$\endgroup\$ – Tombombadilly Oct 14 '14 at 9:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.