• No results found

OpenGL library functions in Perl

In document Graphics Programming With Perl (Page 177-182)

Three-dimensional graphics

9.1.1 OpenGL library functions in Perl

Almost all OpenGL library functions can be translated directly into Perl subroutines that are callable from your program. The name of the C OpenGL function is avail-able as a Perl function, and works the same way. However, there are some exceptions to this, and this section deals with those exceptions.

Functions that use array arguments in the C API to return values, such as glGet-Integerv() and glGetFloatv(), have a counterpart in the Perl API with a _p suf-fix, and they return the values in the more usual Perl way. For example, to attain the dimensions of the current viewport in OpenGL, the C code would look like:

#include <GL/gl.h>

GLint viewport[4];

glGetIntegerv(GL_VIEWPORT, viewport);

after which the x and y coordinates and the width and height are available in that order in the viewport array. In Perl, this code becomes

use OpenGL;

my ($x, $y, $width, $height) = glGetIntegerv_p(GL_VIEWPORT);

As you can see, this looks much closer to the way functions normally operate in Perl.

Functions that take pointers to strings to be filled as the result of the API call are available under several names: first of all, there is the function name with a _c suffix, for which an OpenGL::Array object must be supplied in place of the pointer argument.

The second way to call these functions is with an _s suffix, in which case it takes a Perl string argument in place of the pointer. The function name with no arguments is, in most cases, an alias to the function name with the _s suffix, but it’s safer to just call the one you mean. For example, to read the pixels from the current viewport, using the variables from the preceding code:

my $rgba;

glReadPixels_s($x, $y, $width, $height, GL_RGBA, GL_UNSIGNED_BYTE, $rgba);

NOTE In versions before 0.52 of the OpenGL modules, it was necessary to pre-allocate the string that you passed off to these functions, because the mod-ule failed to allocate memory for them on its own. This has been fixed in newer versions, and I strongly suggest that you get one if at all possible (see section 3.2.10, “OpenGL,” on page 32, for more information on where to get newer versions). If, for some reason, you can’t install a newer version of the module, you will need to do something like:

my $rgba = "X" x ($width * $height * 4 * 1);

glReadPixels_s($x, $y, $width, $height, GL_RGBA, GL_UNSIGNED_BYTE, $rgba);

Needless to say, this tends to be error-prone, because you have to take into account what GL_RGBA and GL_UNSIGNED_BYTE mean for the number of bytes to allocate, and you have to maintain this code. Perl programmers are just not used to having to allocate their memory, and they shouldn’t have to.

The OpenGL library and the associated utility libraries (GLX, GLU and GLUT) have undergone periodic changes. The OpenGL module supports most of the old versions, and several support libraries for OpenGL. Exactly what gets exported to your name space is, as usual, controlled by the arguments to the import function for OpenGL.

Here are some alternatives:

• use OpenGL;

• use OpenGL qw(:constants :functions);

• use OpenGL qw(:constants glReadPixels);

• use OpenGL qw(:all);

The module allows export of all the functions and constants defined in the OpenGL API, as well as their special Perl adaptations, and it defines a set of tags that can be used to export groups of them. The tags are listed in table 9.1.

If no labels are specified, the default exported functions are the same as if the :old label were specified. Note that if you specify another label and still want the old func-tions and constants available, you have to also specify :old explicitly. If you don’t use :old, then none of the functions that start with glpare available. To import abso-lutely everything into your name space, use the following:

use OpenGL qw(:all :old);

9.1.2 Capturing your OpenGL output

Due to the way OpenGL was designed—as an interface to hardware for high speed graphical rendering—every OpenGL application requires a window or screen for its output. The drawing doesn’t occur in some memory buffer in the OpenGL libraries, but the commands are sent, where possible, directly to the hardware responsible for its display.

This is all well and good if you’re writing a game or a three-dimensional walk-through viewer for an architectural program, but less handy when you are trying to produce a copy of your rendering to keep on file, perhaps for printing in a book such as this one. Because this is an area often overlooked in discussions on the OpenGL library, and because it places this section more closely within the range of the rest of the book, I’ll discuss how to read your screen buffer and create an image on disk.

