https://wiki.haskell.org/api.php?action=feedcontributions&user=Laurentg&feedformat=atomHaskellWiki - User contributions [en]2024-03-28T21:21:08ZUser contributionsMediaWiki 1.35.5https://wiki.haskell.org/index.php?title=OpenGL&diff=23965OpenGL2008-11-10T22:28:47Z<p>Laurentg: Replaced broken ghc libs links by hackage doc valid ones</p>
<hr />
<div>This is a stub page for Haskell's OpenGL and GLUT bindings. It is meant as a starting point to replace the outdated and misleading documentation at the<br />
[http://www.haskell.org/HOpenGL-old/ old page].<br />
<br />
First, note that the implementation is far more up-to-date than that old page suggested (originally, it was quite useful, but the page hasn't kept up with the implementation for a long time now).<br />
<br />
== References == <br />
* [http://www.haskell.org/mailman/listinfo/hopengl the HOpenGL mailing list]<br />
<br />
* [http://hackage.haskell.org/packages/archive/OpenGL/latest/doc/html/ the API docs for the OpenGL binding]<br />
<br />
* [http://hackage.haskell.org/packages/archive/GLUT/latest/doc/html/ the API docs for the GLUT binding]<br />
<br />
* [http://darcs.haskell.org/packages/OpenGL the darcs repo with the sources for the OpenGL binding]<br />
<br />
* [http://darcs.haskell.org/packages/GLUT/ the darcs repo with the sources for the GLUT binding]<br />
<br />
In particular, note that the [http://darcs.haskell.org/packages/GLUT/examples/ examples/] directory in the GLUT repo contains lots of examples, including translations of the red book examples.<br />
<br />
Both the API documentation and the examples are best studied with the [http://www.opengl.org/documentation/specs/ original specs] and the original [http://www.opengl.org/documentation/red_book/ red book] examples at hand. An index of the examples from v1.1 of the red book, with screen shots, can be found [http://www.opengl.org/resources/code/samples/redbook/ here]<br />
<br />
== Projects using the OpenGL bindings == <br />
<br />
* [[Frag]], a 3D first-person shooter game.<br />
* [[Monadius]], a 2D scrolling arcade game.<br />
* [[Roguestar]], a roguelike adventure game using 3D graphics.<br />
* [[Shu-thing]], a 2D scroling arcade game.<br />
* [[Topkata]], a jumping ball puzzle game.<br />
* [http://hackage.haskell.org/trac/PolyFunViz/wiki PolyFunViz], a toolkit for scientific visualization (e.g. surfaces, flows, contours, volumes)<br />
<br />
== HOpenGL Resources == <br />
<br />
* [[OpenGLTutorial1]] and [[OpenGLTutorial2]]<br />
* [http://bluheron.europa.renci.org/docs/BeautifulCode.pdf Beautiful Code, Compelling Evidence: Functional Programming for Information Visualization and Visual Analytics] - Writing visualizations using OpenGL or Cairo (PDF)<br />
* [http://www.cin.ufpe.br/~haskell/hopengl/ Andre Furtado's nice tutorial] written in 2001 (bitrotted)<br />
* [http://www.haskell.org/~pairwise/HOpenGL/HOpenGL.html#texLoad Spriting with HOpenGL], David Morra<br />
<br />
== OpenGL Resources ==<br />
<br />
* [http://www.opengl.org/resources/faq/technical/ OpenGL FAQ and Toubleshooting Guide] Assumes some knowledge of OpenGL. Good for those who have written something but want to avoid common pitfalls.<br />
<br />
== Getting Started ==<br />
<br />
* assuming you know Haskell, any OpenGL tutorial of your choice should get you going (browsing the [http://www.opengl.org OpenGL] site is also a good idea)<br />
* use the [http://www.opengl.org/documentation/red_book/ Red Book], and its example code translations, to understand the small differences between OpenGL and HOpenGL<br />
* use the [http://www.opengl.org/documentation/specs/ OpenGL and GLUT specs] to find your way around the [http://hackage.haskell.org/packages/archive/OpenGL/latest/doc/html HOpenGL Haddock documentation]<br />
* use the [http://www.haskell.org/mailman/listinfo/hopengl HopenGL list] for questions and success stories<br />
<br />
== Additional software == <br />
* [[FTGL]]: Portable TrueType font rendering for OpenGL using the Freetype2 library<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/GLFW GLFW]: A binding for GLFW, An OpenGL Framework<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/GLUT GLUT]: A binding for the OpenGL Utility Toolkit<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/graphics-drawingcombinators graphics-drawingcombinators]: A functional interface to 2D drawing in OpenGL<br />
<br />
Somewhat related is [http://libsdl.org/ SDL], which is also based on OpenGL:<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL SDL]: Binding to libSDL<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-gfx SDL-gfx]: Binding to libSDL_gfx<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-image SDL-image]: Binding to libSDL_image<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-mixer SDL-mixer]: Binding to libSDL_mixer<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-mpeg SDL-mpeg]: Binding to the SMPEG library<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-ttf SDL-ttf]: Binding to libSDL_ttf<br />
<br />
To add sound to OpenGL applications:<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/OpenAL OpenAL]: A binding to the [[OpenAL]] cross-platform 3D audio API<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/ALUT ALUT]: A binding for the OpenAL Utility Toolkit<br />
<br />
<br />
== Troubleshooting ==<br />
=== I can't display text with renderString ===<br />
It's probably because the text is displayed too big. Setting a much smaller scale factor before calling renderString should solve the problem.<br />
<haskell><br />
scale 0.001 0.001 (0.001∷GLfloat)<br />
renderString Roman "Test string"<br />
</haskell><br />
=== Animations flicker ===<br />
If you're not using DoubleBuffered display mode, turn that on. Also, you must set the display mode '''before''' creating the window you're going to be drawing in. To check if you've enabled double buffering use something like:<br />
<haskell><br />
db <- get doubleBuffered<br />
</haskell><br />
and set DoubleBuffered mode (before creating your windows!) like this:<br />
<haskell><br />
initialDisplayMode $= [DoubleBuffered]<br />
createWindow "My Window"<br />
</haskell><br />
You will also need to call [http://hackage.haskell.org/packages/archive/GLUT/latest/doc/html/Graphics-UI-GLUT-Window.html#v%3AswapBuffers <haskell>swapBuffers</haskell>] at the end of your draw function.<br />
<br />
[[Category:Packages]]</div>Laurentghttps://wiki.haskell.org/index.php?title=OpenGL&diff=23953OpenGL2008-11-10T21:06:25Z<p>Laurentg: Replaced broken haddock link with valid one pointing to Hackage corresponding page.</p>
<hr />
<div>This is a stub page for Haskell's OpenGL and GLUT bindings. It is meant as a starting point to replace the outdated and misleading documentation at the<br />
[http://www.haskell.org/HOpenGL-old/ old page].<br />
<br />
First, note that the implementation is far more up-to-date than that old page suggested (originally, it was quite useful, but the page hasn't kept up with the implementation for a long time now).<br />
<br />
== References == <br />
* [http://www.haskell.org/mailman/listinfo/hopengl the HOpenGL mailing list]<br />
<br />
* [http://www.haskell.org/ghc/docs/latest/html/libraries/OpenGL/Graphics-Rendering-OpenGL.html the API docs for the OpenGL binding]<br />
<br />
* [http://www.haskell.org/ghc/docs/latest/html/libraries/GLUT/Graphics-UI-GLUT.html the API docs for the GLUT binding]<br />
<br />
* [http://darcs.haskell.org/packages/OpenGL the darcs repo with the sources for the OpenGL binding]<br />
<br />
* [http://darcs.haskell.org/packages/GLUT/ the darcs repo with the sources for the GLUT binding]<br />
<br />
In particular, note that the [http://darcs.haskell.org/packages/GLUT/examples/ examples/] directory in the GLUT repo contains lots of examples, including translations of the red book examples.<br />
<br />
Both the API documentation and the examples are best studied with the [http://www.opengl.org/documentation/specs/ original specs] and the original [http://www.opengl.org/documentation/red_book/ red book] examples at hand. An index of the examples from v1.1 of the red book, with screen shots, can be found [http://www.opengl.org/resources/code/samples/redbook/ here]<br />
<br />
== Projects using the OpenGL bindings == <br />
<br />
* [[Frag]], a 3D first-person shooter game.<br />
* [[Monadius]], a 2D scrolling arcade game.<br />
* [[Roguestar]], a roguelike adventure game using 3D graphics.<br />
* [[Shu-thing]], a 2D scroling arcade game.<br />
* [[Topkata]], a jumping ball puzzle game.<br />
* [http://hackage.haskell.org/trac/PolyFunViz/wiki PolyFunViz], a toolkit for scientific visualization (e.g. surfaces, flows, contours, volumes)<br />
<br />
== HOpenGL Resources == <br />
<br />
* [[OpenGLTutorial1]] and [[OpenGLTutorial2]]<br />
* [http://bluheron.europa.renci.org/docs/BeautifulCode.pdf Beautiful Code, Compelling Evidence: Functional Programming for Information Visualization and Visual Analytics] - Writing visualizations using OpenGL or Cairo (PDF)<br />
* [http://www.cin.ufpe.br/~haskell/hopengl/ Andre Furtado's nice tutorial] written in 2001 (bitrotted)<br />
* [http://www.haskell.org/~pairwise/HOpenGL/HOpenGL.html#texLoad Spriting with HOpenGL], David Morra<br />
<br />
== OpenGL Resources ==<br />
<br />
* [http://www.opengl.org/resources/faq/technical/ OpenGL FAQ and Toubleshooting Guide] Assumes some knowledge of OpenGL. Good for those who have written something but want to avoid common pitfalls.<br />
<br />
== Getting Started ==<br />
<br />
* assuming you know Haskell, any OpenGL tutorial of your choice should get you going (browsing the [http://www.opengl.org OpenGL] site is also a good idea)<br />
* use the [http://www.opengl.org/documentation/red_book/ Red Book], and its example code translations, to understand the small differences between OpenGL and HOpenGL<br />
* use the [http://www.opengl.org/documentation/specs/ OpenGL and GLUT specs] to find your way around the [http://hackage.haskell.org/packages/archive/OpenGL/latest/doc/html HOpenGL Haddock documentation]<br />
* use the [http://www.haskell.org/mailman/listinfo/hopengl HopenGL list] for questions and success stories<br />
<br />
== Additional software == <br />
* [[FTGL]]: Portable TrueType font rendering for OpenGL using the Freetype2 library<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/GLFW GLFW]: A binding for GLFW, An OpenGL Framework<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/GLUT GLUT]: A binding for the OpenGL Utility Toolkit<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/graphics-drawingcombinators graphics-drawingcombinators]: A functional interface to 2D drawing in OpenGL<br />
<br />
Somewhat related is [http://libsdl.org/ SDL], which is also based on OpenGL:<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL SDL]: Binding to libSDL<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-gfx SDL-gfx]: Binding to libSDL_gfx<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-image SDL-image]: Binding to libSDL_image<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-mixer SDL-mixer]: Binding to libSDL_mixer<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-mpeg SDL-mpeg]: Binding to the SMPEG library<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/SDL-ttf SDL-ttf]: Binding to libSDL_ttf<br />
<br />
To add sound to OpenGL applications:<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/OpenAL OpenAL]: A binding to the [[OpenAL]] cross-platform 3D audio API<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/ALUT ALUT]: A binding for the OpenAL Utility Toolkit<br />
<br />
<br />
== Troubleshooting ==<br />
=== I can't display text with renderString ===<br />
It's probably because the text is displayed too big. Setting a much smaller scale factor before calling renderString should solve the problem.<br />
<haskell><br />
scale 0.001 0.001 (0.001∷GLfloat)<br />
renderString Roman "Test string"<br />
</haskell><br />
=== Animations flicker ===<br />
If you're not using DoubleBuffered display mode, turn that on. Also, you must set the display mode '''before''' creating the window you're going to be drawing in. To check if you've enabled double buffering use something like:<br />
<haskell><br />
db <- get doubleBuffered<br />
</haskell><br />
and set DoubleBuffered mode (before creating your windows!) like this:<br />
<haskell><br />
initialDisplayMode $= [DoubleBuffered]<br />
createWindow "My Window"<br />
</haskell><br />
You will also need to call [http://hackage.haskell.org/packages/archive/GLUT/latest/doc/html/Graphics-UI-GLUT-Window.html#v%3AswapBuffers <haskell>swapBuffers</haskell>] at the end of your draw function.<br />
<br />
[[Category:Packages]]</div>Laurentghttps://wiki.haskell.org/index.php?title=OpenGLTutorial1&diff=23952OpenGLTutorial12008-11-10T20:57:12Z<p>Laurentg: Replaced broken callbacks links pointing to ghc libs, now point to Hackage OpenGL pages.</p>
<hr />
<div>''This tutorial [http://blog.mikael.johanssons.org/archive/2006/09/opengl-programming-in-haskell-a-tutorial-part-1/] was originally written by Mikael Vejdemo Johansson, and was copied here with permission.''<br />
<br />
After having failed following the [http://www.tfh-berlin.de/~panitz/hopengl/skript.html googled tutorial in HOpenGL programming], I thought I'd write down the steps I actually can get to work in a tutorial-like fashion. It may be a good idea to read this in parallell to the tutorial linked, since Panitz actually brings a lot of good explanations, even though his syntax isn't up to speed with the latest HOpenGL at all points.<br />
<br />
==Hello World==<br />
First of all, we'll want to load the OpenGL libraries, throw up a window, and generally get to grips with what needs to be done to get a program running at all. Note: the following code did not run on my Mac OS 10.5.5 PowerPC with ghc 6.8.3 and HOpenGL 2.2.1.1 but the code after the next paragraph does run (YMMV). The discrepancy seems to be the line "<hask>displayCallback $= clear [ ColorBuffer ]</hask>" which is needed at least for the Mac version to compile.<br />
<haskell><br />
import Graphics.Rendering.OpenGL<br />
import Graphics.UI.GLUT<br />
main = do <br />
(progname, _) <- getArgsAndInitialize<br />
createWindow "Hello World"<br />
mainLoop<br />
</haskell><br />
This code throws up a window, with a given title. Nothing more happens, though. This is the skeleton that we'll be building on to. Save it to HelloWorld.hs and compile it by running <hask>ghc -package GLUT HelloWorld.hs -o HelloWorld</hask>.<br />
Note: GHCI has problems running this simple program on some platforms.<br />
<br />
However, as a skeleton, it is profoundly worthless. It doesn't even redraw the window, so we should definitely make sure to have a function that takes care of that in there somewhere. Telling the OpenGL-system what to do is done by using state variables, and these, in turn are handled by the datatype Data.IORef. So we modify our code to the following:<br />
<haskell><br />
import Graphics.Rendering.OpenGL<br />
import Graphics.UI.GLUT<br />
main = do<br />
(progname, _) <- getArgsAndInitialize<br />
createWindow "Hello World"<br />
displayCallback $= clear [ ColorBuffer ]<br />
mainLoop<br />
</haskell><br />
This sets the global state variable carrying the callback function responsible for drawing the window to be the function that clears the color state. Save this to the HelloWorld.hs, recompile, and rerun. This program no longer carries the original pixels along, but rather clears everything out.<br />
<br />
The displayCallback is a globally defined IORef, which can be accessed through a host of functions defined in Data.IORef. However, deep within the OpenGL code, there are a couple of definition providing the interface functions $= and get to fascilitate interactions with these. Thus we can do things like:<br />
<haskell><br />
height = newIORef 1.0<br />
currentheight <- get height<br />
height $= 1.5<br />
</haskell><br />
<br />
==Using the drawing canvas==<br />
So, we have a window, we have a display callback that clears the canvas. Don't we want more out of it? Sure we do. So let's draw some things.<br />
<haskell><br />
import Graphics.Rendering.OpenGL<br />
import Graphics.UI.GLUT<br />
myPoints :: [(GLfloat,GLfloat,GLfloat)]<br />
myPoints = map (\k -> (sin(2*pi*k/12),cos(2*pi*k/12),0.0)) [1..12]<br />
main = do <br />
(progname, _) <- getArgsAndInitialize<br />
createWindow "Hello World"<br />
displayCallback $= display<br />
mainLoop<br />
display = do <br />
clear [ColorBuffer]<br />
renderPrimitive Points $ mapM_ (\(x, y, z)->vertex$Vertex3 x y z) myPoints<br />
flush<br />
</haskell><br />
<br />
Now, the important thing to notice in this codeextract is that last line. It starts a rendering definition, gives the type to be rendered, and then a sequence of function calls, each of which adds a vertex to the rendering canvas. The statement is basically equivalent to something along the lines of<br />
<haskell><br />
renderPrimitive Points do<br />
vertex Vertex3 ...<br />
vertex Vertex3 ...<br />
</haskell><br />
for appropriate triples of coordinate values at the appropriate places. This results in the rendition here:<br />
<br />
[[image:OG-Points.png]]<br />
<br />
We can replace Points with other primitives, leading to the rendering of:<br />
<br />
===Triangles===<br />
[[image:OG-Triangles.png]]<br />
<br />
Each three coordinates following each other define a triangle. The last n mod 3 coordinates are ignored.<br />
<br />
Keyword Triangles<br />
<br />
===Triangle strips===<br />
[[image:OG-Trianglestrip.png]]<br />
<br />
Triangles are drawn according to a “moving window” of size three, so the two last coordinates in the previous triangle become the two first in the next triangle.<br />
<br />
Keyword TriangleStrip<br />
<br />
===Triangle fans===<br />
[[image:OG-Trianglesfan.png]]<br />
<br />
Triangle fans have the first given coordinate as a basepoint, and takes each pair of subsequent coordinates to define a triangle together with the first coordinate.<br />
<br />
Keyword TriangleFan<br />
<br />
===Lines===<br />
[[image:OG-Lines.png]]<br />
<br />
Each pair of coordinates define a line.<br />
<br />
Keyword Lines<br />
<br />
===Line loops===<br />
[[image:OG-Lineloop.png]]<br />
<br />
With line loops, each further coordinate defines a line together with the last coordinate used. Once all coordinates are used up, an additional line is drawn back to the beginning.<br />
<br />
Keyword LineLoop<br />
<br />
===Line strips===<br />
[[image:OG-Linestrip.png]]<br />
<br />
Line strips are like line loops, only without the last link added.<br />
<br />
Keyword LineStrip<br />
<br />
===Quadrangles===<br />
[[image:OG-Quad.png]]<br />
<br />
For the Quads keyword, each four coordinates given define a quadrangle.<br />
<br />
Keyword Quads<br />
<br />
===Quadrangle strips===<br />
[[image:OG-Quadstrip.png]]<br />
<br />
And a Quadstrip works as the trianglestrip, only the window is 4 coordinates wide and steps 2 steps each time, so each new pair of coordinates attaches a new quadrangle to the last edge of the last quadrangle.<br />
<br />
Keyword QuadStrip<br />
<br />
===Polygon===<br />
[[image:OG-Polygon.png]]<br />
<br />
A Polygon is a filled line loop. Simple as that!<br />
<br />
Keyword Polygon<br />
<br />
There are more things we can do on our canvas than just spreading out coordinates. Within the command list constructed after a renderPrimitive, we can give several different commands that control what things are supposed to look like, so for instance we could use the following:<br />
<haskell><br />
display = do <br />
clear [ColorBuffer]<br />
renderPrimitive Quads $ do<br />
color $ (Color3 (1.0::GLfloat) 0 0)<br />
vertex $ (Vertex3 (0::GLfloat) 0 0)<br />
vertex $ (Vertex3 (0::GLfloat) 0.2 0)<br />
vertex $ (Vertex3 (0.2::GLfloat) 0.2 0)<br />
vertex $ (Vertex3 (0.2::GLfloat) 0 0)<br />
color $ (Color3 (0::GLfloat) 1 0)<br />
vertex $ (Vertex3 (0::GLfloat) 0 0)<br />
vertex $ (Vertex3 (0::GLfloat) (-0.2) 0)<br />
vertex $ (Vertex3 (0.2::GLfloat) (-0.2) 0)<br />
vertex $ (Vertex3 (0.2::GLfloat) 0 0)<br />
color $ (Color3 (0::GLfloat) 0 1)<br />
vertex $ (Vertex3 (0::GLfloat) 0 0)<br />
vertex $ (Vertex3 (0::GLfloat) (-0.2) 0)<br />
vertex $ (Vertex3 ((-0.2)::GLfloat) (-0.2) 0)<br />
vertex $ (Vertex3 ((-0.2)::GLfloat) 0 0)<br />
color $ (Color3 (1::GLfloat) 0 1)<br />
vertex $ (Vertex3 (0::GLfloat) 0 0)<br />
vertex $ (Vertex3 (0::GLfloat) 0.2 0)<br />
vertex $ (Vertex3 ((-0.2::GLfloat)) 0.2 0)<br />
vertex $ (Vertex3 ((-0.2::GLfloat)) 0 0)<br />
flush<br />
</haskell><br />
in order to produce these four coloured squares:<br />
<br />
[[image:OG-Colorsquares.png]]<br />
<br />
where each color command sets the color for the next item drawn, and the vertex commands give vertices for the four squares.<br />
<br />
==Callbacks - how we react to changes==<br />
We have already seen one callback in action: displayCallBack. The Callbacks are state variables of the HOpenGL system, and are called in order to handle various things that may happen to the place the drawing canvas lives. For a first exercise, go resize the latest window you've used. Go on, do it now.<br />
<br />
I bet it looked ugly, didn't it?<br />
<br />
This is because we have no code handling what to do if the window should suddenly change. Handling this is done in a callback, residing in the IORef reshapeCallback. Similarily, repainting is done in displayCallback, keyboard and mouse input is in keyboardMouseCallback, and so on. We can refer to the HOpenGL documentation for [http://hackage.haskell.org/packages/archive/GLUT/latest/doc/html/Graphics-UI-GLUT-Callbacks-Window.html window callbacks] and for [http://hackage.haskell.org/packages/archive/GLUT/latest/doc/html/Graphics-UI-GLUT-Callbacks-Global.html global callbacks]. Window callbacks are things like display, keyboard and mouse, and reshape. Global callbacks deal with timing issues (for those snazzy animations) and the menu interface systems.<br />
<br />
In order for a callback to possibly not be defined, most are typed within the Maybe monad, so by setting the state variable to Nothing, a callback can be disabled. Thus, setting callbacks is done using the keyword Just. We'll add a callback for reshaping the window to our neat code, changing the main function to:<br />
<haskell><br />
main = do <br />
(progname, _) <- getArgsAndInitialize<br />
createWindow "Hello World"<br />
displayCallback $= display<br />
reshapeCallback $= Just reshape<br />
mainLoop<br />
reshape s@(Size w h) = do<br />
viewport $= (Position 0 0, s)<br />
postRedisplay Nothing<br />
</haskell><br />
<br />
Here, the code for the reshape function resizes the viewport so that our drawing area contains the entire new window. After setting the new viewport, it also tells the windowing system that something has happened to the window, and that therefore, the display function should be called.<br />
<br />
==Summary==<br />
So, in conclusion, so far we can display a window, post basic callbacks to get the windowhandling to run smoothly, and draw in our window. Next installment of the tutorial will bring you 3d drawing, keyboard and mouse interactions, the incredible power of matrices and the ability to rotate 3d objects for your leisure. Possibly, we'll even look into animations.<br />
<br />
[[OpenGLTutorial2|Continue with part 2]]</div>Laurentg