Lösungsvorschlag zum dritten Übungsblatt Aufgabe 1 Wie im Skript x y ! z benutzen wir auch hier die Korrespondenz wx wy wz w zwischen gewöhnlichen 3D-Koordinaten und homogenen Koordinaten. Für die Berechunung der Projektion eines Punktes XA = (xA , yA , zA ) auf eine Ebene parallel zur yz-Ebene mit der x-Koordinate d stellt sich die Sachlage wie folgt dar: y b d b x XA yA XP yP z Man beachte, daß wegen der Rechtshändigkeit des Koordinatensystems die xAchse von der Bildebene aus nach hinten zeigt. Um das Bild des Punktes XA zu bestimmen, verbinden wir ihn mit dem Projektionszentrum, dem Ursprung Ω, und geben dem Schnittpunkt dieser Verbindungsstrecke mit der Ebene x = d den Namen XP . Dieser Punkt XP = (xP , yP , zP ) ist der gesuchte Bildpunkt. Aufgrund des Strahlensatzes gilt (beachte xP = d!) yP yA = d xA und analog zP zA = d xA Insgesamt haben wir also xp yp = zp d dyA xA dzA xA In homogenen Koordinaten können wir diesen Punkt auch als 1 dxA dyA dzA xA schreiben und aus dem Punkt xA in homogenen Koordinaten mittels Matrizenmultiplikation berechnen: dxA xA d 0 0 0 dyA 0 d 0 0 yA dzA = 0 0 d 0 · zA xA 1 1 0 0 0 Da eine Skalierung aller Koordinaten um den gleichen Faktor in unserer neuen Interpretation der homogenen Koordinaten keine Änderung in ihrem zugeordneten 3D-Punkt bewirkt, können wir die Transformationmatrix auch in der Form 1 0 0 0 0 1 0 0 Pd = 0 0 1 0 1 0 0 0 d schreiben. Für d = 0 müssten wir hier durch Null dividieren; geometrisch macht so eine Projektion wenig Sinn, da das Projektionszentrum in der Projektionsebene läge und die Projektion nur zu einem einzigen Punkt entarten würde. Aufgabe 2 Damit die Punkte A, B und C im Gegenuhrzeigersinn erscheinen, müssen die −−→ −→ → Vektoren AB, AC und der gesuchte Normalenvektor − n (in dieser Reihenfolge) → ein Rechtssystem bilden. Für den (noch nicht normierten) Normalenvektor − n erhalten wir also −2 − 2 6−2 −4 4 − − → − → → − n = AB × AC = 4 − 5 × −1 − 5 = −1 × −6 = 6−3 −1 − 3 3 −4 −1 · (−4) − 3 · (−6) 22 3 · 4 − (−4) · (−4) = −4 −4 · (−6) − (−1) · 4 28 und in seiner normierten Form lautet er 22 22 √ 2 1 2 2 −4 = √ 1 −4 1284 22 +(−4) +28 28 28 Um zu entscheiden, ob der gegebene Vektor von der Vorder- oder Rückfläche des Dreiecks wegzeigt, untersuchen wir, welchen Winkel er mit dem Normalen→ vektor − n einschließt. Liegt dieser Winkel zwischen 0◦ und 90◦ , so zeigt er von 2 der Vorderseite des Dreiecks weg, andernfalls von der Rückseite. Der Winkel → zwischen − n und dem gegebenen Vektor liegt genau dann zwischen 0◦ und 90◦ , falls das Skalarprodukt der beiden Vektoren positiv ist, wir berechnen also 4 → h− n , −1 i = 22 · 4 + (−4) · (−1) + 28 · (−4) = −20 −4 Der Vektor (4, −1, −4) zeigt also von der Rückseite des Dreiecks weg. Um den Winkel zwischen der Dreiecksfläche und dem Vektor zu bestimmen, berechnen → wir zunächst den Winkel zwischen dem Normalenvektor − n und dem Vektor (4, −1, −4) und erhalten: − h→ n ,(4,−1,−4)i → −20√ ∢(− n , (4, −1, −4)) = arccos k→ = arccos √1284· ≈ 95, 576◦ − n k·k(4,−1,−4)k 33 Der Winkel zwischen der Dreiecksfläche und dem Vektor (4, −1, −4) beträgt also ungefähr 5, 576◦. Aufgabe 3 Für diese Aufgabe haben wir mehrere Lösungsmöglichkeiten: 1.Ansatz: Ein Punkt P liegt genau dann im Inneren des von den Punkten A, B und C −→ aufgespannten Dreieckes, wenn sich der Vektor AP als Konvexkombination der −−→ −→ Vektoren AB und AC darstellen lässt, d.h., falls es λ1 und λ2 gibt mit λ1 , λ2 ≥ 0, −−→ −→ −→ λ1 + λ2 ≤ 1 und λ1 · AB + λ2 · AC = AP . Dies führt uns auf folgendes Gleichungssystem: xAB · λ1 + xAC · λ2 = xAP yAB · λ1 + yAC · λ2 = yAP Mit den Abkürzungen xAB xAC = xAB · yAC − yAB · xAC , det := yAB yAC xAP xAC = xAP · yAC − yAP · xAC det1 := yAP yAC x xAP = xAB · yAP − yAB · xAP det2 := AB yAB yAP und haben wir λ1 = λ2 = det1 det det2 det und Diese Werte dürfen wir aber nicht direkt berechnen, da sie im allgemeinen keine 3 ganzen Zahlen sind. Dafür untescheiden wir zwei Fälle: 1. Fall: det > 0: Damit λ1 , λ2 ≥ 0 gilt, muß hier det1 , det2 ≥ 0 gelten. Damit λ1 +λ2 ≤ 1 erfüllt ist, müssen det1 und det2 die Bedingung det1 + det2 ≤ det erfüllen. 2. Fall: det < 0: Hier müssen wir wegen der Division durch eine negative Zahl die Ungleichheitszeichen umdrehen; es muß hier det1 , det2 ≤ 0 und det1 + det2 ≥ det gelten. Im Falle von det = 0 entartet das Dreieck zu einer Strecke; hier erklären wir den Aufrufer zum Schuldigen und garantieren für nichts. Unsere Überlegungen können wir in folgender Funktion zusammenfassen: boolean liegt_innerhalb (int x1, int y1, int x2, int y2, int x3, int y3, int p_x, int p_y){ int det = (x2-x1)*(y3-y1)-(y2-y1)*(x3-x1); int det_1 = (p_x-x1)*(y3-y1)-(p_y-y1)*(x3-x1); int det_2 = (x2-x1)*(y_p-y1)-(y2-y1)*(x_p-x1); if (det>0){ return ((det_1>=0) & (det_2>=0) & ((det_1+det_2)<=det)); } else{ return ((det_1<=0) & (det_2<=0) & ((det_1+det_2)>=det)); } } 2. Ansatz: Hier beobachten wir, daß ein Punkt P genau dann im Inneren des von den Punkten A, B und C aufgespannten Dreiecks liegt, wenn beim Ablaufen der Kanten −−→ −−→ −→ AB, BC und CA der Punkt P immer auf der gleichen Seite des Beobachters liegt. Um hier rechts und links in Zahlen ausdrücken zu können, betrachten wir −−→ −−→ −→ −→ −−→ −−→ die Vektoren AB, BC, CA, AP , BP und CP als Vektoren im R3 , indem wir ih−−→ −→ nen einfach noch die z-Komponente 0 geben. In den Kreuzprodukten AB × AP , −→ −−→ −−→ −−→ BA× BP und CA× CP sind dann die x- und y-Koordinate Null, und wir können links und rechts einfach mit dem Vorzeichen der z-Koordinate identifizieren. Was genau links und rechts ist, braucht uns hier nicht zu interessieren; wichtig ist nur, ob das Vorzeichen immer gleich ist oder nicht. Falls die z-Komponente Null ergibt, so kann der Punkt auf einer Kante liegen, und wir behandeln diesen Fall dementsprechend: boolean liegt_innerhalb (int x1, int y1, int x2, int y2, 4 int x3, int y3, int p_x, int p_y){ int det_1 = (x2-x1)*(y_p-y1)-(x_p-x1)*(y2-y1); int det_2 = (x3-x2)*(y_p-y2)-(x_p-x2)*(y3-y2); int det_3 = (x1-x3)*(y_p-y3)-(x_p-x3)*(y1-y3); return (((det_1>=0) & (det_2>=0) & (det_3>=0)) || ((det_2<=0) & (det_2<=0) & (det_3<=0))); } 3. Ansatz: Ein dritter Ansatz führt uns über Flächenbetrachtungen: Der Punkt P liegt genau dann in Inneren des Dreieckes ∆ABC, wenn die Summe der Flächen der Dreiecke ∆ABP , ∆BCP und ∆ACP die Fläche des Dreiecks ∆ABC ergibt. Da der Betrag des Kreuzproduktes zweier Vektoren der Fläche des von ihnen augespannten Parallelogramms entspricht, können wir die Fläche des von ihnen aufgespannten Dreiecks als die Hälfte des Betrages ihres Kreuzproduktes berechnen. Wenn wir die Vektoren wie im vorigen Ansatz in der R3 einbetten, so ist der Betrag eines Kreuzproduktes durch den Betrag seiner zKomponente gegeben, und als Bedingung dafür, daß P im Inneren des Dreiecks ∆ABC liegt, erhalten wir 1 2 −−→ −→ · |(AB × AC)z | = 1 2 −−→ −→ · |(AB × AP )z | + 1 2 −−→ −−→ · |(BC × BP )z | + 1 2 −→ −−→ · |(AC × CP )z | oder äquivalent dazu in ganzen Zahlen: −− → −→ −−→ −→ −−→ −−→ −→ −−→ |(AB × AC)z | = |(AB × AP )z | + |(BC × BP )z | + |(AC × CP )z | Als Code: boolean liegt_innerhalb (int x1, int y1, int x2, int y2, int x3, int y3, int p_x, int p_y){ return (abs((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1)) == (abs((x2-x1)*(y_p-y1)-(y2-y1)*(x_p-x1))+ abs((x3-x2)*(y_p-y2)-(y3-y2)*(x_p-x2))+ abs((x1-x3)*(y_p-y3)-(y1-y3)*(x_p-x3)))); } abs(int x) ist hierbei eine Funktion, die den Absolutwert eines int-Wertes x zurückgibt. Je nach Geschmack kann man sie selbst implementieren oder Math.abs(int x) verwenden. 5 Aufgabe 4 Zuerst sehen wir uns einmal die Situation an: view-up up right eye point of interest Die Kamera (hier mit eye bezeichnet) liegt wegen der Bedingung near = 0 in der vorderen Clippingebene. Die vordere Clippingebene ist dadurch gekennzeichnet, daß sie den Vektor von der Kamera zum point of interest (also die Blickrichtung) als Normalenvektor hat. Der Vektor up ist eine geeignet skalierte Projektion des view-up-Vektors auf diese Ebene, während der Vektor right in dieser Ebene liegt und senkrecht auf dem up-Vektor steht. Am leichtesten ist die Richtung des right-Vektors zu bestimmen: er muß senkrecht auf dem view-up-Vektor und der Blickrichtung stehen. Somit können wir ein Vielfaches des right-Vektors mittels des Kreuzproduktes aus der Blickrichtung und dem view-up-Vektor ermitteln: −4 − 3 7 22 −−−→ right1 = 2 − 1 × 5 = −56 −1 − 4 −3 −42 Gründliches Betrachten der rechten Hand zeigt auch, daß dieser Vektor tatsächlich von der Blickrichtung aus nach rechts zeigt, wenn der view-up-Vektor als oben gewählt ist. Zur späteren Verwendung normieren wir diesen Vektor und erhalten: 22 −−−→ 1 −56 rightn = √5384 −42 Der right-Vektor ist dann identisch zum normierten (da der right right-Vektors 22 −−−→ 1 −56 Parameter 1 ist); somit haben wir right = √5384 −42 Die Richtung des up-Vektors können wir dann durch das Kreuzprodukt eines 6 positiven Vielfachen des right-Vektors und der Blickrichtung bestimmen: 11 −7 161 − → = −28 × 1 = 202 up 1 −21 −5 −185 Auch diesen Vektor normieren wir vor der weiteren Verwendung: 161 − → = √ 1 202 up n 100950 −185 Der up-Vektor ist dann das Doppelte dieses Vektors (da der Wert von top 2 ist): 322 − →= √ 1 404 up 100950 −370 Den rechten oberen Punkt Fvor der Vorderseite des Frustums können wir nun leicht berechnen, indem wir zur Kameraposition die Summe aus up- und rightVektor addieren: 3 4, 31 − − − → → + right ≈ 1, 51 Fvor = 1 + − up 4 2, 26 Analog berechnen wir den linken oberen Punkt Fvol , den linken unteren Punkt Fvul und den rechten unteren Punkt Fvur der vorderen Frustumsebene: 3 3, 71 − − − → − → Fvol = 1 − up + right ≈ 3, 03 4 3, 41 3 1, 69 − − − → → − right ≈ 0, 49 Fvul = 1 − − up 4 5, 74 3 2, 29 − − − → → − right ≈ −1, 03 Fvur = 1 + − up 4 4, 59 Um die hinteren Punkte des Frustums zu bestimmen, müssen wir die so erhaltenen Punkte noch um eine Längeneinheit (far = 1!) in die Blickrichtung verschieben. Mathematisch gesprochen normieren wir die Blickrichtung zum Vektor → − b n: −7 → − b n = √175 1 −5 7 und addieren diesen Vektor zu den bisher erhaltene Punkten und bekommen so die Punkte Fhor , Fhol , Fhul und Fhur : 3, 50 Fhor ≈ 1, 62 1, 69 2, 91 Fhol ≈ 3, 15 2, 83 0, 88 Fhul ≈ 0, 61 5, 16 1, 48 Fhur ≈ −0, 92 4, 01 8