To capture an image you have to read the pixels of the window displayed by the OpenGL client. Luckily, they needn’t be read one by one; instead you can make a

Table 9.1 The OpenGL import tags. The default tag (if nothing is specified) is :old. Note that some of these tags are combinations of some of the others.

:all All the current API functions and constants.

:old All the old, original API functions and constants.

:glconstants All the constants from the OpenGL library.

:gluconstants All the constants from the GLU library.

:glutconstants All the constants from the GLUT library.

:glxconstants All the constants from the GLX library.

:oldconstants All the old, original API constants.

:constants All the current API constants.

:glfunctions All the current functions from the OpenGL library.

:glufunctions All the functions from the GLU library.

:glutfunctions All the functions from the GLUT library.

:glxfunctions All the functions from the GLX library.

:oldfunctions All the old, original API functions.

:functions All the current API functions.

single call to a function provided by the OpenGL library: glReadPixels().2 Before doing so, however, you need to make a decision on which channels interest you and tell the OpenGL library how you want it to convert the pixels into values. Before dis-cussing the particulars, let’s look at a subroutine that can be used to capture any part of the currently displayed window to an image file. We use the Image::Magick module to translate the raw information that comes from the call to glReadPixels(), and to save the file to disk.

glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);

glPixelStorei(GL_PACK_ROW_LENGTH, $width);

glPixelStorei(GL_PACK_ALIGNMENT, 1);

b

If the user has passed in four arguments that define the rectangle to be captured, these arguments are used. Otherwise, the dimensions of the current window are requested and used.

c

The first thing we do is call glFinish(), to make sure that the OpenGL drawing functions are all completed. If we don’t do that, we run the risk of reading out pix-els from a frame buffer that is only half complete. This is followed by a call to glPushClientAttrib() to save all the current pixel settings, after which we can change them to our liking with glPixelStorei(). Once we have read the pixels into $rgba we reset the settings by calling glPopClientAttrib().

2 Also see the note on page 156 for some information on glReadPixels().

b

Get size of window

d e

The pixels are read into a long string of bytes with values for the red, green, blue and alpha channel, which can be read directly into an Image::Magick object with the BlobToImage() method. All we need to do then is save the image. To allow the user to postprocess the image, we return a reference to the Image::Magick object.

This routine can be very handy to have around when you are writing OpenGL appli-cations that need to be able to take snapshots of the rendered scenes. The example program in section 9.1.3 uses this routine to allow the user to save a snapshot of the current window on screen. We will put it in a library of OpenGL tools, which we can expand in the future, and write a little application that uses it to create two images:3

#!/usr/local/bin/perl -w use strict;

use OpenGL qw(:all :old);

require "OpenGLTools.pl";

sub triangle {

glBegin (GL_TRIANGLES);

glColor3f (0.0, 0.0, 0.0);

glVertex2f(0.0, 0.0);

glColor3f (0.5, 0.5, 0.5);

glVertex2f(30.0, 0.0);

glColor3f (1.0, 1.0, 1.0);

glVertex2f(0.0, 30.0);

glEnd ();

}

glpOpenWindow (width => 250, height => 250);

glLoadIdentity();

gluOrtho2D (0.0, 30.0, 0.0, 30.0);

glClearColor (1.0, 1.0, 1.0, 0.0);

glClear (GL_COLOR_BUFFER_BIT);

triangle();

gltIMCapture("OpenGLcap1.png", 50, 50, 100, 100);

gltIMCapture("OpenGLcap2.png");

This is a basic OpenGL application that draws a triangle, filled with a smooth gray-scale gradient. Once the drawing is finished, it captures part of the window, and the whole window with the previously discussed gltIMCapture() function, and saves these captures to files which can be seen in figure 9.1.

You might have noticed that the program presented contains floating point values everywhere, while Perl doesn’t really care about these sorts of things. The truth is that integer values would have worked just as well, but I like to be explicit. It also comes

3 In case you noticed the odd spacing in this code: I often vertically align the brackets around the argu-ments to OpenGL function calls, because I find it slightly more legible. Some people think it’s awful.

in handy when I decide that parts of these programs need to be ported to C, in which case there is less work to do if all the constants are already in the correct format.

In document Graphics Programming With Perl (Page 177-182)