Está en la página 1de 3

2019. 09. 27.

OpenGL with Perl

OpenGL with Perl

Perl isn't normally the first language you'd think to use for rendering realtime graphics, but it's
quite doable on todays's hardware, and is even pretty efficient when combined with OpenGL
display lists and XS (C code linked into perl).

First off, I should mention that I'm not using all the modern fancy shaders or buffer objects that
OpenGL offers, and just using the original matrix stack and point plotting. While these might be
considered slow for video games with polygon counts in the ten-thousands, it's perfectly fine for
polygon counts in the hundreds, and shaders would be overkill. It works especially well with the
Display List feature, where you plot the vertices of a model once, then replay the model each
frame using a single command. This is also particularly convenient when prototyping; just hack
around plotting vertices until you get what you want, then add some display list curly braces
around as much as you can pre-compile for a nice speed boost.

TODO
(This isn't anywhere close to a proper write-up, but I might eventually get there. I might also
upgrade to modern OpenGL shaders etc. before I ever get around to publishing my library of
convenience functions.)

Example: Speedometer Needle


This example shows off how display lists can reduce a bunch of Perl OpenGL overhead. This
particular example isn't a great usage of display lists (editing the texture would be more
sensible) but is easy enough to read that it shows off the concept well.

If you look at my speedometer needle in the demo videos, you can see that it is semi-
transparent around the middle, and solid out near the speedometer ticks. The "right way" to do
this is by adjusting the alpha channel on the texture, but at the time I was developing it I wasn't
sure where I wanted the alpha transition, or how much alpha I wanted, so I just traced out a
solid texture in Gimp and rendered it as multiple polygons, with a varying alpha channel blended
in.

Here is the code that performs this:

speedometer-needle.pl
# Draw the needle
localmatrix {
rotate z => -.25+$self->gauge_angle_fn->($mph);
($self->{_needle_displaylist} ||= do {
my $img= $res->img('speed-needle');
$img->gl_tex->bind; # make sure it's loaded before creating displ
displaylist {
$img->gl_tex->bind;
my $hub_rad= $img->height / $img->width * $self->gauge_tick_r
triangle_strip {
glColor4d(1,1,0,0.2);
glTexCoord2d(0,0);
glVertex2d(-$hub_rad, -$hub_rad);
glTexCoord2d(0,1);
glVertex2d($hub_rad, -$hub_rad);

www.nrdvana.net/opengl.html 1/3
2019. 09. 27. OpenGL with Perl

glColor4d(1,1,0,0.3); OpenGL with Perl


glTexCoord2d(.5,0);
glVertex2d(-$hub_rad, -$hub_rad+($hub_rad + $self->gauge_
glTexCoord2d(.5,1);
glVertex2d($hub_rad, -$hub_rad+($hub_rad + $self->gauge_t
glColor4d(1,1,0,1);
glTexCoord2d(1,0);
glVertex2d(-$hub_rad, $self->gauge_tick_rad);
glTexCoord2d(1,1);
glVertex2d($hub_rad, $self->gauge_tick_rad);
};
};
})->exec;
};

The "localmatrix" function is a thing I wrote that forces a glPushMatrix/glPopMatrix around the
block of code. It is equivalent to

glPushmatrix();
try {
...
}
finally {
glPopMatrix();
}

The second line rotates the coordinate space according to the current speed. It's another
"sugar" function I wrote that is equivalent to the OpenGL glRotate(angle*2*PI, 0, 0, 1),
but which I find more convenient. It also takes "circle fraction angles", so -.25 means a quarter
rotation to the left. gauge_angle_fn is a function that scales between miles-per-hour and the
angle it should point for that speed.

The third line is a bit messy, but what it does is to load the value of object field
"_needle_displaylist", and if that doesn't exist, then populate it with the result of the do {} block.

To construct the display list, the first line loads the image resource named 'speed-needle'. The
resource manager will lazy-load the images the first time they are referenced, but it won't
actually load them into an OpenGL texture until the first time it is bound to the OpenGL context,
so the next line does that prior to starting the display list. (otherwise the allocating/loading of the
texture would become part of the display list, which would be bad)

The displaylist function is another sugar method I wrote. It calls glGenLists, glNewList,
and glEndList around the block of code.

The operations inside the display list are to bind the texture (which must be done every frame
since only one texture is active at once), then plot out four triangles complete with texture
coordinates and colors that include alpha blending. The math involved is mostly to size the
polygons so that they reach from the origin of the gauge to the speed tick marks around the
edge. The ordering of the vertexes is based on GL_TRIABGLE_STRIP, which you can look up if
you're interested.

On completion of the displaylist block, OpenGL internally stores this list of gl* operations in
a buffer that it can play back quickly. The return value of the displaylist function is a Perl
object that wraps the OpenGL display list (which is an integer) and if that object ever goes out of

www.nrdvana.net/opengl.html 2/3
2019. 09. 27. OpenGL with Perl

scope it calls glFreeList. Until then, you can call exec() on thatOpenGL
object to execute a
with Perl
glCallList, which I do on the final line.

To recap, after the first iteration, the only thing that happens in this code is:

# Draw the needle


localmatrix {
rotate z => -.25+$self->gauge_angle_fn->($mph);
$self->{_needle_displaylist}->exec;
};

and that corresponds to the OpenGL calls of:

glPushMatrix();
glRotated(2*PI*(-.25+$self->gauge_angle_fn->($mph)), 0, 0, 1);
glCallList($self->{_needle_displaylist}->id);
glPopMatrix();

www.nrdvana.net/opengl.html 3/3

También podría gustarte