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 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): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.window.mouse_callback = self.mouse self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.shader.colormap = Texture1D(cm.spectral(linspace(0, 1, 256)), wrap_s="MIRRORED_REPEAT") self.shader.minval = (-2.5, -1.75) self.shader.maxval = (1.0, 1.75) self.vao = get_fullscreen_quad() self.history = []
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)))
class MandelbrotRenderer(object): transition_time = 0.3 # seconds update_step = 10 # milliseconds def __init__(self): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.window.mouse_callback = self.mouse self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.shader.colormap = Texture1D(cm.spectral(linspace(0, 1, 256)), wrap_s="MIRRORED_REPEAT") self.shader.minval = (-2.5, -1.75) self.shader.maxval = (1.0, 1.75) self.vao = get_fullscreen_quad() self.history = [] def display(self): self.window.clear() self.vao.draw() self.window.swap_buffers() def timer(self): t = min(1.0, (get_elapsed_time() - self.transition_start) / self.transition_time) x = 0.5 - 0.5 * cos(pi * t) self.shader.minval = self.minstart * (1 - x) + self.minend * x self.shader.maxval = self.maxstart * (1 - x) + self.maxend * x if t < 1: self.window.add_timer(self.update_step, self.timer) self.window.post_redisplay() def start_transition(self, minval, maxval): self.minstart, self.maxstart = self.shader.minval, self.shader.maxval self.minend, self.maxend = minval, maxval self.transition_start = get_elapsed_time() self.timer() def mouse(self, button, state, x, y): if button == glut.GLUT_LEFT_BUTTON: pos = array( (x, self.window.shape[0] - y)) / array(self.window.shape[::-1], dtype=float) pos = pos * (self.shader.maxval - self.shader.minval) + self.shader.minval if state == glut.GLUT_DOWN: self.last_pos = pos elif state == glut.GLUT_UP and all(pos != self.last_pos): self.history.append((self.shader.minval, self.shader.maxval)) self.start_transition(minimum(pos, self.last_pos), maximum(pos, self.last_pos)) elif state == glut.GLUT_DOWN and button == glut.GLUT_RIGHT_BUTTON: if self.history: self.start_transition(*self.history.pop()) def run(self): with self.shader: 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 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, images): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard self.window.special_callback = self.special self.images = images self.window.shape = self.images[0].shape[:2] self.render_pipeline = get_copy_pipeline_2d( context=self.window, sampler_type="isampler2D", maxval=255, yscale=-1, use_framebuffer=False, image=Texture2D(self._prepare_image(0))) self.current_index = 0 self.fps = 25 self.playing = False
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 __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)))
class MandelbrotRenderer(object): transition_time = 0.3 # seconds update_step = 10 # milliseconds def __init__(self): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.window.mouse_callback = self.mouse self.shader = ShaderProgram(vertex=vertex_shader, fragment=fragment_shader) self.shader.colormap = Texture1D(cm.spectral(linspace(0, 1, 256)), wrap_s="MIRRORED_REPEAT") self.shader.minval = (-2.5, -1.75) self.shader.maxval = (1.0, 1.75) self.vao = get_fullscreen_quad() self.history = [] def display(self): self.window.clear() self.vao.draw() self.window.swap_buffers() def timer(self): t = min(1.0, (get_elapsed_time() - self.transition_start) / self.transition_time) x = 0.5 - 0.5 * cos(pi * t) self.shader.minval = self.minstart * (1 - x) + self.minend * x self.shader.maxval = self.maxstart * (1 - x) + self.maxend * x if t < 1: self.window.add_timer(self.update_step, self.timer) self.window.post_redisplay() def start_transition(self, minval, maxval): self.minstart, self.maxstart = self.shader.minval, self.shader.maxval self.minend, self.maxend = minval, maxval self.transition_start = get_elapsed_time() self.timer() def mouse(self, button, state, x, y): if button == glut.GLUT_LEFT_BUTTON: pos = array((x, self.window.shape[0] - y)) / array(self.window.shape[::-1], dtype=float) pos = pos * (self.shader.maxval - self.shader.minval) + self.shader.minval if state == glut.GLUT_DOWN: self.last_pos = pos elif state == glut.GLUT_UP and all(pos != self.last_pos): self.history.append((self.shader.minval, self.shader.maxval)) self.start_transition(minimum(pos, self.last_pos), maximum(pos, self.last_pos)) elif state == glut.GLUT_DOWN and button == glut.GLUT_RIGHT_BUTTON: if self.history: self.start_transition(*self.history.pop()) def run(self): with self.shader: main_loop()
def __init__(self, images): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard self.window.special_callback = self.special self.images = images self.window.shape = self.images[0].shape[:2] self.render_pipeline = get_copy_pipeline_2d(context=self.window, sampler_type="isampler2D", maxval=255, yscale=-1, use_framebuffer=False, image=Texture2D(self._prepare_image(0))) self.current_index = 0 self.fps = 25 self.playing = False
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)
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 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 create_default_context(): """Create a default offscreen rendering context. @todo: This is window system dependent; create an appropriate context dynamically. @todo: When no context exists, create a raw offscreen context instead of a hidden GLUT window so that rendering is possible without an X connection. """ try: from glitter.contexts.glx import GLXContext return GLXContext() except ImportError: from glitter.contexts.glut import GlutWindow return GlutWindow(shape=(1, 1), hide=True)
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)
def get_gl_context(option="g"): """Returns an OpenGL context. Options: g(lut), q(t)""" if "g" == option: print "Creating GLUT context." from glitter.contexts.glut import GlutWindow gl_context = GlutWindow(shape=(1, 1), hide=True) elif "q" == option: print "Creating QT context." from PySide import QtGui from glitter.contexts.qt import QtWidget app = QtGui.QApplication.instance() if app is None: app = QtGui.QApplication(sys.argv) gl_context = QtWidget(None) else: raise Exception("Unknown option: %s" % option) return gl_context
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()
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 IntroductionExample(object): fps_scale = 1.1 def __init__(self, images): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard self.window.special_callback = self.special self.images = images self.window.shape = self.images[0].shape[:2] self.render_pipeline = get_copy_pipeline_2d( context=self.window, sampler_type="isampler2D", maxval=255, yscale=-1, use_framebuffer=False, image=Texture2D(self._prepare_image(0))) self.current_index = 0 self.fps = 25 self.playing = False def _prepare_image(self, idx): img = self.images[idx] while img.ndim < 3: img = img[..., None] return img def display(self): self.window.clear() self.render_pipeline.image.data = self._prepare_image( self.current_index) self.window.window_title = self.images.filenames[ self.current_index].filename self.render_pipeline.draw() self.window.swap_buffers() def keyboard(self, key, x, y): key = chr(key) if key == "+": self.fps *= self.fps_scale elif key == "-": self.fps /= self.fps_scale elif key == " ": if self.playing: self.playing = False else: self.playing = True self.timer() elif key == chr(27): raise SystemExit def special(self, key, x, y): if key == _gl.GLUT_KEY_LEFT: self.current_index = (self.current_index - 1) % len(self.images) self.window.post_redisplay() elif key == _gl.GLUT_KEY_RIGHT: self.current_index = (self.current_index + 1) % len(self.images) self.window.post_redisplay() if key == _gl.GLUT_KEY_PAGE_UP: self.current_index = (self.current_index - self.fps) % len( self.images) self.window.post_redisplay() elif key == _gl.GLUT_KEY_PAGE_DOWN: self.current_index = (self.current_index + self.fps) % len( self.images) self.window.post_redisplay() elif key == _gl.GLUT_KEY_HOME: self.current_index = 0 self.window.post_redisplay() elif key == _gl.GLUT_KEY_END: self.current_index = len(self.images) - 1 self.window.post_redisplay() def timer(self): if self.playing: self.current_index = (self.current_index + 1) % len(self.images) self.window.add_timer(int(round(1000.0 / self.fps)), self.timer) self.window.post_redisplay() def run(self): self.timer() 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()
# clear the screen window.clear(color=True, depth=True) # display the backbuffer window.swap_buffers() def keyboard(key, x, y): # if F1 or ALT-Enter is pressed, toggle fullscreen # TODO: key constants like GLUT_KEY_F1 should be available as an enum 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="NeHe's OpenGL Framework", double=True) # set background color to black # TODO: it would be nice if window.color_clear_value = (0, 0, 0) would # implicitly set alpha to 1 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 # loops until SystemExit is raised or window is closed 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()
class IntroductionExample(object): fps_scale = 1.1 def __init__(self, images): self.window = GlutWindow(double=True, multisample=True) self.window.display_callback = self.display self.window.keyboard_callback = self.keyboard self.window.special_callback = self.special self.images = images self.window.shape = self.images[0].shape[:2] self.render_pipeline = get_copy_pipeline_2d(context=self.window, sampler_type="isampler2D", maxval=255, yscale=-1, use_framebuffer=False, image=Texture2D(self._prepare_image(0))) self.current_index = 0 self.fps = 25 self.playing = False def _prepare_image(self, idx): img = self.images[idx] while img.ndim < 3: img = img[..., None] return img def display(self): self.window.clear() self.render_pipeline.image.data = self._prepare_image(self.current_index) self.window.window_title = self.images.filenames[self.current_index].filename self.render_pipeline.draw() self.window.swap_buffers() def keyboard(self, key, x, y): key = chr(key) if key == "+": self.fps *= self.fps_scale elif key == "-": self.fps /= self.fps_scale elif key == " ": if self.playing: self.playing = False else: self.playing = True self.timer() elif key == chr(27): raise SystemExit def special(self, key, x, y): if key == _gl.GLUT_KEY_LEFT: self.current_index = (self.current_index - 1) % len(self.images) self.window.post_redisplay() elif key == _gl.GLUT_KEY_RIGHT: self.current_index = (self.current_index + 1) % len(self.images) self.window.post_redisplay() if key == _gl.GLUT_KEY_PAGE_UP: self.current_index = (self.current_index - self.fps) % len(self.images) self.window.post_redisplay() elif key == _gl.GLUT_KEY_PAGE_DOWN: self.current_index = (self.current_index + self.fps) % len(self.images) self.window.post_redisplay() elif key == _gl.GLUT_KEY_HOME: self.current_index = 0 self.window.post_redisplay() elif key == _gl.GLUT_KEY_END: self.current_index = len(self.images) - 1 self.window.post_redisplay() def timer(self): if self.playing: self.current_index = (self.current_index + 1) % len(self.images) self.window.add_timer(int(round(1000.0 / self.fps)), self.timer) self.window.post_redisplay() def run(self): self.timer() 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()
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()
def display(): # clear the screen window.clear(color=True, depth=True) # display the backbuffer window.swap_buffers() def keyboard(key, x, y): # if F1 or ALT-Enter is pressed, toggle fullscreen # TODO: key constants like GLUT_KEY_F1 should be available as an enum 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="NeHe's OpenGL Framework", double=True) # set background color to black # TODO: it would be nice if window.color_clear_value = (0, 0, 0) would # implicitly set alpha to 1 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 # loops until SystemExit is raised or window is closed 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()
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)))
triangle.draw(primitive_types.TRIANGLES) # display the backbuffer 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