i need to visualize around 50k-60k points in openGL i managed to print them all but when i use rotate it takes so much time between each rotation because it just prints all the data every single frame. is there a way to print all the data once and freeze the importing of the data so it will keep the image but stop processing?
def PointClouds(pcd_files): #pcd_file
glBegin(GL_POINTS)
for i in pcd_files:
pc = pypcd.PointCloud.from_path(i)
number_of_points = pc.get_metadata().get('points')
z = pc.pc_data['z']
x = pc.pc_data['x']
y = pc.pc_data['y']
for j in range(number_of_points):
glVertex3f(x[j], y[j], z[j])
glEnd()
Main is:
files = glob.glob(os.getcwd() + "\\" + PCD_OutPutDirectory + "\*.pcd")
pygame.init()
display = (1700, 1000)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
gluPerspective(50, (display[0] / display[1]), 0.1, 5000)
glTranslatef(0, 0, -1000)
Clock = pygame.time.Clock()
while True:
Clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glRotatef(2, 1, 1, 3)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
PointClouds(files)
pygame.display.flip()
prints all the points but between each rotation goes over all of the points and prints them again because there are 60k+ points it takes too much time between each rotation. i need it to read the points only once and freeze the image and not the rotation. thanks for the help
The bottleneck is the for loop and the glBegin
/glEnd
sequence. The data is read from the files in every frame.
Note, that drawing by using glBegin
/glEnd
sequences and the fixed function matrix stack is deprecated since decades.
Read the files once at start up and create a Vertex Buffer Object.
(Read more about Vertex Specification and Shader for a state-of-the-art way of rendering.)
The closest solution to your existing code is to use client side capability glEnableClientState
and fixed function attributes glVertexPointer
. With this solution you don't need any shader program.
In the following I assume that you use PyOpenGL.
Load the vertex coordinates to an array
def LoadVertices(pcd_files):
vertices = []
for i in pcd_files:
pc = pypcd.PointCloud.from_path(i)
number_of_points = pc.get_metadata().get('points')
z = pc.pc_data['z']
x = pc.pc_data['x']
y = pc.pc_data['y']
for j in range(number_of_points):
vertices += [x[j], y[j], z[j]]
return vertices
Create a Vertex Buffer Object and create and initialize the buffer object's data store:
import ctypes
def CreateBuffer(vertices):
bufferdata = (ctypes.c_float*len(vertices))(*vertices) # float buffer
buffersize = len(vertices)*4 # buffer size in bytes
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, buffersize, bufferdata, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
return vbo
Create a function which can draw the Point primitives from the buffer:
def DrawBuffer(vbo, noOfVertices):
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, None)
glDrawArrays(GL_POINTS, 0, noOfVertices)
glDisableClientState(GL_VERTEX_ARRAY)
glBindBuffer(GL_ARRAY_BUFFER, 0)
Use this functions in your program:
files = glob.glob(os.getcwd() + "\\" + PCD_OutPutDirectory + "\*.pcd")
pygame.init()
display = (1700, 1000)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
vArray = LoadVertices(files)
noPoints = len(vArray) // 3
bufferObj = CreateBuffer(vArray)
gluPerspective(50, (display[0] / display[1]), 0.1, 5000)
glTranslatef(0, 0, -1000)
Clock = pygame.time.Clock()
while True:
Clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glRotatef(2, 1, 1, 3)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
DrawBuffer(bufferObj, noPoints)
pygame.display.flip()
If you wnat to add individual colors for each point, then vertex and its attribute doesn't only consists of the coordinates (x, y, z)
, it also has to have to RGB color (x, y, z, r, g, b)
, so each attribute tuple consists of 6 components rather than 3.
The fixed function color attribute has to be enabled by the client state GL_COLOR_ARRAY
. Add the attribute is specified by glColorPointer
.
Each attribute tuple has a size of 24 bytes, because a tuple consits of 6 components (x, y, z, r, g, b)
and each component has a size of 4 bytes (this is the size of float
).
This size has to be passed to 3rd parameter (sride
) of glVertexPointer
respectively glColorPointer
.
If a named buffer object is bound, then the last parameter of glVertexPointer
respectively glColorPointer
is treated as byte offset into the buffer objects buffer store. The offset is the number of bytes to the 1st component of the attribute.
In case of glVertexPointer
the offset is 0, because (x, y, z)
are the first components in the attribute tuple.
In case of glColorPointer
the offset is 3*4=12 bytes, because (r, g, b)
are after the 3 coordinates (x, y, z)
and the size of each component is 4.
Since the type of the last parameter is a pointer, the offset has to be casted to ctypes.c_void_p
(e.g. ctypes.c_void_p(3*4)
). For this python built-in library ctypes
has to be imported. If the offset is 0, None
can be used instead of ctypes.c_void_p(0)
.
The specification of the vertex attribtes may lookslike this:
glBindBuffer(GL_ARRAY_BUFFER, vbo)
stride = 6*4 # (24 bates) : [x, y, z, r, g, b] * sizeof(float)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, stride, None)
glEnableClientState(GL_COLOR_ARRAY)
offset = 3*4 # (12 bytes) : the rgb color starts after the 3 coordinates x, y, z
glColorPointer(3, GL_FLOAT, stride, ctypes.c_void_p(offset))
All together:
import ctypes
def LoadVertices(pcd_files):
attributes = []
for i in pcd_files:
pc = pypcd.PointCloud.from_path(i)
number_of_points = pc.get_metadata().get('points')
z = pc.pc_data['z']
x = pc.pc_data['x']
y = pc.pc_data['y']
r = # set the RGB color data here
g =
b =
for j in range(number_of_points):
attributes += [x[j], y[j], z[j], r[j], g[j], b[j]]
return attributes
def CreateBuffer(attributes):
bufferdata = (ctypes.c_float*len(attributes))(*attributes) # float buffer
buffersize = len(attributes)*4 # buffer size in bytes
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, buffersize, bufferdata, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
return vbo
def DrawBuffer(vbo, noOfVertices):
glBindBuffer(GL_ARRAY_BUFFER, vbo)
stride = 6*4 # (24 bates) : [x, y, z, r, g, b] * sizeof(float)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, stride, None)
glEnableClientState(GL_COLOR_ARRAY)
offset = 3*4 # (12 bytes) : the rgb color starts after the 3 coordinates x, y, z
glColorPointer(3, GL_FLOAT, stride, ctypes.c_void_p(offset))
glDrawArrays(GL_POINTS, 0, noOfVertices)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glBindBuffer(GL_ARRAY_BUFFER, 0)
files = glob.glob(os.getcwd() + "\\" + PCD_OutPutDirectory + "\*.pcd")
pygame.init()
display = (800, 600)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
vArray = LoadVertices(files)
noPoints = len(vArray) // 6 # 6 components per attribute tuple
bufferObj = CreateBuffer(vArray)
gluPerspective(50, (display[0] / display[1]), 0.1, 5000)
glTranslatef(0, 0, -1000)
Clock = pygame.time.Clock()
while True:
Clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glRotatef(2, 1, 1, 3)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
DrawBuffer(bufferObj, noPoints)
pygame.display.flip()