Problems with terrain rendering

I am trying to render a terrain after I read a heightmap and used a delaunay triangulation but I have not been successful. I am unable to see what is my mistake and I was wondering whether someone might be able to to help out (I am hoping it is something minor that I am just missing!). Here are the details.

After reading a heightmap (a geotiff image) as a 2D array. I used the pyDelatin function to generate a delaunay triangulation.

tin = Delatin(DEM.data, max_error=0.01)

The triangulation returns two arrays

  • tin.vertices: contains each vertex that make up the triangulation. Each vertex is described as a triplet (r, c, z). Where r, c are the row, column of the 2d heightmap and z the actual value (in this case the elevation) stored at those coordinates. Each cell or combination of r, c represents a fixed terrain resolution, in this case 5x5m2.
  • tin.triangles: contains indices of each triangle. Each entry is made up of 3 indices that correspond to the indices of the vertices stored in tin.vertices.
    Here is a sample of both,
tin.vertices = 
array([[  0.      , 961.      , 127.47311 ],
       [891.      , 961.      , 117.18625 ],
       [  0.      ,   0.      , 115.99143 ],
       ...,
       [867.      , 579.      ,  15.602401],
       [867.      , 578.      ,  15.801425],
       [854.      , 645.      ,  23.049685]], dtype=float32)

tin.triangles=
array([[165544, 200225, 525772],
       [200225, 301175, 525772],
       [499652, 358969, 320685],
       ...,
       [ 22937, 455845, 586587],
       [176033,   3933, 222414],

       [181448, 586587, 340039]], dtype=uint32)

In order to generate the VAO, VBO and EBO necessary, tin,vertices gets transformed. This is done by, switching z values for y, multiplying the r,c by the resolution of the image, i.e, 5 and adding of offset of 0.5. And finally ‘flattening’ the array (from Nx3 to 3N), looking like this

vertices = array([4805.5     ,  127.47311 , 4455.5     , ..., 3225.5     ,
         23.049685,  185.5     ], dtype=float32)

And tin.triangles is simply ‘flattened’ to generate the indices array,

indices = array([165544, 200225, 525772, ..., 181448, 586587, 340039], dtype=uint32)

The rest of the code is as follows,

# CAMERA + WINDOW INITIALIZATION

# camera location
cam_x, cam_z = DEM.to_opengl(np.array([DEM.nrows//2, DEM.ncols//2])) # center
cam_y = DEM.data[DEM.nrows//2, DEM.ncols//2]+10
terrain_camera = Camera(position=(cam_x, cam_y, cam_z) )

# initialize opengl
glfw.init()
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.DEPTH_BITS, 32) 

# create glfw window
window = glfw.create_window(terrain_camera.width, terrain_camera.height, 'Terrain', None, None)
if window is None:
    print("Failed to create GLFW window")
    glfw.terminate()           
glfw.make_context_current(window)

# set up window
glViewport(0,0, terrain_camera.width, terrain_camera.height)

# enable depth buffer
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LESS)

Creating VAO, VBO, EBOs

# CREATE RENDERING OBJECTS
# create tin vertex array object (vao) and vertex buffer object (vbo) 
tin_vao = glGenVertexArrays(1) # where we store the data
glBindVertexArray(tin_vao)

tin_vbo = glGenBuffers(1) # memory buffer where we store array
glBindBuffer(GL_ARRAY_BUFFER, tin_vbo)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)

# position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * vertices.itemsize, ctypes.c_void_p(0)) # None
glEnableVertexAttribArray(0) # position

# create tin element buffer object
tin_ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tin_ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW)

Final rendering,

# RENDER SCENE

# create shaders
shader = Shader(pth / 'terrain/shader/display.vs', pth / 'terrain/shader/display.fs')

# activate cubemap shader
shader.use()

# wireframe
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)

# clear colors and buffers
glClearColor(0.2,0.2,0.4,0.2)
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT)

# create a projection matrix pass it to shader
aspect = terrain_camera.width / terrain_camera.height
projection = glm.perspective(glm.radians(45.0), aspect, 0.1, 5000)
shader.set_uniform_mat4('projection', projection)

# RENDER

# render tin
glBindVertexArray(tin_vao)

# update view matrices for display camera
view = terrain_camera.get_matrix_view()
shader.set_uniform_mat4('view', view)

# terrain
model = glm.mat4(1.0)
model = glm.translate(model, glm.vec3(0, 1, - 10.0))
shader.set_uniform_mat4('model', model)

# draw tin
glDrawElements(GL_TRIANGLES, len(indices), GL_UNSIGNED_INT, 0)

# swap buffers?
glfw.swap_buffers(window)

# remove binding
glBindVertexArray(0)

# TERMINATE
###########
glDeleteVertexArrays(1, (tin_vao,))
glDeleteBuffers(1, (tin_vbo,))
glDeleteBuffers(1, (tin_ebo,))  
glfw.terminate()

my vertex shader is,

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{   
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

and fragment is

#version 330 core

out vec4 frag_color;

void main(){
    frag_color = vec4(1.0f, 0.5f, 0.2f, 0.5f);
}

At the moment all I see is a blank screen. What am I missing?

I’m unfit for details, but are you executing your code properly?
Make sure that you have initiation-code separated from the drawing-loop.
(you present the ‘Final rendering’ with glDelete-everything and glfw.terminate ! )

I’d suggest you put this off to the side and start with a simple GL program that renders a black or blue clear screen. Then gradually add functionality to that. When you “break” it, you’ll know what specifically caused the breakage, and can then ask very specific questions if you don’t understand why.

That’s better than starting with a large chunk of code and having no idea what you did to break it. Then giving up, and asking someone else to debug your code for you.

Thank you!
Yes,… as you can tell I am using a python binding (pyopengl) to call opengl. You are correct that the code would end right away however I am using a notebook to run the code which means that I can execute blocks of code one at a time.

As you can tell I am using a python binding (pyopengl) to call opengl and I run the code in a notebook which means that I execute a line or blocks of lines one a time. Nothing really ‘breaks’ in the code. I have gone through the code a couple of times and I am just not able to see why I not seeing the terrain I am trying to visualize. Everything seems to check out but I know that sometimes opengl will run even if something is not doing what you want. I will keep on digging.

I just realized that I probably should not be translating the terrain as I was doing given that my camera is supposed to be smack in the center of the terrain. So I have just changed that in my original posting. I am still not getting any triangles to show up though.

Ok found the mistake. As I expected it was a very minor issue.

This has to do with the following line

glDrawElements(GL_TRIANGLES, len(self.indices), GL_UNSIGNED_INT, 0)

it should be in pyopengl,

glDrawElements(GL_TRIANGLES, len(self.indices), GL_UNSIGNED_INT, None)