Home   Donators   Contact Us       

<< Previous Tutorial     Next Tutorial >>

Tutorial 11 - Quaternions

Theory

The details of quaternion mathematics is beyond the scope of this tutorial, and we will only concentrate on highlighting the effective use of quaternions in your 3D engine, when designing and rendering 3D scenes with multiple models. We've pre-written a quaternion class for your convenience, of which the use will become clear during the tutorial. If you however have a deep craving to understand the maths of quaternions, we recommend the following Wikipedia link:

en.wikipedia.org/wiki/Quaternion

A quaternion is basically an axis in 3D space with a angle of rotation around the axis. Four values make up a quaternion, namely x, y, z and w. Three of the values are used to represent the axis in vector format, and the forth value would be the angle of rotation around the axis.

This "axis-angle-combo" (aka quaternion) can be used to represent any possible rotation in 3D space.

Another amazing feature of quaternions are that when you have two quaternions, and you multiply them with each other, the rotations are "merged" into a single rotation. If we want to apply successive rotations to a model, we simply multiply all the respective quaternions representing these rotations with each other, and end up with a single quaternion that we can use during the rendering phase of our scene.

When rotating quaternions, we define three types of rotation actions, pitch, yaw and roll. The image below illustrates the meaning of these rotations.

Tutorial Steps
Tutorial created with Real Studio 2011 Release 4.3.
1. Open Real Studio.
2. Choose the "Desktop" project template.
3. Add an OpenGLSurface control to Window1.
4. Resize and position OpenGLSurface1 to fill the whole form.
5. Tick the LockRight and LockBottom properties of OpenGLSurface1.
6. Save your project.
7. Download the following files below and save them next to your project file:
8. Import the classes and modules into your new project file. (Select File > Import... from the main menu)
9. Add a property named "MousePrevX" of type Integer to Window1.
10. Add a property named "MousePrevY" of type Integer to Window1.
11. Add a property named "Scene" of type R3DTScene to Window1.
12. Add the following code to the Open event of Window1:
Dim model As R3DTModel ' tempory model object

' instantiate the Scene object

Scene = new R3DTScene

' instantiate a new axis compass model

model = R3DT_GetModel_AxisCompass
Scene.Model.Append model ' add the model to our scene
13. Add the following code to the Paint event of Window1:
' refresh the OpenGL surface

OpenGLSurface1.Render
14. Add the following code to the Open event of OpenGLSurface1:
R3DT_OGLInitialize ' initalize OpenGL environment

' instantiate memory blocks used to configure our light with

Dim light_position As new MemoryBlock(16)
Dim light_ambience As new MemoryBlock(16)
Dim light_diffANDspec As new MemoryBlock(16)

' define position of the light

light_position.SingleValue(0) = -3.0
light_position.SingleValue(4) = 0.0
light_position.SingleValue(8) = 3.0
light_position.SingleValue(12) = 0.0

' define ambience of the light

light_ambience.SingleValue(0) = 0.0
light_ambience.SingleValue(4) = 0.0
light_ambience.SingleValue(8) = 0.0
light_ambience.SingleValue(12) = 1.0

' define diffuse and specular of the light

light_diffANDspec.SingleValue(0) = 1.0
light_diffANDspec.SingleValue(4) = 1.0
light_diffANDspec.SingleValue(8) = 1.0
light_diffANDspec.SingleValue(12) = 1.0

' apply light settings

OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, light_position ' set position
OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, light_ambience ' set ambience
OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, light_diffANDspec ' set diffuse
OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, light_diffANDspec ' set specular

OpenGL.glEnable OpenGL.GL_LIGHT0 ' enable our light source

' enable overwriting of material properties with vertex colors

OpenGL.glEnable OpenGL.GL_COLOR_MATERIAL
15. Add the following code to the Resized event of OpenGLSurface1:
' set the viewport rectangle

OpenGL.glViewport 0, 0, OpenGLSurface1.Width, OpenGLSurface1.Height

' set up the perspective projection settings

