How to use glDrawArrays() with VBO - Vertex Buffer Object to display .stl geometry

I am working in Python with PyOpenGL bindings for OpenGL. I have a geometry data in .stl file and I read the file and create a data vertices that has the form:

vertices = [ -0.8528478   4.046846   -1.,..,  -1.441251   -1.07833    10.85     ]

or

vertices = [[ -0.8528478   4.046846   -1.       ]
                [  5.244714    7.080829   -1.       ]
                 ..., 
                [ -1.596363   -0.8316395  10.85     ]
                [ -1.441251   -1.07833    10.85     ]]

the data is the same in both cases, the difference is only in shape. I create the VBO with method geometry() from .stl file using model_loader:

def geometry(self):
    glEnable(GL_VERTEX_ARRAY)
    #    generate a new VBO and get the associated vbo_id
    id = 1
    #    bind VBO in order to use
    glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)
    #    upload data to VBO
    vertices = model_loader.Model_loader(filename = "body.stl").vertices
    self.N_vertices = len(vertices)
    #    data size in bytes
    dataSize = arrays.ArrayDatatype.arrayByteCount(vertices)
    print "dataSize =", dataSize
    glBufferData(GL_ARRAY_BUFFER, dataSize, vertices, GL_STATIC_DRAW)
    glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)

Drawing is done in method paintGL:

def paintGL(self):
    """
    display geometry
    """
    # Clear the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    #    setup camera
    glMatrixMode(GL_MODELVIEW)
    glLoadMatrixf(self.camera.currentMatrix)
    glPushMatrix()      #we don't want each object to move the camera           
    glTranslatef(-10, 0, 0)
    #     light
    glLightfv(GL_LIGHT0, GL_POSITION, np.array([0.0, 100.0, -100.0]))
    glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE )
    glColor4f(1, 1, 1, 1)
    glEnableClientState(GL_VERTEX_ARRAY)
    glVertexPointer(3, 
                    GL_FLOAT, 
                    0, 
                    0)
    glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)   
    glDrawArrays( GL_TRIANGLES, 
                  0, 
                  self.N_vertices )
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0) # reset
    glDisableClientState(GL_VERTEX_ARRAY)
    glPopMatrix() 

The problem is, that it only displays one point if I change to GL_POINTS and when GL_TRANGLES is used is doesn’t display anything!

Before working with VBO I display the geometry only with the glDrawArrays() and the geometry was/is displayed without problems (no errors). But when trying to implement VBO it is not working. How can it be solved?

probably not your error but


glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0) # reset

should be



glBindBuffer(GL_ARRAY_BUFFER,0) # reset

I can’t see all you data but the vertices seem to be around an x value of -1 and your are translating another -10 - are you sure they are visible on the display?

Thanks for the help. They are seen on the display, because I displayed the same data (format) but without the VBO with the following code:


glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, vertices)
glDrawArrays(GL_TRIANGLES, 0, len(vertices))
glDisableClientState(GL_VERTEX_ARRAY)

I tried replacing GL_ELEMENT_ARRAY_BUFFER with GL_ARRAY_BUFFER as suggested but it raised an error:

WindowsError: exception: access violation reading 0x0000000000000000

Are you sure that it should be changed?

I am using glOrtho() projection and as I understand to correctly display all the data you have to translate it away from you in z direction so all the data is put backward. Or am I wrong and I can remove this line?
I have to say I think I solved the problem. The problem was in my model loader and its line is changed from:

vertices =  np.array(self.vertices)

to

vertices =  np.array(self.vertices, dtype='float32')

If I understand correctly as I use python 2.7.5 64bit and also (64-bit) numpy version (also 64-bit) the vertices data was numpy dtype=‘float64’ and this data was displayed with GPU without VBO, but when I display vertices data using VBO the GPU did not display data correctly. From this I can deduce (correctly?) that (my or all) GPUs do not support VBOs with type ‘float64’. Is any truth in this or am I totally wrong? :slight_smile:

I tried replacing GL_ELEMENT_ARRAY_BUFFER with GL_ARRAY_BUFFER as suggested but it raised an error:

You should not get an error.



 glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)   
    glDrawArrays( GL_TRIANGLES, 
                  0, 
                  self.N_vertices )
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0) # reset

This code binds the vertex buffer before the draw - which is correct; then bindings null to the element (index) buffer which does not hurt but does nothing since you are not using an element buffer.
The code change I suggested is to bind null to the vertex buffer after your draw. This should not cause an error.

glOrtho() projection and as I understand to correctly display all the data you have to translate it away from you in z direction

In ortho projections there is no perspective so all objects appear the same size no matter how far they are along the camera z axis. The only reason to change the z value is let the depth buffer order the objects from nearest the camera to furtherest. Orth projections is often uses for 2D rendering like when you are writing a gui interface so all z-values are just set to 0.

vertices data was numpy dtype=‘float64’

Vertex buffers in OpenGL are 32-floats not 64-bit. (That is a slight lie - on some graphics cards it is possible to use 64-bit but with specialized calls). If you are supplying data in 64-bit you need to convert it before loading the vertex buffer.

