A simple way to increase performance when rendering point clouds is using levels of detail (LOD). If the camera is far from an object, a lower detailed version of that object can be rendered without much loss of visual detail. As the camera moves closer, higher fidelity versions can be drawn.
A while ago I thought about adding this functionality to XB PointStream and soon realized that the library already supports it! The library can load different point clouds in the same canvas, which allows users to split a cloud into a series of files and conditionally render them. When I had this idea I was too busy with other work, so I had to put it off.
Yesterday I finally sat down and began working out the details to get a demo up and running. I needed two things. First, I needed to evenly distribute the points in a cloud. All of the clouds in my repository have been scanned linearly or in blocks, which doesn’t lend itself well for LOD purposes. For LOD, each cloud needs to represent a coarse level version of the entire object. Second, I needed to split up the cloud into several files.
I decided to start with a simple ASCII point cloud format, ASC. The file is organize something like this:
1.13 6.86 7.81 0 128 255
7.27 9.59 7.29 0 128 255
Using Some Python
I don’t know Python, but I knew it would be a good choice for this task. My plan was to load the input file into an array, randomly select indices from the array and write them out to the output file.
Soon after I got to work on writing my script, I saw there was a shuffle() method for arrays. This saved me quite a bit of work, so I was happy. I then hacked together the rest of the script. If you’re a Python developer, let me know if there are ways I can fix up the code.
""" Andor Salga This script will take an ASC file, evenly distribute the points and separate the cloud into a series of files. """ import random import sys # # Usage: python lod.py pointCloud.asc 4 if (len(sys.argv) < 4): print "Usage: python lod.py pointcloud.asc outFileName [numLevels]\n" else: inFileName = sys.argv; outBaseFileName = sys.argv; arr =  file = open(inFileName) while 1: line = file.readline() arr.append(line) if not line: break file.close() random.shuffle(arr); # Find out how many points we are going to have per # file. Don't worry about rounding issues. We will simply # append the remaining points to the last cloud. numFiles = int(sys.argv) pointsPerFile = len(arr)/numFiles nextFile = 0 outFilename = outBaseFileName + "_0.asc" FILE = open(outFilename, "w") line = 0 for item in arr: FILE.write(str(item)[0 : -1] + "\n") if(line > 0 and (line % pointsPerFile == 0 and nextFile+1 != numFiles )): FILE.close() nextFile += 1 outFilename = outBaseFileName + "_" + str(nextFile) + ".asc" FILE = open(outFilename, "w") line += 1 FILE.close()
I tested this first with a million points and didn’t see much performance gain. This was a bit disappointing and suggests that there are other bottlenecks in the library. I decided instead to try the largest file I have, the visible human, which is about 3.5 million points. I fed the cloud into my script and split it up into 10 files. When testing this I got a reasonable FPS gain. When rendering all the clouds I get ~20FPS and when I zoom out and render only 1 cloud, I get ~60FPS.
If you’re going to render a large data set with XB PointStream, consider using my script to split it up into many files to increase your script’s performance.