NeHe Tutorials for CL-OpenGL June 1st, 2010
Patrick Stein

Introduction

The Neon Helium Productions (NeHe) online tutorials (http://nehe.gamedev.net/) are the best resources available for coders trying to learn specific OpenGL rendering techniques in C/C++. The CL-OpenGL library (http://common-lisp.net/project/cl-opengl/) is, to my mind, the most straightforward mapping of the OpenGL, GLU, and GLUT APIs into Common Lisp. In this series, I hope to combine the best of both so that the aspiring Lisp coder can quickly access these OpenGL techniques.

I am aware that someone else reworked the first six tutorials for CL-OpenGL. However, I can’t track those down any longer. The original website is gone. I am also aware that I won’t have a great deal of time for this sort of coding in the near future, but I hope to tackle a bunch of these this summer. Most of the tutorials are fairly short.

In this first tutorial, we’re going to generate a simple template file that opens an OpenGL window.

;;; *.lisp
#<:use "glut-template.lisp">

Here is the resulting: intro.lisp.

Setting Up CL-OpenGL

To get started with CL-OpenGL, you will need a Lisp implementation that supports ASDF and CFFI, a git client, and OpenGL libraries. I will probably flesh this section out at some later date. For now, I am just barely going to touch on the prerequisites.

Lisp implementation.

CFFI.

git client.

OpenGL libraries. (Under Windows, you may need FreeGlut.dylib.)

Once you have all of the above, you will need to clone the CL-OpenGL repository so that you have the sources on your machine. The CL-OpenGL git repository is http://github.com/3b/cl-opengl.git.

# clone-repository.sh
% cd /where/you/want/to/put/the/cl-opengl/sources
% git clone http://github.com/3b/cl-opengl.git

You need to ensure that the cl-opengl/ directory that you just created is included in your asdf:*central-registry* list. For example, I have this in my ~/.sbclrc file

;;; asdf-prep.lisp
(dolist (subdir (list ;; ... some other packages ...
                      #P"cl-opengl/"))
  (push (merge-pathnames subdir #P"/usr/local/asdf-install/site/")
        asdf:*central-registry*))

Basic Template

Once you have CL-OpenGL installed, you’ll be able to use the following template.

;;; glut-template.lisp
#<:use "load opengl">

(defclass my-window (glut:window)
  ()
  (:default-initargs :width 400 :height 300
                     :title "My Window Title"
                     :x 100 :y 100
                     :mode '(:double :rgb :depth)))

#<:use "initialization method">
#<:use "additional glut methods">

#<:use "create an instance of our window">

Loading OpenGL

Usually, we’re going to load OpenGL, GLU, and GLUT.

;;; load opengl
(require :asdf)                 ; need ASDF to load other things
(asdf:load-system :cl-opengl)   ; load OpenGL bindings
(asdf:load-system :cl-glu)      ; load GLU bindings
(asdf:load-system :cl-glut)     ; load GLUT bindings

Setting up OpenGL

Our initialization method can do anything we need to do in terms of loading textures or fonts or what-have-you. We could do some with the normal CLOS initialize-object method if it is stuff we can do before OpenGL is initialized. For our purposes though, we need to wait until after OpenGL is ready but before our window is displayed so we make sure to go before the glut:display-window call.

;;; initialization method
(defmethod glut:display-window :before ((win my-window))
  #<:use "prepare opengl">
)

To prepare the default OpenGL environment that we’re going to use, we’re first going to turn on smooth shading. This allows colors to blend across our polygons. Later tutorials will go into more detail about smooth shading.

;;; prepare opengl
(gl:shade-model :smooth)        ; enables smooth shading

The next line here sets the color used to clear the screen. OpenGL color values range from zero to one with zero being the darkest and one being the brightest. The parameters here are (in order) the red, green, blue, and alpha channels. The alpha channel doesn’t really come into play when clearing the screen, so it doesn’t much matter in this instance. We’re going to use a black background.

;;; prepare opengl (cont.)
(gl:clear-color 0 0 0 0)        ; background will be black

The next several lines prepare the depth buffer. OpenGL keeps a variety of buffers that are the same dimensions as your window. You were probably expecting the color buffer that stores the actual pixel values that are rendered on the screen. The depth can be used to keep track of the depth of the last item drawn to the screen or to prevent an object from drawing if it doesn’t have a depth thats less than the current value of the depth buffer for the current pixel.

;;; prepare opengl (cont.)
(gl:clear-depth 1)              ; clear buffer to maximum depth
(gl:enable :depth-test)         ; enable depth testing
(gl:depth-func :lequal)         ; okay to write pixel if its depth
                                ; is less-than-or-equal to the
                                ; depth currently written

We are also going to tell OpenGL that we’d like it to make things in perspective look as nice as possible.

;;; prepare opengl (cont.)
                                ; really nice perspective correction
(gl:hint :perspective-correction-hint :nicest)

Display function

Almost all applications will need at least a GLUT display function. Usually, they will do more than this, but this will get us started.

;;; additional glut methods
(defmethod glut:display ((win my-window))
  (gl:clear :color-buffer-bit :depth-buffer-bit)
  (gl:load-identity))

Resizing the window

The following method gets called when your window is first created and any time your window is resized. We will use it to prepare our projection matrix.

;;; additional glut methods (cont.)
(defmethod glut:reshape ((win my-window) width height)
  #<:use "glut reshape -- prepare viewport">
  #<:use "glut reshape -- prepare projection">
  #<:use "glut reshape -- switch to model view">
)

To initialize the viewport, we simply take the given width and height and use them as the horizontal and vertical extents of our coordinate system.

;;; glut reshape -- prepare viewport
(gl:viewport 0 0 width height)  ; reset the current viewport

Next, we’re going to prepare the projection matrix. We’re going to set things up for a perspective view so that distant objects appear smaller than closer objects. We’re going to assume that the window accounts for a 45-degree field of view from left to right. We’re going to assume that objects in our scene can be anywhere from 1/10 to 100 units in front of our viewpoint.

First, we switch into the mode where matrix commands will change the projection matrix. Then, we make sure we’re starting from the identity matrix. Then, we prepare our perspective transformation assuming 45-degrees from left-to-right and a proportional amount from top-to-bottom (taking care not to divide by zero in the proportion).

;;; glut reshape -- prepare projection
(gl:matrix-mode :projection)    ; select the projection matrix
(gl:load-identity)              ; reset the matrix

;; set perspective based on window aspect ratio
(glu:perspective 45 (/ width (max height 1)) 1/10 100)

Once we are done setting up the projection matrix, we need to switch back to the model-view matrix so that further transforms will affect the space we’re viewing rather than where we are viewing things from.

;;; glut reshape -- switch to model view
(gl:matrix-mode :modelview)     ; select the modelview matrix
(gl:load-identity)              ; reset the matrix

Creating and displaying our window

To create and display an instance of our window, we simply go ahead and create and instance and pass it to glut:display-window.

;;; create an instance of our window
(glut:display-window (make-instance 'my-window))

Fourier Transforms in JavaScript September 2nd, 2009
Patrick Stein

In preparation for a study group on Numeric Photography, I started toying with what language to use for the coding projects.

Feature shopping

My inclination, of course, was to go with Lisp. Part of the goal of the assignments, however, was to make interactive web applications for each assignment and a gallery at the end of the completed assignments. I experimented a bit with Armed Bear Common Lisp. The hurdles that I would need a user to go through to use my application safely were too great. The user would have to edit their own java.policy file to give my applet permission. If I wanted to let the user do that with a single click or something, I would have to spring for a $200/year code-signing cert.

My next choice then was stand-alone Lisp applications. Rather than online, interactive applications, I would have downloadable ones. Not ideal, but doable. The big hurdle here is crafting a GUI to run the application. The GUI wouldn’t be too terrible thanks to an interesting concept that Dirk Gerrits pointed out: Immediate Mode GUIs. I have already implemented the buttons from this tutorial in OpenGL under SBCL.

Late one night, I remembered Parenscript. It is a Lisp-like language that compiles to Javascript. Parenscript was a little bit more its own thing and a little bit less Lisp than I had wanted. But, it got me thinking. What can you do in Javascript these days?

I searched for image processing javascript. The first hit was Pixastic. It is a library of basic image processing functions that you can run on any image on your page. At its heart, it uses the Canvas element that is available on Safari, Firefox, and Opera. So, I figured, why not? If I can get an FFT working in Javascript, then there is nothing keeping me from implementing all of the assignments in Javascript. Yes, Javascript is awful, but it’s a dream compared to Java. The new Javascript debugging features in Safari are quite useful, too.

Success

Below is a simple example of what I have working. When you first load the page, you see an image of my son and my dad at the Pittsburgh Zoo. This image is actually loaded in a hidden image and copied into a visible canvas. You are seeing the canvas.

If you press the FFT button and wait for it, you will see a representation of the 2-D Discrete Fourier Transform of the image. Really, the DFT output is complex numbers. The representation is just the magnitude of the complex numbers. The actual complex numbers are kept around in a variable so that when you hit the IFFT button, it has enough information to actually reconstruct the image. So, the FFT button works from the pixels in the canvas. The IFFT button works from the data generated with the last FFT. So, unfortunately, you cannot meaningfully FFT-FFT-IFFT-IFFT here. It is interesting, but not meaningful.

Also, I was trying to add a text box here so that you could use your own image, but I am not getting it right. You’re going to have to wait until I finish the whole assignment.




You need a browser that supports the Canvas tag.
Firefox, Safari, and Opera should all work.



A Different Look at the Mandelbrot Set May 14th, 2009
Patrick Stein

When you see pictures of the Mandelbrot set you are seeing the results of iterating a function over and over.

For the Mandelbrot set, you take a complex number c. You square it and add the original number. You take that, square it, and add the original. You do this a number of times. If the value stays relatively close to zero, the number is in the Mandelbrot set. If the value takes off away from zero, the number is not in the Mandelbrot set. Typically, one colors in the parts of the picture that aren’t in the Mandelbrot set with some color that indicates just how fast it hightailed it out of there.

I wanted to try something different. I wanted to see what the iteration process looks like. So, I threw together some code using Lisp with cl-opengl.

fibersThe first visualization that I coded was to start with tiny patches of the complex plane. From each patch, I would extrude it up out of the plane and over to its next iteration. The picture at the right is (IIRC) a patch with side-length one half centered at i with either 25 or 36 patches tiled in that area. As you can see, this isn’t a wholly satisfying picture of what’s going on.

The other idea that I had was to show the progress through iterations as an animation. This is a bit more interesting to look at until some of the patches start overflowing my floating point numbers or get too big for my clipping region. This is an area of side-length one centered at the origin with four-hundred patches. Here is a movie of that flow.

l