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?