OpenGL.glMatrixMode OpenGL.GL_PROJECTION
OpenGL.glLoadIdentity
OpenGL.gluPerspective 60.0, OpenGLSurface1.Width / OpenGLSurface1.Height, 1, 100.0

' select and reset the modelview matrix

OpenGL.glMatrixMode OpenGL.GL_MODELVIEW
OpenGL.glLoadIdentity
16. Add the following code to the Render event of OpenGLSurface1:
' render the Scene with OpenGL

R3DT_OGLRenderScene Scene
17. Add the following code to the MouseDown event of OpenGLSurface1:
' save the mouse position

MousePrevX = x
MousePrevY = y

return true
18. Add the following code to the MouseDrag event of OpenGLSurface1:
Dim model As R3DTModel ' temporary model object

model = Scene.Model(0) ' get the first model in the scene

' apply a pitch and yaw rotation using the mouse x and y movement

model.Rotation.RotateProjection((y - MousePrevY), (x - MousePrevX)) 

OpenGLSurface1.Render ' refresh the OpenGL surface

' save the mouse position

MousePrevX = x
MousePrevY = y
19. Save and run the project.
20. Click down on the OpenGL surface with your left mouse button and hold down the button. Drag the mouse cursor around over the surface and see what happens.

Code Analysis

The R3DTQuaternion class provides a way to store information about rotations in 3D space. The easiest way to initialize a quaternion, is to call its SetEulerRotation method with the axis and the angle around the axis, of the rotation you want the quaternion to represent.

The R3DTQuaternion.Rotate method rotates the quaternion with the given pitch, yaw and roll values. This is the main method used to animate models with rotations. With the Rotate method you are able to rotate any model or vector in any imaginable 3D direction.

R3DTQuaternion.RotateProjection is similar to R3DTQuaternion.Rotate, with the difference being that the rotation is based on the quaternion's projected image on a 2D surface (e.g. computer screen). A good example for the use of RotateProjection, is when you use your 2D mouse input to rotate a model. When you move your mouse cursor, there are only x and y values that changes, making it impossible to completely determine the pitch, yaw and roll values. RotateProjection only requires a x-angle and a y-angle to perform rotation.

To summarize, R3DTQuaternion.Rotate is most commonly used to rotate models in 3D space, while R3DTQuaternion.RotateProjection might be used in a 3D editing program that uses the mouse cursor and screen for rotation input.

The new "Rotation" property, added to the R3DTModel class, is a R3DTQuaternion object type. This "Rotation" property is used during the rendering algorithm to rotate the model according to the settings in the quaternion, before rendering it to the screen. To give your model a 40°-Roll rotation, is now as simple as making the call:

MyModel.Rotation.Rotate(0,0,40)

Note that all angle values are always given in degrees (not radians).

On line 7 in the OpenGLSurface1.MouseDrag event, you can see how we use the R3DTQuaternion.RotateProjection method to give our model a simultaneous pitch and yaw rotation, using the movement of the mouse.

Another valuable method to point out is the R3DTVector.Rotate method that takes a R3DTQuaternion object as a parameter. The purpose of this method is self-explanatory... it provides you with an easy way to rotate a vector in 3D space using quaternions.

The mathematics involved with quaternions can be intimidating, but the R3DTQuaternion class provides you with everything you need to apply 3D rotations to your models. Experiment with the R3DTQuaternion.Rotate and R3DTQuaternion.RotateProjection methods to get a good understanding of the difference between the two.

Project Downloads

<< Previous Tutorial     Next Tutorial >>


 
All the content on Real 3D Tutorials, with the exception of the SyntaxHighlighter which is licensed under the MIT License, is provided to the public domain and everyone is free to use, modify, republish, sell or give away this work without prior consent from anybody. Content is provided without warranty of any kind. Under no circumstances shall the author(s) or contributor(s) be liable for damages resulting directly or indirectly from the use or non-use of the content.
Should you find the content useful and would like to make a contribution, you can show your support by making a donation.