glitter Example: Point Cloud Renderer
Summary
This program will open a GLUT window and render a random, colored, rotating point cloud.
Front matter
Module docstring
The module docstring is used as a description of this example in the generated documentation:
"""Viewer for a random point cloud.
@author: Stephan Wenger
@date: 2012-03-13
"""
Imports
Our scene is going to rotate. The rotating modelview matrix is computed using the sine and cosine functions from the math module:
from numpy import sin, cos, pi
glitter uses numpy for
representation of array data. We will use numpy's randn()
function to generate random point coordinates:
from numpy.random import randn
We can usually import classes and functions contained in glitter submodules directly from glitter:
from glitter import VertexArray, State, get_default_program
Modules with external dependencies other than numpy, such as platform dependent parts like methods for the generation of an OpenGL context, however, have to be imported from their respective submodules:
from glitter.contexts.glut import GlutWindow, main_loop, get_elapsed_time
Main class
We wrap all the OpenGL interaction in a class. The class will contain an
__init__()
method to set up all OpenGL objects, any required
callback methods, as well as a run()
method to trigger execution
of the GLUT main loop.
class PointcloudRenderer(object):
Initialization
When a PointcloudRenderer
instance is created, we need to
initialize a few OpenGL objects.
def __init__(self):
First, we create a window; this also creates an OpenGL context.
self.window = GlutWindow(double=True, multisample=True)
Then, we set the GLUT display callback function which will be defined later.
self.window.display_callback = self.display
In the OpenGL core profile, there is no such thing as a "standard pipeline"
any more. We use the minimalistic defaultpipeline
from the
glitter.convenience
module to create a shader program instead:
self.shader = get_default_program()
Here, we create a vertex array that contains buffers for two vertex array input variables as well as an index array:
n_points = 100000
self.vao = VertexArray(randn(n_points, 3), randn(n_points, 3))
Callback functions
Display function
Here we define the display function. It will be called by GLUT whenever the screen has to be redrawn.
def display(self):
First we clear the default framebuffer:
self.window.clear()
To draw the vertex array, we use:
self.vao.draw()
After all rendering commands have been issued, we swap the back buffer to the front, making the rendered image visible all at once:
self.window.swap_buffers()
Timer function
The animation is controlled by a GLUT timer. The timer callback changes the modelview matrix, schedules the next timer event, and causes a screen redraw:
def timer(self):
We first get the elapsed time from GLUT using get_elapsed_time()
:
phi = 2 * pi * get_elapsed_time() / 20.0
We then set the modelview_matrix
uniform variable of the
shader created in the initialization section simply by setting an
attribute:
self.shader.modelview_matrix = ((cos(phi), 0, sin(phi), 0), (0, 1, 0, 0), (-sin(phi), 0, cos(phi), 0), (0, 0, 0, 5))
The following line schedules the next timer event to execute after ten milliseconds.
self.window.add_timer(10, self.timer)
Finally, we tell GLUT to redraw the screen.
self.window.post_redisplay()
Running
We will call the run()
method later to run the OpenGL code.
def run(self):
To start the animation, we call the timer once; all subsequent timer calls will be scheduled by the timer function itself.
self.timer()
The default program is bound by using a with
statement:
with self.shader:
The State
class encapsulates state changes in the
context. For example, to enable depth testing and set the point size
to three for the duration of the following function call, we would
write:
with State(depth_test=True, point_size=3):
With the shader bound, depth testing enabled, and the point size set, we enter the GLUT main loop.
main_loop()
When the main loop exits, control is handed back to the script.
Main section
Finally, if this program is being run from the command line, we instanciate the main class and run it.
if __name__ == "__main__":
PointcloudRenderer().run()