You should not get an error.

You are right. I tried once again and it is working. If I understand correctly you suggested to change the code:


glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, vertices)
glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)
glDrawArrays(GL_TRIANGLES, 0, len(vertices))
glBindBuffer(GL_ARRAY_BUFFER, 0) # reset
glDisableClientState(GL_VERTEX_ARRAY)

to:


glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, None)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.vbo_id)  
glDrawArrays(GL_TRIANGLES, 0, self.N_vertices)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) # reset
glDisableClientState(GL_VERTEX_ARRAY)

I have to look what is best for my application GL_ELEMENT_ARRAY_BUFFER or GL_ARRAY_BUFFER. Can you give any practical guidelines about it?

In ortho projections there is no perspective so all objects appear the same size no matter how far they are along the camera z axis. The only reason to change the z value is let the depth buffer order the objects from nearest the camera to furtherest. Orth projections is often uses for 2D rendering like when you are writing a gui interface so all z-values are just set to 0.

Thanks for the warning. I was going trough some tutorials and they translated everything away from the camera if the camera was to close to the object and the object was not displayed properly. As I read the CAD softwares uses ortographic (parallel) projection and this is exactly what I need - no distortions in z direction :slight_smile:

Vertex buffers in OpenGL are 32-floats not 64-bit. (That is a slight lie - on some graphics cards it is possible to use 64-bit but with specialized calls). If you are supplying data in 64-bit you need to convert it before loading the vertex buffer.

Thanks for clearing that out and confirming that I was thinking in the right direction :slight_smile:
I will just transform the datatype when assigning (saving) it to VBO (it’s the easiest solution and I hope, I will not have to change it again in the future).

I have to look what is best for my application GL_ELEMENT_ARRAY_BUFFER or GL_ARRAY_BUFFER. Can you give any practical guidelines about it?

Element (index) buffers will usually save some space in the vertex buffer eg a quad would require 6 vertices with GL_ARRAY_BUFFER or 4 vertices and 6 indices with GL_ELEMENT_ARRAY_BUFFER . I don’t believe you will see a performance difference since both can cache vertices equally well (or badly:p)

Thanks for the help and clear explanations. I would like to ask another question more related to OpenGL version(s) (than GL_ELEMENT_ARRAY_BUFFER or GL_ARRAY_BUFFER). The code I am working on runs w/o problems on:
[ol]
[li]GL Version: 4.2.12217 Compatibility Profile Context 12.104.0.0, GL Renderer: AMD Radeon HD 6300M Series, GL Vendor: ATI Technologies Inc.
[/li][li]GL Version: 4.4.0, GL Renderer: NVS 5400M/PCIe/SSE2, GL Vendor: NVIDIA Corporation
[/li][/ol]
but it returns an error when I try to run the code on integrated GPU:
[ol]
[li]GL Version: 4.0.0 - Build 9.17.10.2843, GL Renderer: Intel® HD Graphics 4000, GL Vendor: Intel
[/li][/ol]
And the error:


Traceback (most recent call last):
  File "C:\...\visualization_engine_V6.py", line 120, in initializeGL
    self.geometry()
  File "C:\...\visualization_engine_V6.py", line 167, in geometry
    glEnable(GL_VERTEX_ARRAY)
  File "errorchecker.pyx", line 50, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError (src\errorchecker.c:854)
OpenGL.error.GLError: GLError(
	err = 1280,
	description = 'invalid enumerant',
	baseOperation = glEnable,
	cArguments = (GL_VERTEX_ARRAY,)
)
Traceback (most recent call last):
  File "C:\Users\Ales\Dropbox\python\src\visualization_engine\visualization_engine_V6.py", line 156, in paintGL
    glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)
AttributeError: 'OpenGLWidget' object has no attribute 'vbo_id'

It returns an error if I use GL_ELEMENT_ARRAY_BUFFER or GL_ARRAY_BUFFER calls. Can you point out where to look for the problem? The OpenGL versions are different…

Isn’t the case that, lets say, version 4.2 supports all the functions from the first version (1.0 or 0.1) to version 4.1 and the new added functions to version 4.2?
And the code:


        glEnableClientState(GL_VERTEX_ARRAY)
        glEnable(GL_VERTEX_ARRAY)
        #    generate a new VBO and get the associated vbo_id
        _id = 1
        self.vbo_id = glGenBuffers (_id)
        #    bind VBO in order to use
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)
        #    upload data to VBO
        vertices = model_loader.Model_loader(filename = "udarni_vzvod.stl").vertices
        self.N_vertices = len(vertices)
        #    data size in bytes
        self.dataSize = arrays.ArrayDatatype.arrayByteCount(vertices)
        glBufferData(GL_ARRAY_BUFFER, self.dataSize, vertices, GL_STATIC_DRAW)
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo_id)
        glDisableClientState(GL_VERTEX_ARRAY)

I fixed the problem. The solution is to remove:


glEnable(GL_VERTEX_ARRAY)

in the upper (main) code :slight_smile: