We'll begin with the requisite "Hello, World" introduction. This program will open a window with some text in it and wait to be closed. You can find the entire program in the hello_world.py file.
Begin by importing the glumpy package:
import glumpy
Create a Window by calling its default constructor. The window will be visible as soon as it's created, and will have reasonable default values for all its parameters:
window = glumpy.Window(width=512, height=512)
To display the text, we'll create a Label. Keyword arguments are used to set the font, position and anchorage of the label:
font = glumpy.text.Font("Vera.ttf", 64)
label = glumpy.text.Label(u"Hello World !", font,
anchor_x = 'center', anchor_y = 'center')
An on_draw event is dispatched to the window to give it a chance to redraw its contents. glumpy provides several ways to attach event handlers to objects; a simple way is to use a decorator:
@window.event
def on_draw():
window.clear()
label.draw(x=256, y=256, color=(1,1,1,1))
Within the on_draw handler the window is cleared to the default background color (black), and the label is drawn.
Finally, call:
glumpy.run()
To let glumpy respond to application events such as the mouse and keyboard. Your event handlers will now be called as required, and the run method will return only when all application windows have been closed.
In this example we'll load an image from the examples data directory and display it within the window while enforcing the image aspect such that proportion of the image are conserved when user resize the window. You can find the entire program in the image.py file.
import glumpy
img = glumpy.graphics.Image("data/lena.png",
anchor_x = 'center', anchor_y = 'center')
aspect = float(img.width)/float(img.height)
window = glumpy.Window(aspect = aspect)
@window.event
def on_draw():
window.clear()
with window.viewport():
img.draw(x=0, y=0)
app.run()
The previous example made implicit use of shaders to display things on screen. However, the real power of modern OpenGL lies in the possibility of writing custom shaders to draw virutally anything. We'll now see how to write a shader from scratch. Let's start by creating a window as usual.
import glumpy.gl as gl
import glumpy.app as app
import glumpy.gloo as gloo
window = Window()
What has not be explained previously is that the position on the window surface can be accessed in many different ways, until now, we have been using an implicit normalized representation of the surface that goes from [-1,-1] to [+1,+1]. This means that if we want to draw something, we need to have our coordinates transformed such that they fit within this range. Suppose we want to display a simple quad that cover the whole window:
quad = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)]
We need to tell OpenGL how to display this object and we thus need a program that is composed of a vertex shader and a fragment shader. Let's write first the vertex shader that tell OpenGL how to transform vertex coordinates into a normalized coordinates (easy since our quad is already normalized).
vec2 attribute position; void main() { gl_Position = vec4(position, 0.0, 1.0); }
The first line declares that a vertex is made of one attribute that is a vector
of two floats and named position
such that it can be used in the main
function. gl_Position
is a special keyword of GLSL that tell the vertex
shader the final position of the vertex. It is a four-dimensions vector because
OpenGL uses quaternion. We can now consider the fragment shader in order to
tell OpenGL the color to draw each fragment that will be contained within our
object.
Note
At this point, we still don't known what our shape will be, we only have some vertices placed on screen.
void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }
gl_FragColor
is another special GLSL keyword that contains the final
fragment (=pixel) color and uses an RGBA normalized encoding. In the program
above, any fragment will be white. We're almost done and we need now to create
a program:
program = gloo.Program(vertex, fragment, 4)
and we need to fill the attributes. The most simple and straightforward way to do that is:
program['position'] = quad