This page should serve as tutorial and also as reference to Ruby bindings for OpenGL language. It is assumed that you have basic understanding of both OpenGL and Ruby.
If you are new to Ruby, the ruby-lang website contains lots of documentation and manuals for Ruby.
Table of Contents
- Naming Conventions
- Function parameters
- Return values
- Textures and other raw data
- Error Checking
- OpenGL version and Extensions
- Selection and Feedback queries
- Vertex Arrays
- Buffer Objects
- GLUT, SDL, GLFW..
- GLUT callbacks
The bindings contains three modules:
- 'Gl' - OpenGL functions itself
- 'Glu' - OpenGL Utility Library API - higher-level drawing routines, NURBS etc.
- 'Glut' - OpenGL Utility Toolkit - low level functions such as creating OpenGL context, opening window or handling user input
You can import all three modules by calling
You can also load the modules separately by using:
require 'gl' require 'glu' require 'glut'
The functions and constants are named the same as their C counterparts:
require 'opengl' ... Gl.glFooBar( Gl::GL_FOO_BAR ) Glu.gluFooBar( Glu::GLU_FOO_BAR ) Glut.glutFooBar( Glut::GLUT_FOO_BAR )
This is the 'full' syntax, usefull when you are expecting name clashes with other modules, or just want to be formal ;) More often, you will want to use the 'C-style' syntax, which you can accomplish by using 'include' to export the module functions and constants to global namespace:
require 'opengl' include Gl,Glu,Glut ... glFooBar( GL_FOO_BAR ) gluFooBar( GLU_FOO_BAR ) glutFooBar( GLUT_FOO_BAR )
Finally, you can use the 'old' syntax:
require 'opengl' ... # Note the missing prefixes in functions and constants # and also capitalization of module names GL.FooBar( GL::FOO_BAR ) GLU.FooBar( GLU::FOO_BAR ) GLUT.FooBar( GLUT::FOO_BAR )
This syntax was used by previous ruby-opengl versions; some people also consider it as being more in the spirit of OO programming. It has one downside though - due to Ruby's naming scheme, you cannot use constants which begins with number, e.g. GL_2D would under this syntax be (GL::)2D which is illegal.
All three variants of syntax will continue to be supported in future, so it's up to you which one you choose to use.
The rest of this tutorial will use the C syntax.
For most types the ruby syntax follows the C API. If needed, ruby will do automatic parameter conversion to required type if possible. Example:
glVertex3f( 1.0, 1.0, 1.0 ) # matches C syntax glVertex3f( 1, 1, 1 ) # equivalent to the above glVertex3f( "string", 1, 1 ) # raises TypeError exception
Arrays are passed/received as Ruby arrays:
vertex = [ 1, 1, 1 ] glVertex3fv( vertex )
For functions with multiple parameter-number variations (glVertex,glColor,...) we define 'overloaded' functions, as in:
glVertexf( 1, 1 ) # will call glVertex2f() glVertexf( 1, 1, 1 ) # will call glVertex3f() glVertexf( 1, 1, 1, 1 ) # will call glVertex4f() glVertexi( 1, 1 ) # will call glVertex2i() ... # and so on
In C, OpenGL functions rarely return values directly, instead you pass in pointer to preallocated buffer and they will fill it with values; sometimes you have to even query how big buffer you'll need to allocate. Ruby does this all for you, returning either single value or array:
glColor4f( 1.0, 1.0, 1.0, 1.0 ) ... color = glGetDoublev(GL_CURRENT_COLOR) p color # will be [1.0,1.0,1.0,1.0]
Matrices are passed and received as ruby array, or as ruby Matrix objects:
matrix_a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] matrix_b = [ [ 0, 1, 2, 3 ], [ 4, 5, 6, 7 ], [ 8, 9,10,11 ], [ 12,13,14,15 ] ] matrix_c = Matrix.rows( [ [ 0, 1, 2, 3 ], [ 4, 5, 6, 7 ], [ 8, 9,10,11 ], [ 12,13,14,15 ] ] ) ... glLoadMatrixf(matrix_a) glLoadMatrixf(matrix_b) glLoadMatrixf(matrix_c) # same result
You may also create your own matrix class and pass it this way, provided that it is convertible to array (has 'to_a' method).
Note that as OpenGL uses column-major notation for matrices, you may need to call transpose() when working with row-major matrices or arrays in ruby.
Textures and other raw data
Data for textures, arrays, buffers etc. can be specified either as ruby arrays or directly as raw packed strings - strings that contains their direct memory representation (just like C arrays). If you need to convert between ruby arrays and these strings, use ruby Array#pack() and String#unpack() functions. Example:
# create texture, 2x2 pixels, # 3 components (R,G,B) for each pixel as floats texture = [ 1.0, 0.0, 0.0, # 1st pixel, red 0.0, 1.0, 0.0, # 2nd pixel, green 0.0, 0.0, 1.0, # 3rd pixel, blue 1.0, 1.0, 1.0 # 4th pixel, white ] # convert it to string # f = native float representation # * = convert all values in the array the same way data = texture.pack("f*") ... glTexImage2D( GL_TEXTURE_2D, # target 0, # mipmap level, GL_RGB8, # internal format 2, 2, # width, height 0, # border = no GL_RGB, # components per each pixel GL_FLOAT, # component type - floats data # the packed data )
Reverse works just the same:
... data = glGetTexImage( # returns the packed data as string GL_TEXTURE_2D, # target 0, # mipmap level GL_RGB, # components per pixel GL_FLOAT # component type ) # now convert it to ruby array texture = data.unpack("f*") ...
For storage, packed strings are more memory efficient than ruby arrays, but cannot be easily changed or manipulated.
Starting with version 0.60.0, ruby-opengl performs automatic checking of OpenGL and GLU errors. Functions:
Gl.enable_error_checking Gl.disable_error_checking Gl.is_error_checking_enabled? # true/false
When the checking is enabled (default), glGetError() is executed after each OpenGL call, and should error occur, Gl::Error exception is raised:
Gl.enable_error_checking ... begin ... glEnable(GL_TRUE) # will raise exception ... rescue Gl::Error => err # err.id contains the OpenGL error ID if (err.id == GL_INVALID_ENUM) puts "Oh noes! You used invalid enum!" ... end ... end
Some GLU functions may also throw Glu::Error - the handling is the same as above.
It is usually good idea to leave error checking on for all your code, as OpenGL errors have habit to pop-up in unexpected places. For now there is no measurable performance hit for error checking, although this may depend on your graphic drivers implementation.
Various examples are in 'examples' directory of the bindings. To run them, manually pass them to
On windows, you may want to use 'rubyw' instead, which displays the standard output window as some examples use the console for usage info etc.
If you get 'opengl not found' error, and you installed ruby-opengl from gems, your shell or ruby installation is probably not configured to use the gems; in that case type:
ruby -rubygems some_sample.rb
README file in the
examples directory contains some notes on the examples.
OpenGL Version and Extensions
To query for available OpenGL version or OpenGL extension, use Gl.is_available? function:
# true if OpenGL version is 2.0 or later is available Gl.is_available?(2.0) ... # returns true if GL_ARB_shadow is available on this system Gl.is_available?("GL_ARB_shadow")
For list of what extensions are supported in ruby-opengl see this page
The extensions' function names once again follows the C API. Some extensions were over time promoted to ARB or even to OpenGL core, retaining their function names just with suffix changed or removed. However sometimes the functions semantics was changed in the process, so to avoid confusion, ruby-opengl bindings will strictly adhere to the C naming, e.g. :
# will call the function from GL_ARB_transpose_matrix extension glLoadTransposeMatrixfARB(matrix) ... # will call the function from OpenGL 1.3 glLoadTransposeMatrixf(matrix)
Note: ruby-opengl is compiled against OpenGL 1.1, and all functions and enums from later versions of OpenGL and from extensions are loaded dynamically at runtime. That means that all of OpenGL 2.1 and supported extensions are available even if the ruby-opengl bindings are compiled on platform which lacks proper libraries or headers (like for example Windows without installed graphic drivers). This should ease binary-only distribution and application packaging.
Querying selection and feedback is different from C. Example:
# this will create selection buffer 512*sizeof(GLuint) long buf = glselectbuffer(512) # enter feedback mode glRenderMode(GL_SELECT) ... # draw something here # return to render mode count = glRenderMode(GL_RENDER) # at this point the buf string is freezed and contains # the selection data, which you can recover with unpack # function data = buf.unpack("I*") # I for unsigned integer # also, next call to glRenderMode(GL_SELECT) will overwrite # the 'buf' buffer with new data
The feedback query follows the same pattern, only the data are stored as floats.
In current state, vertex arrays are not very efficient in ruby-opengl, as it is not possible to change the array content once it is specified, and there is overhead for converting between ruby and C representation of numbers. Using display lists for static and immediate mode for dynamic objects is recommended instead.
You can specify the data the same way as texture data. Example:
normals = [0,1,0, 1,0,0, 1,1,1] glNormalPointer(GL_FLOAT,0,normals) ... glEnable(GL_NORMAL_ARRAY) glDrawArrays(...) ...
This applies to all *pointer functions. glGetPointerv will return reference to the frozen string previously specified.
Once again, in current state buffer objects (VBOs in particular) are not very efficient in ruby-opengl. Unlike textures and vertex arrays, the data for buffers must be prepacked by using .pack() function, as buffers does not retain information about the storage type. Mapping of the buffer afterwards is read-only.
Like in C, buffer binding affects some functions in way that if particular buffer is bound, the related functions (for example glTexImage) take integer offset in place of data string argument. This is also true for getter functions (e.g. glGetTexImage) - instead of returning the data string, they take offset as they're last argument (so in ruby they take one extra argument), and will write the data in the bound buffer as expected.
# specify 3 vertices, 2*float each data = [0,0, 0,1, 1,1].pack("f*") ... # generate buffer name buffers = glGenBuffers(1) # bind to the name to ARRAY buffer for vertex array glBindBuffer(GL_ARRAY_BUFFER,buffers) # here the data is specified, size is n*sizeof(float) # note that you don't get to specify type, as buffers # operate on byte level glBufferData(GL_ARRAY_BUFFER,6*4,data,GL_DYNAMIC_DRAW) ... # here instead of specyfing the data, you pass '0' (or # positive integer) as offset to the bound buffer glVertexPointer(2,GL_FLOAT,0,0) ... glEnableClientState(GL_VERTEX_ARRAY) ...
GLUT, SDL, GLFW..
When it comes to low-level task like GL window creation, input and event handling, the first choice is GLUT, as it is readilly available alongside OpenGL. However both GLUT itself and its implementations have their drawbacks, and for that and other reasons there are number of replacement libraries. You can use any of them with ruby-opengl (as long as there are ruby bindings for them).
Here is example for SDL:
require 'opengl' require 'sdl' # init SDL.init(SDL::INIT_VIDEO) SDL.setGLAttr(SDL::GL_DOUBLEBUFFER,1) SDL.setVideoMode(512,512,32,SDL::OPENGL) ... Gl.glVertex3f(1.0,0.0,0.0) ... SDL.GLSwapBuffers() ...
and another example for GLFW:
require 'opengl' require 'glfw' # init Glfw.glfwOpenWindow( 500,500, 0,0,0,0, 32,0, Glfw::GLFW_WINDOW ) ... Gl.glVertex3f(1.0,0.0,0.0) ... Glfw.glfwSwapBuffers() ...
The GLUT callback functions are specified as Proc objects, which you can either create with lambda as:
reshape = lambda do |w, h| ... end ... glutReshapeFunc( reshape )
or by conversion from normal functions:
def reshape(w,h) ... end ... glutReshapeFunc( method("reshape").to_proc )
Note: An older notation you'll see instead of
PickAxe v2 notes that
proc is "mildly deprecated" in favor of
You'll also sometimes see
Proc.new used in place of either. Pages 359-360 of
PickAxe v2 describe the differences between using
but for our purposes either will be fine.
The directory structure follows current Ruby standards, with a few extra directories added.
doc/-- Contains documentation for the project (from which this website is generated).
examples/-- Example programs.
ext/-- Contains subdirectories, one for each of the three extension modules (gl, glu, glut). Herein are the files needed to compile the extension modules.
lib/-- Files that the user is meant to
requirein their own code.
test/-- Contains automatic testsuite for the bindings
utils-- Some utility scripts used to help generate code, documentation and website.
website-- After running
rake gen_websitethis directory will contain the ruby-opengl website.