Spotlampe auf Heightmap: ein Tutorial für dynamisches Licht und einem Terrain.
Platformen:
Die auf dieser Seite aufgeführten Sourcecodes dürfen uneingeschränkt verwendet, kopiert, verändert und publiziert werden. Jegliche Haftung wird abgelehnt, die Verwendung des hier publizierten Materials geschieht auf eigenes Risiko.
Source: tutorial06.cpp
Paket: tutorial06.tgz
Dieses Demo zeigt ein Heightmap (Feld von Höhenwerten), welches von einer Lichtquelle, in diesem Fall eine Spotlampe, beschienen wird.
Das Handling der Lampe wird in einer eigenen Klasse abgehandelt. Alles Übrige ist wie bisher.
Das Programm
Die Klasse Light, welche alle nötigen Eigenschaften und Methoden kennt um verschiedene Lichtquellen zu erzeugen. In diesem konkreten Fall, brauchen wir eine Spotlampe.
class Light
{
private:
int id;
float pos[4];
float ambient[4];
float diffuse[4];
float specular[4];
float attenuation[3];
float spot_dir[3];
float spot_exp;
float spot_cutoff;
public:
Light(int);
Light & setPos(float, float, float);
Light & setSpotDir(float, float, float);
void init();
void set();
void draw();
};
Light::Light(int id)
: id(id)
{
// ....
}
Light & Light::setPos(float p0, float p1, float p2)
{
// ....
}
Light & Light::setSpotDir(float p0, float p1, float p2)
{
// ....
}
Die Initialisierung der Lichtquelle, setzt alle möglichen Parameter.
void Light::init()
{
glEnable(id);
glLightfv(id, GL_AMBIENT, ambient);
glLightfv(id, GL_DIFFUSE, diffuse);
glLightfv(id, GL_SPECULAR, specular);
glLightf(id, GL_SPOT_EXPONENT, spot_exp);
glLightf(id, GL_SPOT_CUTOFF, spot_cutoff);
glLightf(id, GL_CONSTANT_ATTENUATION, attenuation[0]);
glLightf(id, GL_LINEAR_ATTENUATION, attenuation[1]);
glLightf(id, GL_QUADRATIC_ATTENUATION, attenuation[2]);
}
Das Setzen einer Lichtquelle muss allerdings nur die Position der Quelle und die Richtung des Spots neu setzen.
void Light::set()
{
glPushMatrix();
glLightfv(id, GL_POSITION, pos);
glLightfv(id, GL_SPOT_DIRECTION, spot_dir);
glPopMatrix();
}
Die Methode draw zeichnet eine Linie von der Position der Lichtquelle in der Richtung des Spots. Dies wird zur Demonstration benötigt.
void Light::draw()
{
float p[3];
for (int i = 0; i < 3; ++i) p[i] = pos[i] + 10.0 * spot_dir[i];
glLineWidth(1.0);
glColor4f(1.0, 0.0, 0.0, 1.0);
glBegin(GL_LINES);
glVertex3fv(pos);
glVertex3fv(p);
glEnd();
}
Es folgen nun alle nötigen Definitionen und Deklarationen für das Heightmap: Breite (width
), Höhe (height
), anzahl Abteile in beiden Richtungen (nx
, ny
), die Heightmap selbst (h
) und die zugehörigen Normalen (n
).
static const float width = 20.0;
static const float height = 20.0;
static const int nx = 16;
static const int ny = 16;
float h[nx+1][ny+1][3];
float n[nx+1][ny+1][3];
Ferner benötigen wir noch eine Lichtquelle light0, den Zustand der Anzeige (Fullscreen oder Fenster) und Initialwerte für das Material der Heightmap.
Light light0(GL_LIGHT0);
int fullscreen = 0;
static float modelAmb[4] = {0.2, 0.2, 0.7, 1.0};
static float matAmb[4] = {0.2, 0.2, 0.3, 1.0};
static float matDiff[4] = {0.8, 0.8, 0.8, 1.0};
static float matSpec[4] = {0.4, 0.4, 0.4, 1.0};
static float matEmission[4] = {0.0, 0.0, 0.0, 1.0};
Die folgende Funktion berechnet die Heightmap. In diesem Fall ist es eine glatte Fläche. Um dies zu ändern müssen die Höhenwerte auf der Zeile 142 geändert werden, z.B. in Abhägigkeit der Koordinate: h[x][y][2] = sin(x);
. Will man auch nocht ein korrektes Shading, so müssen die Normalen (Zeilen 144-146) berechnet werden.
void calc_heightfield(void)
{
float dw = width / nx;
float dh = height / ny;
for (int x = 0; x <= nx; ++x)
{
for (int y = 0; y <= ny; ++y)
{
h[x][y][0] = x * dw;
h[x][y][1] = y * dh;
h[x][y][2] = 0.0;
n[x][y][0] = 0.0;
n[x][y][1] = 0.0;
n[x][y][2] = 1.0;
}
}
}
Zu der Zeichnungsfunktion display gibt es zwei Dinge anzumerken. Zum Einen sind es die Materialeigenschaften der Oberfläche (gegeben durch die Heigtmap, Zeilen 188-193), zum Andern sind es die Normalen die für jeden Punkt definiert werden (Zeilen 201-207).
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(20.0, -20.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_LIGHTING);
light0.set();
// surface
glMaterialfv(GL_FRONT, GL_AMBIENT, matAmb);
glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiff);
glMaterialfv(GL_FRONT, GL_SPECULAR, matSpec);
glMaterialfv(GL_FRONT, GL_EMISSION, matEmission);
glMaterialf(GL_FRONT, GL_SHININESS, 10.0);
glPushMatrix();
glLineWidth(1.0);
glTranslatef(-width/2.0, -height/2.0, 0.0);
for (int x = 0; x < nx; ++x)
{
glBegin(GL_TRIANGLES);
for (int y = 0; y < ny; ++y)
{
glNormal3fv(n[x][y]); glVertex3fv(h[x][y]);
glNormal3fv(n[x+1][y]); glVertex3fv(h[x+1][y]);
glNormal3fv(n[x+1][y+1]); glVertex3fv(h[x+1][y+1]);
glNormal3fv(n[x][y]); glVertex3fv(h[x][y]);
glNormal3fv(n[x+1][y+1]); glVertex3fv(h[x+1][y+1]);
glNormal3fv(n[x][y+1]); glVertex3fv(h[x][y+1]);
}
glEnd();
}
glPopMatrix();
glDisable(GL_LIGHTING);
// bounding box
glLineWidth(3.0);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor4f(0.8, 0.8, 0.8, 1.0);
glBegin(GL_QUADS);
glVertex3f(-width/2.0, -height/2.0, 0.0);
glVertex3f( width/2.0, -height/2.0, 0.0);
glVertex3f( width/2.0, height/2.0, 0.0);
glVertex3f(-width/2.0, height/2.0, 0.0);
glEnd();
// light ray
light0.draw();
glFlush();
glutSwapBuffers();
}
Die Funktion main beinhaltet eigentlich nichts Spektakuläres, Vollständigkeitshalber sind die Zeilen 253-263 zu erwähnen die Licht und Material konfigurieren.
int main(int argc, char ** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(0, 0);
glutInitWindowSize(800, 600);
glutCreateWindow("Tutorial 06: Spotlight and Heightmap");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutIdleFunc(idle);
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_NORMALIZE);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, modelAmb);
glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
glMaterialfv(GL_FRONT, GL_AMBIENT, matAmb);
glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiff);
glMaterialfv(GL_FRONT, GL_SPECULAR, matSpec);
glMaterialfv(GL_FRONT, GL_EMISSION, matEmission);
glMaterialf(GL_FRONT, GL_SHININESS, 10.0);
calc_heightfield();
light0.init();
glutMainLoop();
return 0;
}