class MinimalFramebufferExample(object): def __init__(self): self.window = GlutWindow(double=True, multisample=True, shape=(100, 800)) self.window.display_callback = self.display self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.shader.dimy, self.shader.dimx = self.window.shape self.fbo = Framebuffer( RectangleTexture(shape=self.window.shape + (3, ))) self.vao = VertexArray( ((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3))) def save(self, filename): self.fbo.clear() with self.shader: with self.fbo: self.vao.draw() imsave(filename, self.fbo[0].data) def display(self): self.window.clear() with self.shader: self.vao.draw() self.window.swap_buffers() def run(self): main_loop()
class MinimalFramebufferExample(object): def __init__(self): self.window = GlutWindow(double=True, multisample=True, shape=(100, 800)) self.window.display_callback = self.display self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.shader.dimy, self.shader.dimx = self.window.shape self.fbo = Framebuffer(RectangleTexture(shape=self.window.shape + (3,))) self.vao = VertexArray(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3))) def save(self, filename): self.fbo.clear() with self.shader: with self.fbo: self.vao.draw() imsave(filename, self.fbo[0].data) def display(self): self.window.clear() with self.shader: self.vao.draw() self.window.swap_buffers() def run(self): main_loop()
def __init__(self, volume, **kwargs): assert isinstance(volume, Texture3D), "volume must be a 3D texture" # We need a vertex array to hold the faces of the cube: self.vao = VertexArray(cube_vertices, elements=cube_indices) # For rendering the back faces, we create a shader program and a # framebuffer with an attached texture: self.back_shader = ShaderProgram(vertex=vertex_code, fragment=back_fragment_code) self.back_fbo = Framebuffer( RectangleTexture(shape=volume.shape[:2] + (4, ), dtype=float32)) # For the front faces, we do the same. Additionally, the shader # receives the texture containing the back face coordinates in a # uniform variable <code>back</code>, and the 3D texture containing the # volumetric data in a uniform variable <code>volume</code>. self.front_shader = ShaderProgram(vertex=vertex_code, fragment=front_fragment_code, back=self.back_fbo[0], volume=volume) self.front_fbo = Framebuffer( RectangleTexture(shape=volume.shape[:2] + (4, ), dtype=float32)) # All other parameters are simply set as attributes (this might be # <code>modelview_matrix</code>, <code>intensity_scale</code>, or # <code>absorption</code>). for key, value in list(kwargs.items()): setattr(self, key, value)
def __init__(self, filename): # 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 <code>defaultpipeline</code> from the # <code>glitter.convenience</code> module to create a shader program instead: self.shader = get_default_program() # We open the HDF5 file specified on the command line for reading: if filename.endswith(".dat"): data = loadtxt(filename) assert data.ndim == 2 and data.shape[1] in (3, 6) vertices = data[:, :3] vertices -= vertices.min() vertices /= vertices.max() vertices -= 0.5 colors = data[:, 3:] if data.shape[1] == 6 else None colors -= colors.min() colors /= colors.max() elements = None else: with h5py.File(filename, "r") as f: # The vertices, colors and indices of the mesh are read from the # corresponding datasets in the HDF5 file. Note that the names of the # datasets are mere convention. Colors and indices are allowed to be # undefined. vertices = f["vertices"] colors = f.get("colors", None) elements = f.get("indices", None) if vertices is not None: vertices = vertices.value if colors is not None: colors = colors.value if elements is not None: elements = elements.value # If no colors were specified, we generate random ones so we can # distinguish the triangles without fancy shading. if colors is None: colors = random((len(vertices), 3))[:, None, :][:, [0] * vertices.shape[1], :] # Here, we create a vertex array that contains buffers for two vertex array # input variables as well as an index array. If <code>elements</code> # is <code>None</code>, the vertex array class will draw all vertices # in order. self.vao = VertexArray(vertices, colors, elements=elements)
def __init__(self): # First, we create a window; this also creates an OpenGL context. self.window = GlutWindow(double=True, alpha=True, depth=True) # Then, we set the GLUT display and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # Here, we generate numpy arrays to hold the positions, colors, and # velocities of the particles: num = 200000 positions = numpy.empty((num, 4), dtype=numpy.float32) colors = numpy.empty((num, 4), dtype=numpy.float32) velocities = numpy.empty((num, 4), dtype=numpy.float32) # So far, the array contents are undefined. We have to initialize them with meaningful values: positions[:, 0] = numpy.sin(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample( (num, )) / 3 + 0.2) positions[:, 1] = numpy.cos(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample( (num, )) / 3 + 0.2) positions[:, 2:] = 0, 1 colors[:] = 0, 1, 0, 1 velocities[:, :2] = 2 * positions[:, :2] velocities[:, 2] = 3 velocities[:, 3] = numpy.random.random_sample((num, )) # Instead of simply generating a vertex array from the position and color # data, we first generate array buffers for them: gl_positions = ArrayBuffer(data=positions, usage="DYNAMIC_DRAW") gl_colors = ArrayBuffer(data=colors, usage="DYNAMIC_DRAW") # These array buffers will later also be used by OpenCL. We do not need to # wrap <code>velocities</code> in this way, as it will only be used by # OpenCL and can be wrapped in an OpenCL buffer directly. # We now create a vertex array that will pass the position and color data # to the shader. The vertex array constructor accepts # <code>ArrayBuffer</code> instances: self.vao = VertexArray(gl_positions, gl_colors) # In the OpenGL core profile, there is no such thing as a "standard pipeline" # any more. We use the minimalistic <code>defaultpipeline</code> from the # <code>glitter.convenience</code> module to create a shader program instead: self.shader = get_default_program() # Here, we create the <code>CLCode</code> object that manages OpenCL # interaction. It is passed the OpenGL buffer objects as well as a numpy # array of velocities. self.clcode = CLCode(gl_positions, gl_colors, velocities)
def __init__(self): self.window = GlutWindow(double=True, multisample=True, shape=(100, 800)) self.window.display_callback = self.display self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.shader.dimy, self.shader.dimx = self.window.shape self.fbo = Framebuffer( RectangleTexture(shape=self.window.shape + (3, ))) self.vao = VertexArray( ((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3)))
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 <code>defaultpipeline</code> from the # <code>glitter.convenience</code> 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))
def __init__(self, filename): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.shader = get_default_program() self.fbo = Framebuffer( depth=Texture2D(shape=self.window.shape + (1, ), depth=True)) self.copy_pipeline = get_copy_pipeline_2d(image=self.fbo.depth, use_framebuffer=False) with h5py.File(filename, "r") as f: vertices = f["vertices"] colors = f.get("colors", None) elements = f.get("indices", None) if colors is None: colors = random( (len(vertices), 3))[:, None, :][:, [0] * vertices.shape[1], :] self.vao = VertexArray(vertices, colors, elements=elements)
class DepthViewer(object): def __init__(self, filename): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.shader = get_default_program() self.fbo = Framebuffer( depth=Texture2D(shape=self.window.shape + (1, ), depth=True)) self.copy_pipeline = get_copy_pipeline_2d(image=self.fbo.depth, use_framebuffer=False) with h5py.File(filename, "r") as f: vertices = f["vertices"] colors = f.get("colors", None) elements = f.get("indices", None) if colors is None: colors = random( (len(vertices), 3))[:, None, :][:, [0] * vertices.shape[1], :] self.vao = VertexArray(vertices, colors, elements=elements) def display(self): try: with self.fbo: self.fbo.clear() self.vao.draw() self.window.clear() self.copy_pipeline.draw() self.window.swap_buffers() except Exception as e: import traceback traceback.print_exc() raise SystemExit(e) def timer(self): phi = 2 * pi * get_elapsed_time() / 20.0 self.shader.modelview_matrix = ((cos(phi), 0, sin(phi), 0), (0, 1, 0, 0), (-sin(phi), 0, cos(phi), 0), (0, 0, 0, 1)) self.window.add_timer(10, self.timer) self.window.post_redisplay() def run(self): self.timer() with self.shader: with State(depth_test=True): main_loop()
def __init__(self): self.window = GlutWindow(double=True, multisample=True, shape=(100, 800)) self.window.display_callback = self.display self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.shader.dimy, self.shader.dimx = self.window.shape self.fbo = Framebuffer(RectangleTexture(shape=self.window.shape + (3,))) self.vao = VertexArray(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3)))
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 and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # A shader program is built from the previously defined vertex and # fragment codes: self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) # This shader program is then used to build a pipeline. A pipeline is a # convenience object that encapsulates a vertex array for input, a # shader program for processing, and a framebuffer for output. The # framebuffer is optional (we could render directly to the screen if we # wanted), but we will render into a texture and copy the texture to # the screen for instructional purposes. The <code>Pipeline</code> # constructor automatically creates an empty vertex array and a # framebuffer with no attachments. Named constructor arguments are # interpreted as attributes of the vertex array and the framebuffer or # as named inputs and outputs of the shader. This means we can directly # pass in arrays of vertices and colors that will be bound to # <code>in_position</code> and <code>in_color</code>, respectivey, as # well as the array of element indices to draw and an empty texture to # bind to the framebuffer: self.render_pipeline = Pipeline(self.shader, in_position=vertices, in_color=colors, elements=indices, out_color=RectangleTexture(shape=(300, 300, 3))) # Shader uniform variables like textures can also be set directly on # the pipeline. Here we initialize two textures with random data: self.render_pipeline.texture_0 = Texture2D(random((30, 30, 4))) self.render_pipeline.texture_1 = RectangleTexture(random((30, 30, 4))) # Many properties, such as the filtering mode for textures, can # directly be set as attributes on the corresponding objects: self.render_pipeline.texture_0.min_filter = Texture2D.min_filters.NEAREST self.render_pipeline.texture_0.mag_filter = Texture2D.mag_filters.NEAREST # For copying the texture to the screen, we create another shader # program. self.copy_shader = ShaderProgram(vertex=copy_vertex_shader, fragment=copy_fragment_shader) # The input texture of this shader program is the output texture of the # previous pipeline. Since all textures and framebuffers are # automatically bound and unbound, we do not need to worry about # whether the framebuffer is still writing to the texture. self.copy_shader.image = self.render_pipeline.out_color # Instead of using a pipeline with named vertex shader inputs, we can # also create a vertex array object directly with a list of vertex # shader inputs to use. Here we use only a single vertex shader input: # the coordinates of a fullscreen quad. The <code>elements</code> # parameter defines two triangles that make up the quad. self.vao = VertexArray(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3)))
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 # We also set mouse callbacks that will display the pixel color in the # title bar. self.window.mouse_callback = self.mouse self.window.motion_callback = self.motion # In the OpenGL core profile, there is no such thing as a "standard pipeline" # any more. We use the minimalistic <code>defaultpipeline</code> from the # <code>glitter.convenience</code> 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: self.vao = VertexArray(vertices, colors, elements=indices)
class IntroductionExample(object): def __init__(self): self.window = GlutWindow(double=True, multisample=True, shape=(600, 800)) self.window.display_callback = self.display self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.copy_shader = ShaderProgram(vertex=copy_vertex_shader, fragment=copy_fragment_shader) self.copy_shader.image = RectangleTexture(shape=self.window.shape + (3,)) self.fbo = Framebuffer(self.copy_shader.image) self.vao = VertexArray(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3))) def display(self, use_texture=False): self.window.clear() if use_texture: self.fbo.clear() with self.fbo: with self.shader: self.vao.draw() with self.copy_shader: self.vao.draw() else: with self.shader: self.vao.draw() self.window.swap_buffers() def run(self): main_loop()
def __init__(self, filename): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.shader = get_default_program() self.fbo = Framebuffer(depth=Texture2D(shape=self.window.shape + (1,), depth=True)) self.copy_pipeline = get_copy_pipeline_2d(image=self.fbo.depth, use_framebuffer=False) with h5py.File(filename, "r") as f: vertices = f["vertices"] colors = f.get("colors", None) elements = f.get("indices", None) if colors is None: colors = random((len(vertices), 3))[:, None, :][:, [0] * vertices.shape[1], :] self.vao = VertexArray(vertices, colors, elements=elements)
class DepthViewer(object): def __init__(self, filename): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.shader = get_default_program() self.fbo = Framebuffer(depth=Texture2D(shape=self.window.shape + (1,), depth=True)) self.copy_pipeline = get_copy_pipeline_2d(image=self.fbo.depth, use_framebuffer=False) with h5py.File(filename, "r") as f: vertices = f["vertices"] colors = f.get("colors", None) elements = f.get("indices", None) if colors is None: colors = random((len(vertices), 3))[:, None, :][:, [0] * vertices.shape[1], :] self.vao = VertexArray(vertices, colors, elements=elements) def display(self): try: with self.fbo: self.fbo.clear() self.vao.draw() self.window.clear() self.copy_pipeline.draw() self.window.swap_buffers() except Exception as e: import traceback traceback.print_exc() raise SystemExit(e) def timer(self): phi = 2 * pi * get_elapsed_time() / 20.0 self.shader.modelview_matrix = ((cos(phi), 0, sin(phi), 0), (0, 1, 0, 0), (-sin(phi), 0, cos(phi), 0), (0, 0, 0, 1)) self.window.add_timer(10, self.timer) self.window.post_redisplay() def run(self): self.timer() with self.shader: with State(depth_test=True): main_loop()
def __init__(self): # First, we create a window; this also creates an OpenGL context. self.window = GlutWindow(double=True, alpha=True, depth=True) # Then, we set the GLUT display and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # Here, we generate numpy arrays to hold the positions, colors, and # velocities of the particles: num = 200000 positions = numpy.empty((num, 4), dtype=numpy.float32) colors = numpy.empty((num, 4), dtype=numpy.float32) velocities = numpy.empty((num, 4), dtype=numpy.float32) # So far, the array contents are undefined. We have to initialize them with meaningful values: positions[:, 0] = numpy.sin(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample((num,)) / 3 + 0.2) positions[:, 1] = numpy.cos(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample((num,)) / 3 + 0.2) positions[:, 2:] = 0, 1 colors[:] = 0, 1, 0, 1 velocities[:, :2] = 2 * positions[:, :2] velocities[:, 2] = 3 velocities[:, 3] = numpy.random.random_sample((num,)) # Instead of simply generating a vertex array from the position and color # data, we first generate array buffers for them: gl_positions = ArrayBuffer(data=positions, usage="DYNAMIC_DRAW") gl_colors = ArrayBuffer(data=colors, usage="DYNAMIC_DRAW") # These array buffers will later also be used by OpenCL. We do not need to # wrap <code>velocities</code> in this way, as it will only be used by # OpenCL and can be wrapped in an OpenCL buffer directly. # We now create a vertex array that will pass the position and color data # to the shader. The vertex array constructor accepts # <code>ArrayBuffer</code> instances: self.vao = VertexArray(gl_positions, gl_colors) # In the OpenGL core profile, there is no such thing as a "standard pipeline" # any more. We use the minimalistic <code>defaultpipeline</code> from the # <code>glitter.convenience</code> module to create a shader program instead: self.shader = get_default_program() # Here, we create the <code>CLCode</code> object that manages OpenCL # interaction. It is passed the OpenGL buffer objects as well as a numpy # array of velocities. self.clcode = CLCode(gl_positions, gl_colors, velocities)
class IntroductionExample(object): def __init__(self): self.window = GlutWindow(double=True, multisample=True, shape=(600, 800)) self.window.display_callback = self.display self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.copy_shader = ShaderProgram(vertex=copy_vertex_shader, fragment=copy_fragment_shader) self.copy_shader.image = RectangleTexture(shape=self.window.shape + (3, )) self.fbo = Framebuffer(self.copy_shader.image) self.vao = VertexArray( ((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3))) def display(self, use_texture=False): self.window.clear() if use_texture: self.fbo.clear() with self.fbo: with self.shader: self.vao.draw() with self.copy_shader: self.vao.draw() else: with self.shader: self.vao.draw() self.window.swap_buffers() def run(self): main_loop()
class IntroductionExample(object): # <h3>Initialization</h3> # When an <code>IntroductionExample</code> 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 and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # A shader program is built from the previously defined vertex and # fragment codes: self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) # This shader program is then used to build a pipeline. A pipeline is a # convenience object that encapsulates a vertex array for input, a # shader program for processing, and a framebuffer for output. The # framebuffer is optional (we could render directly to the screen if we # wanted), but we will render into a texture and copy the texture to # the screen for instructional purposes. The <code>Pipeline</code> # constructor automatically creates an empty vertex array and a # framebuffer with no attachments. Named constructor arguments are # interpreted as attributes of the vertex array and the framebuffer or # as named inputs and outputs of the shader. This means we can directly # pass in arrays of vertices and colors that will be bound to # <code>in_position</code> and <code>in_color</code>, respectivey, as # well as the array of element indices to draw and an empty texture to # bind to the framebuffer: self.render_pipeline = Pipeline(self.shader, in_position=vertices, in_color=colors, elements=indices, out_color=RectangleTexture(shape=(300, 300, 3))) # Shader uniform variables like textures can also be set directly on # the pipeline. Here we initialize two textures with random data: self.render_pipeline.texture_0 = Texture2D(random((30, 30, 4))) self.render_pipeline.texture_1 = RectangleTexture(random((30, 30, 4))) # Many properties, such as the filtering mode for textures, can # directly be set as attributes on the corresponding objects: self.render_pipeline.texture_0.min_filter = Texture2D.min_filters.NEAREST self.render_pipeline.texture_0.mag_filter = Texture2D.mag_filters.NEAREST # For copying the texture to the screen, we create another shader # program. self.copy_shader = ShaderProgram(vertex=copy_vertex_shader, fragment=copy_fragment_shader) # The input texture of this shader program is the output texture of the # previous pipeline. Since all textures and framebuffers are # automatically bound and unbound, we do not need to worry about # whether the framebuffer is still writing to the texture. self.copy_shader.image = self.render_pipeline.out_color # Instead of using a pipeline with named vertex shader inputs, we can # also create a vertex array object directly with a list of vertex # shader inputs to use. Here we use only a single vertex shader input: # the coordinates of a fullscreen quad. The <code>elements</code> # parameter defines two triangles that make up the quad. self.vao = VertexArray(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3))) # <h3>Callback functions</h3> # <h4>Display function</h4> # Here we define the display function. It will be called by GLUT whenever # the screen has to be redrawn. def display(self): # We can simply clear the pipeline and render the vertices into the # framebuffer using the shader with the following two lines: self.render_pipeline.clear() self.render_pipeline.draw() # For output on the screen, we have created a GLUT window. It can be # cleared in very much the same way: self.window.clear() # To copy the results of the pipeline to the screen, we use the shader # that simply displays a texture. The shader can be bound by using a # <code>with</code> statement: with self.copy_shader: # All textures used by the shader are then bound automatically, and # everything is reset to its previous state when we leave the # <code>with</code> block. # With the shader bound, we simply draw a fullscreen quad that is # stored in a vertex array we will create in the initialization # section: 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() # Finally, we disable logging so that we only see the OpenGL calls of # the first run of the display function: add_logger(None) # <h4>Keyboard function</h4> # To further illustrate the concept of GLUT callbacks, here's a keyboard # handler that will simply make the program exit when any key is pressed: def keyboard(self, key, x, y): raise SystemExit # <h4>Timer function</h4> # 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 # <code>get_elapsed_time()</code>: t = get_elapsed_time() phi = 2 * pi * t / 4.0 # We then set the <code>modelview_matrix</code> uniform variable of the # shader created in the initialization section simply by setting an # attribute: self.shader.modelview_matrix = ((cos(phi), sin(phi), 0, 0), (-sin(phi), cos(phi), 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) # 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() # <h3>Running</h3> # We will call the <code>run()</code> 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() # Now that all the initialization is done, we add the default logger to # all OpenGL commands so that we can see what OpenGL the display # function issues, and in which order. add_logger() # Finally, to start rendering, we enter the GLUT main loop. main_loop()
# an HDF5 output file. An optional third parameter specifies the size of the # resulting volume. if __name__ == "__main__": # We need to read a mesh filename from <code>sys.argv</code>, so import # <code>sys</code>. import sys # We assume the mesh is stored in a <a # href="http://www.hdfgroup.org/HDF5/">HDF5</a> file, so import <a # href="h5py.alfven.org"><code>h5py</code></a>. import h5py # We open the HDF5 file specified on the command line for reading, extract # the vertices and indices, and store them in a vertex array: with h5py.File(sys.argv[1]) as f: mesh = VertexArray(f["vertices"], elements=f["indices"]) # Now the voxelization is performed and the result is returned as an array # of 2D integer textures. volume = voxelize(mesh, int(sys.argv[3]) if len(sys.argv) > 3 else 128) # Finally, we open the output file, download the data, and store it in a # dataset. The dataset can typically be compressed a lot, which is why we # enable LZF compression. However, this may take a long time, so you can # disable it if you are not short on disk space. with h5py.File(sys.argv[2], "w") as f: f.create_dataset("data", data=volume.data, compression="lzf") # Note how we did not need to create an OpenGL context explicitly. The # first object that needs an OpenGL context (in this case the # <code>VertexArray</code>) will create an invisible context as soon as it
class OpenCLExample(object): # <h3>Initialization</h3> # When a <code>OpenCLExample</code> 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, alpha=True, depth=True) # Then, we set the GLUT display and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # Here, we generate numpy arrays to hold the positions, colors, and # velocities of the particles: num = 200000 positions = numpy.empty((num, 4), dtype=numpy.float32) colors = numpy.empty((num, 4), dtype=numpy.float32) velocities = numpy.empty((num, 4), dtype=numpy.float32) # So far, the array contents are undefined. We have to initialize them with meaningful values: positions[:, 0] = numpy.sin(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample((num,)) / 3 + 0.2) positions[:, 1] = numpy.cos(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample((num,)) / 3 + 0.2) positions[:, 2:] = 0, 1 colors[:] = 0, 1, 0, 1 velocities[:, :2] = 2 * positions[:, :2] velocities[:, 2] = 3 velocities[:, 3] = numpy.random.random_sample((num,)) # Instead of simply generating a vertex array from the position and color # data, we first generate array buffers for them: gl_positions = ArrayBuffer(data=positions, usage="DYNAMIC_DRAW") gl_colors = ArrayBuffer(data=colors, usage="DYNAMIC_DRAW") # These array buffers will later also be used by OpenCL. We do not need to # wrap <code>velocities</code> in this way, as it will only be used by # OpenCL and can be wrapped in an OpenCL buffer directly. # We now create a vertex array that will pass the position and color data # to the shader. The vertex array constructor accepts # <code>ArrayBuffer</code> instances: self.vao = VertexArray(gl_positions, gl_colors) # In the OpenGL core profile, there is no such thing as a "standard pipeline" # any more. We use the minimalistic <code>defaultpipeline</code> from the # <code>glitter.convenience</code> module to create a shader program instead: self.shader = get_default_program() # Here, we create the <code>CLCode</code> object that manages OpenCL # interaction. It is passed the OpenGL buffer objects as well as a numpy # array of velocities. self.clcode = CLCode(gl_positions, gl_colors, velocities) # <h3>Callback functions</h3> # <h4>Display function</h4> # 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() # <h4>Timer function</h4> # The animation is controlled by a GLUT timer. The timer callback animates the # particle system, schedules the next timer event, and causes a screen redraw: def timer(self): # We first tell an instance of the <code>CLCode</code> class to execute the # OpenCL kernel: self.clcode.execute(10) # The following line schedules the next timer event to execute after one millisecond. self.window.add_timer(1, self.timer) # Finally, we tell GLUT to redraw the screen. self.window.post_redisplay() # <h4>Keyboard function</h4> # To further illustrate the concept of GLUT callbacks, here's a keyboard # handler that will simply make the program exit when any key is pressed: def keyboard(self, key, x, y): raise SystemExit # <h3>Running</h3> # We will call the <code>run()</code> 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 <code>with</code> statement. At # the same time, we can pass in additional uniform variables, such as the # modelview matrix: with self.shader(modelview_matrix=((1, 0, 0, 0), (0, 0, 1, 0), (0, 1, 0, 0), (0, 0, 0, 2))): # With the shader bound, we enter the GLUT main loop. main_loop()
class SimpleExample(object): # <h3>Initialization</h3> # When a <code>SimpleExample</code> 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 # We also set mouse callbacks that will display the pixel color in the # title bar. self.window.mouse_callback = self.mouse self.window.motion_callback = self.motion # In the OpenGL core profile, there is no such thing as a "standard pipeline" # any more. We use the minimalistic <code>defaultpipeline</code> from the # <code>glitter.convenience</code> 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: self.vao = VertexArray(vertices, colors, elements=indices) # <h3>Callback functions</h3> # <h4>Display function</h4> # 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() # <h4>Mouse functions</h4> # The motion callback is called when the mouse is moved while a button is # pressed. It reads the current color of the pixel under the cursor from # the front buffer and displays it in the window title. def motion(self, x, y): self.window.window_title = "%.2f, %.2f, %.2f, %.2f" % tuple( self.window.front_pixels[self.window.shape[0] - y, x]) # The mouse callback is called whenever a mouse button is pressed. It # redirects to the functionality of the motion callback. def mouse(self, button, state, x, y): self.motion(x, y) # <h4>Timer function</h4> # 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 <code>get_elapsed_time()</code>: phi = get_elapsed_time() # We then set the <code>modelview_matrix</code> uniform variable of the # shader created in the initialization section simply by setting an # attribute: self.shader.modelview_matrix = ((cos(phi), sin(phi), 0, 0), (-sin(phi), cos(phi), 0, 0), (0, 0, 1, 0), (0, 0, 0, 2)) # 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() # <h3>Running</h3> # We will call the <code>run()</code> 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 <code>with</code> statement: with self.shader: # With the shader bound, we enter the GLUT main loop. main_loop()
class MeshViewer(object): # <h3>Initialization</h3> # When a <code>MeshViewer</code> instance is created, we need to # initialize a few OpenGL objects. def __init__(self, filename): # 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 <code>defaultpipeline</code> from the # <code>glitter.convenience</code> module to create a shader program instead: self.shader = get_default_program() # We open the HDF5 file specified on the command line for reading: if filename.endswith(".dat"): data = loadtxt(filename) assert data.ndim == 2 and data.shape[1] in (3, 6) vertices = data[:, :3] vertices -= vertices.min() vertices /= vertices.max() vertices -= 0.5 colors = data[:, 3:] if data.shape[1] == 6 else None colors -= colors.min() colors /= colors.max() elements = None else: with h5py.File(filename, "r") as f: # The vertices, colors and indices of the mesh are read from the # corresponding datasets in the HDF5 file. Note that the names of the # datasets are mere convention. Colors and indices are allowed to be # undefined. vertices = f["vertices"] colors = f.get("colors", None) elements = f.get("indices", None) if vertices is not None: vertices = vertices.value if colors is not None: colors = colors.value if elements is not None: elements = elements.value # If no colors were specified, we generate random ones so we can # distinguish the triangles without fancy shading. if colors is None: colors = random((len(vertices), 3))[:, None, :][:, [0] * vertices.shape[1], :] # Here, we create a vertex array that contains buffers for two vertex array # input variables as well as an index array. If <code>elements</code> # is <code>None</code>, the vertex array class will draw all vertices # in order. self.vao = VertexArray(vertices, colors, elements=elements) # <h3>Callback functions</h3> # <h4>Display function</h4> # 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() # <h4>Timer function</h4> # 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 <code>get_elapsed_time()</code>: phi = 2 * pi * get_elapsed_time() / 20.0 # We then set the <code>modelview_matrix</code> uniform variable of the # shader 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, 1)) # 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() # <h3>Running</h3> # We will call the <code>run()</code> 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 shader program is bound by using a <code>with</code> statement: with self.shader: # The <code>State</code> class encapsulates state changes in the # context. For example, to enable depth testing for the duration of the # following function call, we would write: with State(depth_test=True): # With the shader bound and depth testing enabled, we enter the # GLUT main loop. main_loop()
class PointcloudRenderer(object): # <h3>Initialization</h3> # When a <code>PointcloudRenderer</code> 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 <code>defaultpipeline</code> from the # <code>glitter.convenience</code> 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)) # <h3>Callback functions</h3> # <h4>Display function</h4> # 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() # <h4>Timer function</h4> # 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 <code>get_elapsed_time()</code>: phi = 2 * pi * get_elapsed_time() / 20.0 # We then set the <code>modelview_matrix</code> 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() # <h3>Running</h3> # We will call the <code>run()</code> 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 <code>with</code> statement: with self.shader: # The <code>State</code> 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()
window.swap_buffers() def keyboard(key, x, y): # if F1 or ALT-Enter is pressed, toggle fullscreen if key == _gl.GLUT_KEY_F1 or (get_alt_state() and key == ord('\r')): window.toggle_full_screen() elif key == 27: # escape key raise SystemExit # makes program quit out of main_loop if __name__ == "__main__": # create main window window = GlutWindow(name="My First Quad and Triangle", double=True) # set background color to black window.color_clear_value = (0, 0, 0, 1) # callback for rendering window.display_callback = display # callback for normal keys and special keys (like F1), both handled by same function here window.keyboard_callback = window.special_callback = keyboard # render all the time window.idle_callback = window.post_redisplay # shader program for rendering shader = get_default_program(modelview=False, color=False) quad = VertexArray(((-0.7, 0.3, 0.0, 1.0), (-0.1, 0.3, 0.0, 1.0), (-0.7, -0.3, 0.0, 1.0), (-0.1, -0.3, 0.0, 1.0))) triangle = VertexArray( ((0.1, -0.2, 0.0, 1.0), (0.7, -0.2, 0.0, 1.0), (0.4, 0.2, 0.0, 1.0))) # loops until SystemExit is raised or window is closed main_loop()
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 and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # A shader program is built from the previously defined vertex and # fragment codes: self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) # This shader program is then used to build a pipeline. A pipeline is a # convenience object that encapsulates a vertex array for input, a # shader program for processing, and a framebuffer for output. The # framebuffer is optional (we could render directly to the screen if we # wanted), but we will render into a texture and copy the texture to # the screen for instructional purposes. The <code>Pipeline</code> # constructor automatically creates an empty vertex array and a # framebuffer with no attachments. Named constructor arguments are # interpreted as attributes of the vertex array and the framebuffer or # as named inputs and outputs of the shader. This means we can directly # pass in arrays of vertices and colors that will be bound to # <code>in_position</code> and <code>in_color</code>, respectivey, as # well as the array of element indices to draw and an empty texture to # bind to the framebuffer: self.render_pipeline = Pipeline(self.shader, in_position=vertices, in_color=colors, elements=indices, out_color=RectangleTexture(shape=(300, 300, 3))) # Shader uniform variables like textures can also be set directly on # the pipeline. Here we initialize two textures with random data: self.render_pipeline.texture_0 = Texture2D(random((30, 30, 4))) self.render_pipeline.texture_1 = RectangleTexture(random((30, 30, 4))) # Many properties, such as the filtering mode for textures, can # directly be set as attributes on the corresponding objects: self.render_pipeline.texture_0.min_filter = Texture2D.min_filters.NEAREST self.render_pipeline.texture_0.mag_filter = Texture2D.mag_filters.NEAREST # For copying the texture to the screen, we create another shader # program. self.copy_shader = ShaderProgram(vertex=copy_vertex_shader, fragment=copy_fragment_shader) # The input texture of this shader program is the output texture of the # previous pipeline. Since all textures and framebuffers are # automatically bound and unbound, we do not need to worry about # whether the framebuffer is still writing to the texture. self.copy_shader.image = self.render_pipeline.out_color # Instead of using a pipeline with named vertex shader inputs, we can # also create a vertex array object directly with a list of vertex # shader inputs to use. Here we use only a single vertex shader input: # the coordinates of a fullscreen quad. The <code>elements</code> # parameter defines two triangles that make up the quad. self.vao = VertexArray( ((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3)))
class IntroductionExample(object): # <h3>Initialization</h3> # When an <code>IntroductionExample</code> 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 and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # A shader program is built from the previously defined vertex and # fragment codes: self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) # This shader program is then used to build a pipeline. A pipeline is a # convenience object that encapsulates a vertex array for input, a # shader program for processing, and a framebuffer for output. The # framebuffer is optional (we could render directly to the screen if we # wanted), but we will render into a texture and copy the texture to # the screen for instructional purposes. The <code>Pipeline</code> # constructor automatically creates an empty vertex array and a # framebuffer with no attachments. Named constructor arguments are # interpreted as attributes of the vertex array and the framebuffer or # as named inputs and outputs of the shader. This means we can directly # pass in arrays of vertices and colors that will be bound to # <code>in_position</code> and <code>in_color</code>, respectivey, as # well as the array of element indices to draw and an empty texture to # bind to the framebuffer: self.render_pipeline = Pipeline(self.shader, in_position=vertices, in_color=colors, elements=indices, out_color=RectangleTexture(shape=(300, 300, 3))) # Shader uniform variables like textures can also be set directly on # the pipeline. Here we initialize two textures with random data: self.render_pipeline.texture_0 = Texture2D(random((30, 30, 4))) self.render_pipeline.texture_1 = RectangleTexture(random((30, 30, 4))) # Many properties, such as the filtering mode for textures, can # directly be set as attributes on the corresponding objects: self.render_pipeline.texture_0.min_filter = Texture2D.min_filters.NEAREST self.render_pipeline.texture_0.mag_filter = Texture2D.mag_filters.NEAREST # For copying the texture to the screen, we create another shader # program. self.copy_shader = ShaderProgram(vertex=copy_vertex_shader, fragment=copy_fragment_shader) # The input texture of this shader program is the output texture of the # previous pipeline. Since all textures and framebuffers are # automatically bound and unbound, we do not need to worry about # whether the framebuffer is still writing to the texture. self.copy_shader.image = self.render_pipeline.out_color # Instead of using a pipeline with named vertex shader inputs, we can # also create a vertex array object directly with a list of vertex # shader inputs to use. Here we use only a single vertex shader input: # the coordinates of a fullscreen quad. The <code>elements</code> # parameter defines two triangles that make up the quad. self.vao = VertexArray( ((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)), elements=((0, 1, 2), (0, 2, 3))) # <h3>Callback functions</h3> # <h4>Display function</h4> # Here we define the display function. It will be called by GLUT whenever # the screen has to be redrawn. def display(self): # We can simply clear the pipeline and render the vertices into the # framebuffer using the shader with the following two lines: self.render_pipeline.clear() self.render_pipeline.draw() # For output on the screen, we have created a GLUT window. It can be # cleared in very much the same way: self.window.clear() # To copy the results of the pipeline to the screen, we use the shader # that simply displays a texture. The shader can be bound by using a # <code>with</code> statement: with self.copy_shader: # All textures used by the shader are then bound automatically, and # everything is reset to its previous state when we leave the # <code>with</code> block. # With the shader bound, we simply draw a fullscreen quad that is # stored in a vertex array we will create in the initialization # section: 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() # Finally, we disable logging so that we only see the OpenGL calls of # the first run of the display function: add_logger(None) # <h4>Keyboard function</h4> # To further illustrate the concept of GLUT callbacks, here's a keyboard # handler that will simply make the program exit when any key is pressed: def keyboard(self, key, x, y): raise SystemExit # <h4>Timer function</h4> # 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 # <code>get_elapsed_time()</code>: t = get_elapsed_time() phi = 2 * pi * t / 4.0 # We then set the <code>modelview_matrix</code> uniform variable of the # shader created in the initialization section simply by setting an # attribute: self.shader.modelview_matrix = ((cos(phi), sin(phi), 0, 0), (-sin(phi), cos(phi), 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) # 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() # <h3>Running</h3> # We will call the <code>run()</code> 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() # Now that all the initialization is done, we add the default logger to # all OpenGL commands so that we can see what OpenGL the display # function issues, and in which order. add_logger() # Finally, to start rendering, we enter the GLUT main loop. main_loop()
class SimpleExample(object): # <h3>Initialization</h3> # When a <code>SimpleExample</code> 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 # We also set mouse callbacks that will display the pixel color in the # title bar. self.window.mouse_callback = self.mouse self.window.motion_callback = self.motion # In the OpenGL core profile, there is no such thing as a "standard pipeline" # any more. We use the minimalistic <code>defaultpipeline</code> from the # <code>glitter.convenience</code> 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: self.vao = VertexArray(vertices, colors, elements=indices) # <h3>Callback functions</h3> # <h4>Display function</h4> # 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() # <h4>Mouse functions</h4> # The motion callback is called when the mouse is moved while a button is # pressed. It reads the current color of the pixel under the cursor from # the front buffer and displays it in the window title. def motion(self, x, y): self.window.window_title = "%.2f, %.2f, %.2f, %.2f" % tuple(self.window.front_pixels[self.window.shape[0] - y, x]) # The mouse callback is called whenever a mouse button is pressed. It # redirects to the functionality of the motion callback. def mouse(self, button, state, x, y): self.motion(x, y) # <h4>Timer function</h4> # 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 <code>get_elapsed_time()</code>: phi = get_elapsed_time() # We then set the <code>modelview_matrix</code> uniform variable of the # shader created in the initialization section simply by setting an # attribute: self.shader.modelview_matrix = ((cos(phi), sin(phi), 0, 0), (-sin(phi), cos(phi), 0, 0), (0, 0, 1, 0), (0, 0, 0, 2)) # 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() # <h3>Running</h3> # We will call the <code>run()</code> 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 <code>with</code> statement: with self.shader: # With the shader bound, we enter the GLUT main loop. main_loop()
class OpenCLExample(object): # <h3>Initialization</h3> # When a <code>OpenCLExample</code> 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, alpha=True, depth=True) # Then, we set the GLUT display and keyboard callback functions which # will be defined later. self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard # Here, we generate numpy arrays to hold the positions, colors, and # velocities of the particles: num = 200000 positions = numpy.empty((num, 4), dtype=numpy.float32) colors = numpy.empty((num, 4), dtype=numpy.float32) velocities = numpy.empty((num, 4), dtype=numpy.float32) # So far, the array contents are undefined. We have to initialize them with meaningful values: positions[:, 0] = numpy.sin(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample( (num, )) / 3 + 0.2) positions[:, 1] = numpy.cos(numpy.arange(0, num) * 2 * numpy.pi / num) * (numpy.random.random_sample( (num, )) / 3 + 0.2) positions[:, 2:] = 0, 1 colors[:] = 0, 1, 0, 1 velocities[:, :2] = 2 * positions[:, :2] velocities[:, 2] = 3 velocities[:, 3] = numpy.random.random_sample((num, )) # Instead of simply generating a vertex array from the position and color # data, we first generate array buffers for them: gl_positions = ArrayBuffer(data=positions, usage="DYNAMIC_DRAW") gl_colors = ArrayBuffer(data=colors, usage="DYNAMIC_DRAW") # These array buffers will later also be used by OpenCL. We do not need to # wrap <code>velocities</code> in this way, as it will only be used by # OpenCL and can be wrapped in an OpenCL buffer directly. # We now create a vertex array that will pass the position and color data # to the shader. The vertex array constructor accepts # <code>ArrayBuffer</code> instances: self.vao = VertexArray(gl_positions, gl_colors) # In the OpenGL core profile, there is no such thing as a "standard pipeline" # any more. We use the minimalistic <code>defaultpipeline</code> from the # <code>glitter.convenience</code> module to create a shader program instead: self.shader = get_default_program() # Here, we create the <code>CLCode</code> object that manages OpenCL # interaction. It is passed the OpenGL buffer objects as well as a numpy # array of velocities. self.clcode = CLCode(gl_positions, gl_colors, velocities) # <h3>Callback functions</h3> # <h4>Display function</h4> # 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() # <h4>Timer function</h4> # The animation is controlled by a GLUT timer. The timer callback animates the # particle system, schedules the next timer event, and causes a screen redraw: def timer(self): # We first tell an instance of the <code>CLCode</code> class to execute the # OpenCL kernel: self.clcode.execute(10) # The following line schedules the next timer event to execute after one millisecond. self.window.add_timer(1, self.timer) # Finally, we tell GLUT to redraw the screen. self.window.post_redisplay() # <h4>Keyboard function</h4> # To further illustrate the concept of GLUT callbacks, here's a keyboard # handler that will simply make the program exit when any key is pressed: def keyboard(self, key, x, y): raise SystemExit # <h3>Running</h3> # We will call the <code>run()</code> 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 <code>with</code> statement. At # the same time, we can pass in additional uniform variables, such as the # modelview matrix: with self.shader(modelview_matrix=((1, 0, 0, 0), (0, 0, 1, 0), (0, 1, 0, 0), (0, 0, 0, 2))): # With the shader bound, we enter the GLUT main loop. main_loop()
class VolumeRenderer(object): # Some default values for instance variables can be declared as class # variables (these values are all immutable, so no problem here): modelview_matrix = tuple(map(tuple, numpy.eye(4))) intensity_scale = 1.0 absorption = False # <h3>Initialization</h3> def __init__(self, volume, **kwargs): assert isinstance(volume, Texture3D), "volume must be a 3D texture" # We need a vertex array to hold the faces of the cube: self.vao = VertexArray(cube_vertices, elements=cube_indices) # For rendering the back faces, we create a shader program and a # framebuffer with an attached texture: self.back_shader = ShaderProgram(vertex=vertex_code, fragment=back_fragment_code) self.back_fbo = Framebuffer( RectangleTexture(shape=volume.shape[:2] + (4, ), dtype=float32)) # For the front faces, we do the same. Additionally, the shader # receives the texture containing the back face coordinates in a # uniform variable <code>back</code>, and the 3D texture containing the # volumetric data in a uniform variable <code>volume</code>. self.front_shader = ShaderProgram(vertex=vertex_code, fragment=front_fragment_code, back=self.back_fbo[0], volume=volume) self.front_fbo = Framebuffer( RectangleTexture(shape=volume.shape[:2] + (4, ), dtype=float32)) # All other parameters are simply set as attributes (this might be # <code>modelview_matrix</code>, <code>intensity_scale</code>, or # <code>absorption</code>). for key, value in list(kwargs.items()): setattr(self, key, value) # <h3>Rendering</h3> def render(self): # We first draw the back faces, storing their coordinates into a # texture. This means we have to clear and bind the back framebuffer, # activate face culling with cull face mode "FRONT", bind the back face # shader with the current modelview matrix set, and render the vertex # array for the cube: self.back_fbo.clear() with State(cull_face=True, cull_face_mode="FRONT"): with self.back_shader(modelview_matrix=self.modelview_matrix): with self.back_fbo: self.vao.draw() # We then draw the front faces, accumulating the intensity between # front and back face. The setup is very similar to the one stated # before, except that the additional uniform variables # <code>intensity_scale</code> and <code>absorption</code> are passed # to the shader: self.front_fbo.clear() with State(cull_face=True, cull_face_mode="BACK"): with self.front_shader(modelview_matrix=self.modelview_matrix, intensity_scale=self.intensity_scale, absorption=self.absorption): with self.front_fbo: self.vao.draw() # Finally, we simply return the texture into which we just rendered. # The client code might want to display this directly, or to download # the data into a numpy array. return self.front_fbo[0]