Lektion 18: Quadrics
Mit sogenannten Quadratics lassen sich recht komplexe Objekte zeichnen, die
man mit den bisherigen Stand nur mit vielen unschönen FOR-Schleifen zeichnen
und texturieren könnte. Es wird der Code aus Lektion 7 erweitert.
#include <windows.h>// Header File For Windows
#include <stdio.h>// Header File For Standard Input/Output
#include <gl\gl.h>// Header File For The OpenGL32 Library
#include <gl\glu.h>// Header File For The GLu32 Library
#include <gl\glaux.h>// Header File For The GLaux Library
HDChDC=NULL;// Private GDI Device Context
HGLRChRC=NULL;// Permanent Rendering Context
HWNDhWnd=NULL;// Holds Our Window Handle
HINSTANCEhInstance;// Holds The Instance Of The Application
boolkeys[256];// Array Used For The Keyboard Routine
boolactive=TRUE;// Window Active Flag Set To TRUE By Default
boolfullscreen=TRUE;// Fullscreen Flag Set To Fullscreen Mode By Default
boollight;// Lighting ON/OFF
boollp;// L Pressed?
boolfp;// F Pressed?
bool sp;// Spacebar Pressed?( NEW )
intpart1;// Start Of Disc( NEW )
intpart2;// End Of Disc( NEW )
intp1=0;// Increase 1( NEW )
intp2=1;// Increase 2( NEW )
GLfloatxrot;// X Rotation
GLfloatyrot;// Y Rotation
GLfloat xspeed;// X Rotation Speed
GLfloat yspeed;// Y Rotation Speed
GLfloatz=-5.0f;// Depth Into The Screen
GLUquadricObj *quadratic;// Storage For Our Quadratic Objects ( NEW )
GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };// Ambient Light Values
GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };// Diffuse Light Values
GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };// Light Position
GLuintfilter;// Which Filter To Use
GLuinttexture[3];// Storage for 3 textures
GLuint object=0;// Which Object To Draw( NEW )
LRESULTCALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);// Declaration For WndProc
Weiter zu InitGL(). Wir fügen 3 Zeilen hinzu um unser Quadratic zu initialisieren
(Am besten kurz vor dem "return true"). Die erste Zeile initialisiert
das Quadratic und speichert einen Zeiger auf die entsprechende Speicheradresse
in "quadratic". Die zweite Zeile erstellt Normalen für das Objekt,
damit die Beleuchtung funktioniert. Auch Texturmapping muss für dieses
Objekt aktiviert werden (3. Zeile).
quadratic=gluNewQuadric();// Create A Pointer To The Quadric Object ( NEW )
gluQuadricNormals(quadratic, GLU_SMOOTH);// Create Smooth Normals ( NEW )
gluQuadricTexture(quadratic, GL_TRUE);// Create Texture Coords ( NEW )
Die folgenden Zeilen sollten noch bekannt sein, zum Vergleich wird auch der Würfel
aus Lektion 7 nocheinmal ausgegeben.
GLvoid glDrawCube()// Draw A Cube
{
glBegin(GL_QUADS);// Start Drawing Quads
// Front Face
glNormal3f( 0.0f, 0.0f, 1.0f);// Normal Facing Forward
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);// Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);// Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);// Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);// Top Left Of The Texture and Quad
// Back Face
glNormal3f( 0.0f, 0.0f,-1.0f);// Normal Facing Away
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);// Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);// Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);// Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);// Bottom Left Of The Texture and Quad
// Top Face
glNormal3f( 0.0f, 1.0f, 0.0f);// Normal Facing Up
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);// Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);// Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);// Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);// Top Right Of The Texture and Quad
// Bottom Face
glNormal3f( 0.0f,-1.0f, 0.0f);// Normal Facing Down
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);// Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);// Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);// Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);// Bottom Right Of The Texture and Quad
// Right face
glNormal3f( 1.0f, 0.0f, 0.0f);// Normal Facing Right
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);// Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);// Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);// Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);// Bottom Left Of The Texture and Quad
// Left Face
glNormal3f(-1.0f, 0.0f, 0.0f);// Normal Facing Left
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);// Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);// Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);// Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);// Top Left Of The Texture and Quad
glEnd();// Done Drawing Quads
}
Als nächstes die DrawGLScene-Funktion. Zuerst die üblichen Initialisierungen,
danach folgt eine simple Fallunterscheidung um verschiedene Objekte ausgeben
zu können, je nachdem welchenWert die Variable "object" hat.
Im ersten Fall wird der Würfel ausgegeben (Sollte ja noch bekannt sein).
int DrawGLScene(GLvoid)// Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear The Screen And The Depth Buffer
glLoadIdentity();// Reset The View
glTranslatef(0.0f,0.0f,z);// Translate Into The Screen
glRotatef(xrot,1.0f,0.0f,0.0f);// Rotate On The X Axis
glRotatef(yrot,0.0f,1.0f,0.0f);// Rotate On The Y Axis
glBindTexture(GL_TEXTURE_2D, texture[filter]);// Select A Filtered Texture
// This Section Of Code Is New ( NEW )
switch(object)// Check object To Find Out What To Draw
{
case 0:// Drawing Object 1
glDrawCube();// Draw Our Cube
break;// Done
Das zweite Objekt ist ein Zylinder. Der erste Parameter (1.0f) legt den Radius
an seiner Basis fest. Der zweite Parameter (auch 1.0f) ist der Radius an der
Oberseite, der dritte legt die Höhe des Körpers fest. Die letzten
beiden Werte sind ein Mass für die Dichte des Gitternetzes aus dem der
Zylinder besteht, je höher diese ausfallen, destso weniger kantig wirkt
das Objekt.
case 1:// Drawing Object 2
glTranslatef(0.0f,0.0f,-1.5f);// Center The Cylinder
gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);// Draw Our Cylinder
break;// Done
Das dritte mögliche Objekt sieht ungefähr aus wie eine CD. Der erste
Parameter ist der Radius des Lochs in der Mitte der Scheibe, ist dieser 0.0f,
gibts dieses natürlich nicht. 1.5f steht für den äußeren
Radius, dieser sollte natürlich größer ausfallen als der innere.
Die letzten beiden Parameter geben wieder die Dichte des Gitters an.
case 2:// Drawing Object 3
gluDisk(quadratic,0.5f,1.5f,32,32);// Draw A Disc (CD Shape)
break;// Done
Und nun, die sollte hier nicht fehlen, eine Kugel. Der erste Parameter beschreibt
den Radius, die letzten beiden die Dichte des Drahtgitters.
case 3:// Drawing Object 4
gluSphere(quadratic,1.3f,32,32);// Draw A Sphere
break;// Done
Mithilfe der Zylinderfunktion lassen sich auch Kegel erstellen, indem der Radius
"oben" oder "unten" auf 0.0f gesetzt wird, das fünfte
Objekt ensteht.
case 4:// Drawing Object 5
glTranslatef(0.0f,0.0f,-1.5f);// Center The Cone
gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);// A Cone With A Bottom Radius Of 0.0f
break;// Done
Das letzte Objekt wird animiert indem eine Scheibe partiell ausgegeben wird.
gluPartialDisk gleicht in den ersten 5 Parametern gluDisk, die letzten beiden
werden in Grad (0-360) angegeben und bestimmen wieviel von der Scheibe zusehen
sein soll ( Bei 0 und 360 wäre alles zusehen, bei 0 und 180 genau die Hälfte
der Scheibe). Um Bewegung hinnein zubringen werden diese Werte bei jedem Durchlauf
verändert.
case 5:// Drawing Object 6
part1+=p1;// Increase Start Angle
part2+=p2;// Increase Sweep Angle
if(part1>359)// 360 Degrees
{
p1=0;// Stop Increasing Start Angle
part1=0;// Set Start Angle To Zero
p2=1;// Start Increasing Sweep Angle
part2=0;// Start Sweep Angle At Zero
}
if(part2>359)// 360 Degrees
{
p1=1;// Start Increasing Start Angle
p2=0;// Stop Increasing Sweep Angle
}
gluPartialDisk(quadratic,0.5f,1.5f,32,32,part1,part2-part1);// A Disk Like The One Before
break;// Done
};
xrot+=xspeed;// Increase Rotation On X Axis
yrot+=yspeed;// Increase Rotation On Y Axis
return TRUE;// Keep Going
}
In KillGLWindow muss der Speicher des Quadratics wieder freigegeben werden,
dies passiert mit gluDeleteQuadric.
GLvoid KillGLWindow(GLvoid)// Properly Kill The Window
{
gluDeleteQuadric(quadratic);// Delete Quadratic - Free Resources
(...)
Zum Schluss müssen nur noch die Tastatureingaben verarbeitet werden, diese
Zeilen müssen in die entspechende Funktion aus Lektion 7 eingefügt
werden.
if (keys[' '] && !sp)// Is Spacebar Being Pressed?
{
sp=TRUE;// If So, Set sp To TRUE
object++;// Cycle Through The Objects
if(object>5)// Is object Greater Than 5?
object=0;// If So, Set To Zero
}
if (!keys[' '])// Has The Spacebar Been Released?
{
sp=FALSE;// If So, Set sp To FALSE
}
Und das wars auch schon, viel Spaß beim rumprobieren:
Jeff Molofee (NeHe) http://nehe.gamedev.net
Die Source Codes und Ausführbaren Dateien zu den Kursen liegen auf der Neon
Helium Website
Übersetzt und leicht modifiziert von Hans-Jakob Schwer 23.10.2k2, www.codeworx.org