(2) Mathematische Grundlagen
Vorlesung
„Computergraphik I“
S. Müller
Plan
18.10.12 1 Einführung OpenGL 10.01.13 15 Texturen (Ü8) 25.10.12 2 Mathe Grundlagen C++ 1 (Ü1) 11.01.13 16 Advanced OpenGL 25.10.12 3 Rasterisierung C++ 2 (Ü2) 17.01.13 17 Kurven (Ü9) 01.11.12 4 Linien C++ 3 (Ü2) 18.01.13 18 Splines (Ü9)
01.11.12 (Allerheiligen) 24.01.13 19 Ray Tracing
08.11.12 5 Clipping (Ü3) 25.01.13 20 Ray Tracing 2
08.11.12 6. Poly Clipping (Ü4) 31.01.13 Wiederholung
15.11.12 7 Scan Konvertierung 01.02.13 -
15.11.12 8 Transformationen (Ü5) 07.02.13 - 22.11.12 9 Transform & OpenGL (Ü5) 08.02.13 Klausur 22.11.12 10 View (Ü6)
29.11.12 11 Perspektive (Ü6) 29.11.12 12 OpenGL Pipeline (Ü7) 06.12.12 13 Licht & Material (Ü7) 06.12.12 14 Beleuchtung (Ü7) 07.12.12 Weihnachtsvorlesung
Was ist OpenGL ?
Open Graphics Library
Grafik Bibliothek zur Darstellung von 3D Objekten
Polygone, Materialien, Lichtquellen, Kamera, ….
Hintergrund / Geschichte
… auf der Suche nach einer einheitlichen Software- Schnittstelle (API: Application Programming Interface) zur Programmierung von Graphiksystemen
Standardisierungsbemühungen
GKS, PHIGS…
„Proprietäre Systeme“
HP: Starbase, SGI: GL (Graphics Library)
Gewinner: SGI mit GL in Verbindung mit sehr guter Hardware
OpenGL (1992, Mark Segal & Kurt Akeley)
OpenGL
OpenGL ist ein Software Interface für Graphik
Hardware mit ca. 250 verschiedenen Kommandos
Hardware unabhängig
warum „Open“?
offen für Lizenznehmer
verwaltet vom Architecture Review Board (ARB)
• NVIDIA, ATI, IBM, Intel, SGI, ….
von jedem Lizenznehmer erweiterbar (Extension)
Nicht dabei:
Handhabung von Fenstern/Windows
Benutzereingabe
Versionen
OpenGL 1.0 wurde 1992 verabschiedet
OpenGL 1.1 1996
(wesentliche Neuerungen:
Texture Objects & Vertex Arrays)
OpenGL 1.2 1998
(wesentliche Neuerungen:
Imaging Extensions)
OpenGL 1.3 2001
(Erweiterungen für Texturen)
OpenGL 1.4 2002 (Depth
OpenGL 1.5 2003 (Buffer Objects, Occlusion Query, Shadow Functions)
OpenGL 2.0 (2004) : GLSL, Multiple Render Targets und non-Power-of-Two Texturen
OpenGL 2.1 (2006) Pixel Buffer Objects (schnellere Übertragung von GPU->
CPU), GLSL 1.2 und sRGB Texturen
OpenGL 3.0 (2008) GLSL 1.3, kein begin/end mehr,
keine Fixed Function Pipeline (alles shader), Architektur
Einführung
OpenGL Core: Basisprimitive (Punkte, Linien, Polygone…)
Darauf aufbauend gibt es diverse Tools:
OpenGL Utility Library (GLU): standardmaessig dabei für Oberflächen (Quadrics, NURBS…)
OpenGL ist eine „State-Machine“
Man versetzt die „Maschine“ in einen Zustand, der so lange besteht, bis er wieder verändert wird
Beispiel: ab jetzt alles rot, ab jetzt dieses Material, ab jetzt diese Transformation
Effizienter, als Daten jedes Mal neu zu übergeben
OpenGL Grundstruktur
Low-Level-API
Hardware-nah aber Hardware-unabhängig
2 Arten von Funktionen
Zustand ändern
Primitive darstellen
immediate mode System
sehr einfache Befehle
Direktes Durchreichen an die HW möglich
Dreiecks-basiert, keine interne Repräsentation der Szene
klarer Namensraum
Beispiel
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
glBegin(GL_POLYGON);
glVertex3f(0.25, 0.25, 0.0);
glVertex3f(0.75, 0.25, 0.0);
glVertex3f(0.75, 0.75, 0.0);
glVertex3f(0.25, 0.75, 0.0);
glEnd();
glFlush();
Bestimmt die Farbe, mit der das Bild gelöscht wird
Löscht das Bild, genauer den Color Buffer (alternativ z-Buffer etc.)
Setzt die Farbe auf weiß Definiert das
Koordinatensystem für das Rendering
Definiert ein zu zeichnendes Objekt, hier ein Polygon mit 4 Ecken
Sorgt dafür, daß die
Zeichenkommandos auch ausgeführt werden, anstatt in einem Puffer auf weitere
GLUT, the OpenGL Utility Toolkit
OpenGL ist unabhängig vom Betriebssystem oder Window-Manager
Leider läuft keine Graphikapplikation ohne dass man zumindest ein Fenster öffnet oder einfache
Eingabewerte (Tastatur/Maus) abfragt
GLUT: Ganz gut für den Start…
Beispiel
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (250, 250);
glutInitWindowPosition (100, 100);
glutCreateWindow ("hello");
init ();
glutDisplayFunc(display);
glutMainLoop();
return 0; /* ANSI C */
}
Initialisiert GLUT
RGBA oder Color-Index Mode, Single oder Double Buffer etc.
Position und Größe des Fensters
Das Fenster mit GL-Kontext wird erzeugt, allerdings erst durch glutMainLoop dargestellt Display Callback Funktion:
sobald GLUT ein Neuzeichnen des Fensterinhalts für nötig hält, wird diese CB-Funktion
aufgerufen
Event-getriggerte Hauptschleife
Beispiel: hello.c
Hello.exe
hello.vcproj
GLUT
Initialisierung
glutInit(int *argc, char **argv);
glutInitDisplayMode( ... );
glutCreateWindow(„name“);
Zeichenfunktion registrieren
glutDisplayFunc( void (*func)(void) );
Neuzeichnen anmelden
glutPostRedisplay();
GLUT
Interaktionsfunktionen
glutMouseFunc(void (*func)(int button, int state, int x, int y) );
glutKeyboardFunc(void (*func)(unsigned char key, int x, int y) );
glutMotionFunc(void (*func)(int x, int y) );
Hilfsfunktionen
glutReshapeFunc(void (*func)(int width, int height) );
glutIdleFunc( void (*idle)(void) );
Hauptschleife
glutMainLoop();
GLUT
Hilfsfunktionen für Primitive
void glutWireSphere(float radius, int slices, int stacks);
void glutWireTorus(float inner_radius, float outer_radius, int sides, int rings) ;
void glutWireTeapot(double size);
Frage …
Gegeben sei ein Vektor
Der Betrag ist ?
Gegeben sei ein rechtwinkliges Dreieck
Der sinα ist?
= z y x
a x x
z
y
a y
z
α
h a
Trigonometrie
sin, cos, tan
α
b
h a
h b Hypotenuse
Ankathete
= α =
cos
h a Hypotenuse
te Gegenkathe
= α =
sin
b a Ankathete
te
Gegenkathe = =
= α
α α
cos tan sin
GAGA
HHAG
Beispiel I
Wir blicken senkrecht auf den Mittelpunkt eines Bildschirms.
Die Distanz vom „Augpunkt“
zum Bildschirmmittelpunkt sei d = 20 cm
Der Bildschirm hat eine Größe b x h von 20x20 cm
Wie groß ist der horizontale Öffnungswinkel der Kamera
h b
d α
d b d
b
2 tan α = 2 =
=
d b arctan 2 α
Öffnungswinkel: 2 ⋅ α = 53 , 13 °
Beispiel II
Fläche in einem Parallelogramm
α
b h
a
h a A = ⋅
b
= h α sin
α
⋅ sin
= b h
Fläche in einem Dreieck
α 2 sin
1 ⋅ ⋅ ⋅
= a b
A α
b
a
Lineare Algebra
Vektoren
Gegeben sind die Punkte A und C
Gesucht ist der Vektor:
Was ist?
Was ist der Unterschied
zwischen einem Punkt C und
x z
y
0
A C
AC
A C
AC = − OC
c
( C
xC
yC
z)
C = , ,
beschreibt die Koordinaten des
Punktes in einem Koordinatensystem und ist im Prinzip eine
Kurzschreibweise für:
Vektoren
=
z y x
a a a a
=
z y x
b b b
b α
a
b
a a
a a
a =
x2+
y2+
z2= Betrag:
z z y
y x
x
b a b a b
a b
a b
a = ⋅ ⋅ cos α = + +
Skalarprodukt:
Ergebnis ist ein Skalar
Senkrechte Projektion
Senkrechte Projektion:
Was ist, wenn der Vektor, auf den wir projizieren, bereits normiert ist?
dann berechnet sich die
senkrechte Projektion durch das Skalarprodukt
Oder anders herum: das Skalarprodukt lässt sich als
α
a
b b
a → b
b a a
b
a
=
⋅
=
→ cos α
= 1 b
b a b
a
→ =
a
xBeispiel
= 0 0 1 x
= 0 1 0 y
= 1 0 0 z
x z
y
=
z y x
a a a a
a
a
xx a =
a
ya
za
ya y =
= 0 y x
a = a a = a
xa
x+ a
ya
y+ a
za
z
22 2 2
2
a a a
a
x+
y+
z=
=
Die jeweilige Koordinate erhält man durch die senkrechte Projektion auf die
Koordinatenachsen. Sind diese normiert, braucht man hierfür nur das Skalarprodukt.
Kreuzprodukt
−
−
−
=
×
=
×
=
x y y
x
z x x
z
y z z
y
z y x
z y x
b a b
a
b a b
a
b a b
a b
b b a
a a b
a c
α
⋅ sin
⋅
=
×
= a b a b
c
Ergebnis ist ein Vektor, der
senkrecht auf beiden Vektoren steht
Beispiel I
= 0 0 1 x
= 0 1 0 y
= 1 0 0 z
x z
y
=
× y x
z
=
=
×
=
1 0 0 0
1 0 0
0 1
=
× x y
z
−
=
−
=
×
=
1 0 0 0
0 1 0
1 0
=
× x x
=
×
=
0 0 0 0
0 1 0
0 1
z ?
3D-Koordinatensysteme
x
z y
x
y z
Linke-Hand-System (Linkssystem)
Rechte-Hand-System
(Rechtssystem)
C++ kompakt - Teil 1
Matthias Biedermann
Dank an Markus Geimer
Programmorganisation (1)
Ein C++ Projekt kann auf mehrere Quelldateien verteilt werden.
Dabei muss in genau einer Quelldatei die Funktion main (Hauptprogramm) enthalten sein.
Vor der Verwendung einer Funktion/Klasse muss diese definiert (nicht implementiert!) sein, d.h. bei Funktionen muss dem
Compiler
- der Name,
- der Rückgabetyp,
- sowie die Anzahl und Typen der Parameter bekannt sein.
Auslagern der Definitionen in eigene Dateien
Programmorganisation (2)
Für die Definition legt man eine “.h”-Datei (Header) an, für die Implementierung eine “.cpp”-Datei.
Möchte man eine Funktion oder Klasse verwenden, dann muss man die Header-Datei mit der Definition einbinden:
#include “name”
Zum Einbinden von System-Bibliotheken benutzt man die Variante
#include <name>
Die “...”-Variante sucht im aktuellen, die <...>-Form in den Systemverzeichnissen
Beispiel
magic.h
int doMagic(int max);
magic.cpp
#include “magic.h”
int doMagic(int max) {
int sum = 0;
for (int i=0; i < max; ++i) sum += i;
main.cpp
#include “magic.h”
int main() {
return doMagic(10);
}
Mehrfach-Includes
Wird eine Header-Datei mehrfach eingebunden (weil sie z.B. von mehreren anderen Headern benötigt wird), dann erhält man eine Fehlermeldung (“Symbol already defined”).
Lösung: Verwendung von Preprozessor-Befehlen zur bedingten Compilierung
#ifndef EINDEUTIGER_NAME
#define EINDEUTIGER_NAME // Inhalt der Header-Datei [...]
#endif
Klassendefinition
Allgemein:
class Name {
public:
// Öffentliche Komponenten
// (Konstruktoren, Methoden usw.) protected:
// Geschützte Komponenten private:
// Private Komponenten };
Beispiel
class Vector2D {
public:
float x() const;
float y() const;
void setX(float value);
void setY(float value);
protected:
float mElement[2];
};
Zeigt dem Compiler an, dass innerhalb der Methode keine Membervariablen verändert werden dürfen.
⇒ Zusätzliche Überprüfungen durch den Compiler möglich.
Klassenimplementierung
Da sich die Implementierung nicht innerhalb der Definition befindet, muss man irgendwie die Verbindung zur jeweiligen Klasse herstellen. Dazu dient folgende Syntax:
Rückgabetyp Klasse::Methode(Parameter) { // Implementierung
}
Die Implementierung einer Klasse kann ohne weiteres auf mehrere Quelldateien verteilt werden.
Beispiel
#include “Vector2D.h”
float Vector2D::x() const {
return mElement[0];
}
[...]
void Vector2D::setX(float value) {
mElement[0] = value;
}
Konstruktoren
Ein Konstruktor ist eine spezielle Methode ohne Rückgabewert, deren Namen mit dem der Klasse übereinstimmt.
Bei der Erzeugung einer Klasseninstanz wird nach der Speicher- reservierung automatisch der Konstruktor aufgerufen.
Definiert der Programmierer keinen eigenen Konstruktor, dann wird vom Compiler automatisch ein parameterloser Default- Konstruktor erzeugt, der aber keine Funktionalität besitzt.
Man kann auch mehrere Konstruktoren mit unterschiedlichen Parameterlisten für eine Klasse definieren. Es ist jedoch nicht möglich, aus einem Konstruktor heraus einen anderen der gleichen Klasse aufzurufen.
Workaround: Private Initialisierungsmethode, die von allen
Beispiel
Definition:
class Vector2D { public:
Vector2D();
Vector2D(float x, float y);
private:
void init(float x, float y);
};
Implementierung:
Vector2D::Vector2D() { init(0, 0);
}
Vector2D::Vector2D(float x, float y) { init(x, y);
}
void Vector2D::init(float x, float y) { mElement[0] = x;
mElement[1] = y;
}
Ein-/Ausgabe
C++ verwendet zur Ein-/Ausgabe s.g. Streams.
Vordefiniert sind im System-Headerfile iostream
cin (Standardeingabe)
cout (Standardausgabe)
cerr (Standardfehlerausgabe, gepuffert)
clog (Standardfehlerausgabe, ungepuffert)
Die Ein-/Ausgabe erfolgt mit Hilfe der Shiftoperatoren << und >>
Die Definitionen befinden sich alle im Namensraum std
Beispiel
#include <iostream>
using namespace std;
int main() {
int value;
cin >> value;
cout << “Eingegebener Wert: << value << endl;
return 0;
}
Stream-Definitionen einbinden und Namensraum std aktivieren
Neue Zeile beginnen