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:
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.
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 scene13. Add the following code to the Paint event of Window1:
' refresh the OpenGL surface OpenGLSurface1.Render14. 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_MATERIAL15. 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.glLoadIdentity16. Add the following code to the Render event of OpenGLSurface1:
' render the Scene with OpenGL R3DT_OGLRenderScene Scene17. Add the following code to the MouseDown event of OpenGLSurface1:
' save the mouse position MousePrevX = x MousePrevY = y return true18. 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 = y19. Save and run the project.
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:
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.