Category: Γ' Εξάμηνο: OpenGL Published: Friday, 22 January 2016 Written by Γεώργιος Παπαλουκάς

OpenGL: Μάθημα 4ο - 3D Σχήματα

Ο κώδικας που ακολουθεί έχει ληφθεί από την ιστοσελίδα

https://www3.ntu.edu.sg/home/ehchua/programming/opengl/CG_Examples.html.

Έχουν προστεθεί σχόλια για καλύτερη κατανόηση. Τα σχόλια εστιάζουν σε τμήματα κώδικα που δεν έχουν αναλυθεί σε βάθος σε προηγούμενα μαθήματα ή τα συναντούμε για πρώτη φορά εδώ. Συγκεκριμένα γίνεται εκτενής αναφορά στις παρακάτω συναρτήσεις και έννοιες:

? glutDisplayFunc

? glutInitDisplayMode

? glClearColor

? glutMainLoop()

? glClearDepth

? glClear

? glEnable(GL_DEPTH_TEST) και glDepthFunc

? glBegin(GL_QUADS)

? local space ή model space

? world space

? view space

? viewport transform

? projection transform

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
* OGL01Shape3D.cpp: 3D Shapes
*/
#include <windows.h> // for MS Windows #include <GL/glut.h> // GLUT, include glu.h and gl.h /* Global variables */ char title[] = "3D Shapes"; /* Initialize OpenGL Graphics */ void initGL() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque glClearDepth(1.0f); // Set background depth to farthest glEnable(GL_DEPTH_TEST); // Enable depth testing for z-culling glDepthFunc(GL_LEQUAL); // Set the type of depth-test glShadeModel(GL_SMOOTH); // Enable smooth shading glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Nice perspective corrections } /* Handler for window-repaint event. Called back when the window first appears and
whenever the window needs to be re-painted. */
void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers glMatrixMode(GL_MODELVIEW); // To operate on model-view matrix // Render a color-cube consisting of 6 quads with different colors glLoadIdentity(); // Reset the model-view matrix glTranslatef(1.5f, 0.0f, -7.0f); // Move right and into the screen glBegin(GL_QUADS); // Begin drawing the color cube with 6 quads // Top face (y = 1.0f) // Define vertices in counter-clockwise (CCW) order with normal pointing out glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f( 1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom face (y = -1.0f) glColor3f(1.0f, 0.5f, 0.0f); // Orange glVertex3f( 1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Front face (z = 1.0f) glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Back face (z = -1.0f) glColor3f(1.0f, 1.0f, 0.0f); // Yellow glVertex3f( 1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Left face (x = -1.0f) glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Right face (x = 1.0f) glColor3f(1.0f, 0.0f, 1.0f); // Magenta glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glEnd(); // End of drawing color-cube // Render a pyramid consists of 4 triangles glLoadIdentity(); // Reset the model-view matrix glTranslatef(-1.5f, 0.0f, -6.0f); // Move left and into the screen glBegin(GL_TRIANGLES); // Begin drawing the pyramid with 4 triangles // Front glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f( 0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(-1.0f, -1.0f, 1.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(1.0f, -1.0f, 1.0f); // Right glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(1.0f, -1.0f, 1.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(1.0f, -1.0f, -1.0f); // Back glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(1.0f, -1.0f, -1.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(-1.0f, -1.0f, -1.0f); // Left glColor3f(1.0f,0.0f,0.0f); // Red glVertex3f( 0.0f, 1.0f, 0.0f); glColor3f(0.0f,0.0f,1.0f); // Blue glVertex3f(-1.0f,-1.0f,-1.0f); glColor3f(0.0f,1.0f,0.0f); // Green glVertex3f(-1.0f,-1.0f, 1.0f); glEnd(); // Done drawing the pyramid glutSwapBuffers(); // Swap the front and back frame buffers (double buffering) } /* Handler for window re-size event. Called back when the window first appears and
whenever the window is re-sized with its new width and height */
void reshape(GLsizei width, GLsizei height) { // GLsizei for non-negative integer // Compute aspect ratio of the new window if (height == 0) height = 1; // To prevent divide by 0 GLfloat aspect = (GLfloat)width / (GLfloat)height; // Set the viewport to cover the new window glViewport(0, 0, width, height); // Set the aspect ratio of the clipping volume to match the viewport glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix glLoadIdentity(); // Reset // Enable perspective projection with fovy, aspect, zNear and zFar gluPerspective(45.0f, aspect, 0.1f, 100.0f); } /* Main function: GLUT runs as a console application starting at main() */ int main(int argc, char** argv) { glutInit(&argc, argv); // Initialize GLUT glutInitDisplayMode(GLUT_DOUBLE); // Enable double buffered mode glutInitWindowSize(640, 480); // Set the window's initial width & height glutInitWindowPosition(50, 50); // Position the window's initial top-left corner glutCreateWindow(title); // Create window with the given title glutDisplayFunc(display); // Register callback handler for window re-paint event glutReshapeFunc(reshape); // Register callback handler for window re-size event initGL(); // Our own OpenGL initialization glutMainLoop(); // Enter the infinite event-processing loop return 0; }

Επανασχεδιασμός του παραθύρου

glutDisplayFunc(display); // Register callback handler for window re-paint event

Η εντολή glutDisplayFunc(display) δηλώνει τη συνάρτηση display() σαν χειριστή του συμβάντος re-paint (επανασχεδιασμός). Δηλαδή, το υποσύστημα γραφικών πυροδοτεί την display() όταν το παράθυρο πρωτοεμφανίζεται και οποτεδήποτε υπάρχει ένα re-paint αίτημα (συμβάν επανασχεδιασμού). Πότε όμως υπάρχει re-paint αίτημα, δηλαδή πότε πυροδοτείται η display callback function; H GLUT είναι αυτή που καθορίζει πότε η συνάρτηση display() πρέπει να πυροδοτηθεί, βασισμένη στην κατάσταση επανασχεδιασμού του παραθύρου (window's redisplay state). Η redisplay state για ένα παράθυρο μπορεί να τεθεί ευθέως με κλήση της glutPostRedisplay ή εμμέσως σαν αποτέλεσμα ενός window damage το οποίο αναφέρεται από το window system (π.χ αλλαγή μεγέθους παραθύρου). 

Αξίζει να σημειωθεί το εξής "παράδοξο". Όταν δημιουργείται ένα παράθυρο, δεν υπάρχει αυτόματα κλήση κάποιας συνάρτησης εμφάνισης του παραθύρου. Είναι ευθύνη του προγραμματιστή να ορίσει μια συνάρτηση πυροδοτούμενη από το γεγονός της δημιουργίας του παραθύρου. Συγκεκριμένα η δημιουργία του παραθύρου ενεργοποιεί τη redisplay state του παραθύρου, η οποία με τη σειρά της προκαλεί re-paint event. Αν δεν έχει δηλωθεί συνάρτηση χειριστής του re-paint συμβάντος τότε προκύπτει "μοιραίο λάθος" (fatal error).

Βασικές παράμετροι εμφάνισης του παραθύρου

glutInitDisplayMode(GLUT_DOUBLE); // Enable double buffered mode

Η glutInitDisplayMode θέτει τον αρχικό τρόπο εμφάνισης του προς δημιουργία παραθύρου. Τα ορίσματά της χωρίζονται με τον τελεστή | (OR). Παράδειγμα χρήσης της είναι η glutInitDisplayMode(GLUT_DOUBLE| GLUT_RGBA). Ορίζει ότι το παράθυρο που θα δημιουργηθεί θα είναι double buffered με RGBA χρωματικό κώδικα. Στην περίπτωση του κώδικα που εξετάζουμε απουσιάζει το όρισμα GLUT_RGBA αν και σε επόμενη εντολή ( την glClearColor(0.0f, 0.0f, 0.0f, 1.0f)) ορίζεται RGB χρώμα (το το 0.0f, 0.0f, 0.0f) και διαφάνεια Α (η 1.0f). Αυτό συμβαίνει διότι το GLUT_RGBA είναι η προκαθορισμένη τιμή αν ούτε η GLUT_RGBA ούτε η εναλλακτική της GLUT_INDEX ορίζονται ρητώς (το ίδιο ισχύει και για άλλα αμοιβαία αποκλειόμενα ορίσματα - glutInitDisplayMode. Είναι προτιμότερο να γίνεται χρήση της σαφούς εντολής glutInitDisplayMode(GLUT_DOUBLE| GLUT_RGBA).

glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque 

Γίνεται επιλογή χρώματος όχι χρωματισμός. Ορίζονται δηλαδή οι τιμές red, green, blue και alpha (διαφάνεια) οι οποίες θα χρησιμοποιηθούν από την glClear για την αρχικοποίηση των συσσωρευτών χρωμάτων (color buffers).

glutMainLoop

glutMainLoop(); // Enter the infinite event-processing loop

Για να γίνει καλύτερα κατανοητός ο τρόπος λειτουργίας της glutMainLoop() τον αναπαριστούμε με ψευδοκώδικα.

glutMainLoop()

{

ΕΠΑΝΑΛΑΒΕ

{

έλεγξε για συμβάντα (events)

ΑΝ δεν υπάρχουν συμβάντα ΤΟΤΕ

ΑΝ υπάρχει συνάρτηση χρήστη για χειρισμό του idle event ΤΟΤΕ κάλεσε την idle συνάρτηση χρήστη

ΑΛΛΙΩΣ

ΠΕΡΙΠΤΩΣΗ συμβάν

1 {ΑΝ υπάρχει συνάρτηση χειρισμού για το συμβάν 1 ΤΟΤΕ κάλεσε τη συνάρτηση χειρισμού}

2 {ΑΝ υπάρχει συνάρτηση χειρισμού για το συμβάν 2 ΤΟΤΕ κάλεσε τη συνάρτηση χειρισμού}

3 {ΑΝ υπάρχει συνάρτηση χειρισμού για το συμβάν 3 ΤΟΤΕ κάλεσε τη συνάρτηση χειρισμού}

.

.

.

ν {ΑΝ υπάρχει συνάρτηση χειρισμού για το συμβάν ν ΤΟΤΕ κάλεσε τη συνάρτηση χειρισμού}

}

}

DepthBuffer & ColorBuffer

 glClearDepth(1.0f); // Set background depth to farthest

Η εντολή glClearDepth επιλέγει την τιμή που θα εγγραφεί στον Depth Buffer όταν κληθεί η glClear. Δείτε το ρόλο του Depth Buffer στη διαδικασία χρωματισμού των  pixels.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers

Η glClear χρωματίζει την ενεργή περιοχή του παραθύρου σύμφωνα με τις τιμές (χρώματα & τοποθέτηση στον άξονα Ζ) που έχουν ήδη οριστεί από τις  glClearColor και glClearDepth. Η εντολή glClear τοποθετείται στην αρχή της συνάρτησης diplay(), η οποία χειρίζεται τα re-paint συμβάντα. Με άλλα λόγια πρόκειται για εντολή αρχικοποίησης και καλείται πριν παρασταθούν τα γραφικά στο παράθυρο. Προκαλεί αρχικοποίηση

α. του χρώματος παρασκηνίου (background) και της διαφάνειας του παρασκηνίου (στην ενεργή περιοχή του παραθύρου)

β. του βάθους του παρασκηνίου (στην ενεργή περιοχή του παραθύρου)

Δείτε στο επόμενο βίντεο (από 5:24'') τι συμβαίνει αν ξεχάσουμε να καθαρίσουμε τον Color Buffer καθώς και τη διαδικασία εναλλαγής Buffer Swap.

glEnable(GL_DEPTH_TEST); // Enable depth testing for z-culling

glDepthFunc(GL_LEQUAL); // Set the type of depth-test 

Η σύγκριση που υποδηλώνεται στην εντολή glDepthFunc(GL_LEQUAL) γίνεται μόνο αν έχει προηγηθεί η ενεργοποίηση του ελέγχου βάθους στον άξονα z (με την εντολή glEnable(GL_DEPTH_TEST))

Η εντολή glDepthFunc ορίζει τη συνάρτηση "τελεστή σύγκρισης" η οποία χρησιμοποιείται για να συγκρίνει το βάθος κάθε νεοεισερχόμενου pixel με το βάθος που υπάρχει στον Depth Buffer (για το εν λόγω pixel).

Με GL_EQUAL το νεοεισερχόμενο pixel αναπαρίσταται στην οθόνη μόνο αν το βάθος του είναι ίσο με την υπάρχουσα στον Depth Buffer τιμή βάθους. Άλλες πιθανές τιμές ορίσματος είναι οι 

GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER,GL_NOTEQUAL, GL_GEQUAL, και GL_ALWAYS. Προκαθοριμένη τιμή είναι η  GL_LESS

Για να γίνει πιο κατανοητή η διαδικασία παρακολουθήστε το βίντεο του Jamie King, το οποίο δείχνει πως να χειριστούμε το depth test (z test) με χρήση της glEnable(GL_DEPTH_TEST) προκειμένου να επιτρέψουμε την εγγραφή στον depth buffer (z-buffer). Πρέπει να βάλουμε σε τάξη (να ταξινομήσουμε) τη γεωμετρία μας στον άξονα των Z. Ομοίως, στο βίντεο αποσαφηνίζεται το πώς καθαρίζεται ο  depth buffer με χρήση της glClear(GL_DEPTH_BUFFER_BIT) και πως αυτές οι λειτουργίες επηρεάζουν αυτό που τελικά αναπαρίσταται στην οθόνη.

 

Ορισμός 3D αντικειμένων

   glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
      // Top face (y = 1.0f)
      // Define vertices in counter-clockwise (CCW) order with normal pointing out
      glColor3f(0.0f, 1.0f, 0.0f);     // Green
      glVertex3f( 1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f,  1.0f);
      glVertex3f( 1.0f, 1.0f,  1.0f);

Κάθε αντικείμενο της OpenGL συγκροτείται από οντότητες (primitives) όπως τρίγωνα, τετράγωνα, πολύγωνα, σημεία και γραμμές. Μια οντότητα ορίζεται από μία ή περισσότερες κορυφές (vertices). Ο έγχρωμος κύβος αποτελείται από 6 τετράγωνα. Κάθε τετράγωνο αποτελείται από 4 κορυφές. Στον κώδικα που προηγείται και οι 4 κορυφές του τετραγώνου (ενός εκ των 6 τετραγώνων) έχουν πράσινο χρώμα (0.0f, 1.0f, 0.0f).

Ο έγχρωμος κύβος (δηλαδή τα 6 τετράγωνα) ορίζεται στο τοπικό ορθοκανονικό σύστημα αξόνων (local space ή model space) με αρχή των αξόνων x, y, z το κέντρο του κύβου (στο εσωτερικό του 3D σχήματος).

Ο άξονας x έχει φορά (θετικές τιμές) προς τα δεξιά, ο άξονας y κατακόρυφα προς τα πάνω και ο άξονας z φορά προς τα έξω.

 

Το μήκος της κάθε πλευράς του τετραγώνου (και των υπολοίπων 5 τετραγώνων) είναι 2 μονάδες.

Είναι εύκολο να αντιληφθούμε πλέον ότι οι επόμενες εντολές ορίζουν τις κορυφές V6, V5, V1 και V4.

 glVertex3f( 1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f,  1.0f);
      glVertex3f( 1.0f, 1.0f,  1.0f);

Με άλλα λόγια ορίζουν την πάνω έδρα του κύβου.

Με όμοιο τρόπο ορίζονται στο τοπικό (για το κάθε αντικείμενο) σύστημα συντεταγμένων (local space ή model space) οι υπόλοιπες κορυφές του κύβου και της πυραμίδας. Οι κορυφές είναι διανύσματα (π.χ. η κορυφή V3 είναι διάνυσμα με αρχή το σημείο τομής των αξόνων του local space 0,0,0 και τέλος το σημείο 1,-1,1 με φορά από το 0,0,0 προς το 1,-1,1).

Τα local spaces (ή model spaces) είναι όσα και τα αντικείμενα που ορίζονται στο OpenGL πρόγραμμα.

MODEL TO WORLD (Model Transform)

Αφού τα αντικείμενα δημιουργηθούν στο δικό τους τοπικό χώρο (local space) πρέπει να μεταφερθούν στον κοινό για όλα τα αντικείμενα χώρο, δηλαδή στο World Space.

 

Παρατηρήστε ότι τα κέντρα των επιμέρους local spaces έχουν μετασχηματιστεί, διότι έχουν πλέον συντεταγμένες (? 0,0,0) ως προς την αρχή του συστήματος συντεταγμένων World Space. Κατά ανάλογο τρόπο όλες οι κορυφές όλων των αντικειμένων που μεταφέρονται στο World Space μετασχηματίζονται διότι λαμβάνουν συντεταγμένες ως προς το κέντρο του. Οι κορυφές είναι πάντα διανύσματα με αρχή το σημείο 0,0,0 του κοινού World Space (και όχι του δικού τους Local Space)

Η διαδικασία που περιγράφηκε ονομάζεται Model Transformation ή Model to World Transformation (μετασχηματισμός) και υλοποιείται με τις εντολές που ακολουθούν.

 glMatrixMode(GL_MODELVIEW);     // To operate on model-view matrix
 
   // Render a color-cube consisting of 6 quads with different colors
   glLoadIdentity();                 // Reset the model-view matrix

Η εντολή  glMatrixMode καθορίζει σε ποιον πίνακα θα συμβούν οι επερχόμενες πράξεις πινάκων. Συγκεκριμένα, η glMatrixMode(GL_MODELVIEW) ορίζει ότι θα χρησιμοποιηθεί ο πίνακας MODELVIEW ο οποίος (από κατασκευής) προορίζεται να υποστεί μεταβολές (πράξεις) που αντιπροσωπεύουν τους μετασχηματισμούς των αντικειμένων στον κοινό κόσμο (World Space). Τέτοιοι μετασχηματισμοί είναι η κλιμάκωση (scaling / εντολή glScale), περιστροφή (rotation / εντολή glRotate) και μετατόπιση (translation / εντολή glTranslate) ενός αντικειμένου. Η εντολή glMatrixMode(GL_MODELVIEW) καλείται μόνο μια φορά. Συχνά ο πίνακας MODELVIEW αναφέρεται και σαν MODEL TO WORLD πίνακας (το όνομα MODEL TO WORLD παραπέμπει στη χρήση του πίνακα).

Ακολουθεί η γενική μορφή του πίνακα MODELVIEW (χωρίς λεπτομέρειες).

 

Η εντολή glLoadIdentity() καλείται κάθε φορά που σκοπεύουμε να ασκήσουμε μετασχηματισμούς σε διαφορετικό αντικείμενο, προκειμένου να αρχικοποιήσει τον πίνακα MODELVIEW.  Αυτό συμβαίνει για τον εξής λόγο: Οι εντολές glTranslate και glRotate λειτουργούν πάντα σχετικά ως προς την τρέχουσα κατάσταση του MODELVIEW πίνακα. Έτσι, αν καλέσουμε τη glTranslate μετατοπίζουμε από την "τρέχουσα θέση" του πίνακα MODELVIEW, όχι από την αρχική (προκαθορισμένη) θέση. Αν επιθυμούμε να μετατοπίσουμε με σημείο αναφοράς την αρχική θέση του πίνακα πρέπει να καλέσουμε τη glLoadIdentity(), η οποία επανατοποθετεί τον πίνακα MODELVIEW  στην αρχική του θέση. Ομοίως, μπορούμε να περιστρέψουμε (glRotate) με πίνακα αναφοράς τον MODELVIEW, ο οποίος είναι τώρα προσανατολισμένος στην αρχική κατεύθυνση.

Ο MODEL to WORLD (MODELVIEW) πίνακας διατηρείται για κάθε αντικείμενο που εισέρχεται στο World space, ανεξάρτητα από το αν η εντολή glLoadIdentity() θα τον αρχικοποιήσει για άσκηση μετασχηματισμού σε άλλο αντικείμενο. Κάθε αντικείμενο του World space κουβαλάει το δικό του MODELVIEW πίνακα.

Με βάση τα παραπάνω είναι κατανοητή η χρήση των εντολών

   glMatrixMode(GL_MODELVIEW); // To operate on model-view matrix
   // Render a color-cube consisting of 6 quads with different colors
   glLoadIdentity();                 // Reset the model-view matrix
   glTranslatef(1.5f, 0.0f, -7.0f);  // Move right and into the screen

Σημειώνεται ότι οι εντολές σχεδιασμού των αντικειμένων έπονται των εντολών MODEL TO WORLD. Φανταστείτε ότι πρώτα τοποθετούμε στο World space το κέντρο του αντικειμένου και ακολούθως σχεδιάζουμε (ως προς αυτό το κέντρο) το ίδιο το αντικείμενο (local space συντεταγμένες).

 // Render a pyramid consists of 4 triangles
   glLoadIdentity();                  // Reset the model-view matrix
   glTranslatef(-1.5f, 0.0f, -6.0f);  // Move left and into the screen
 
   glBegin(GL_TRIANGLES);           // Begin drawing the pyramid with 4 triangles
      // Front
      glColor3f(1.0f, 0.0f, 0.0f);     // Red
      glVertex3f( 0.0f, 1.0f, 0.0f);
      glColor3f(0.0f, 1.0f, 0.0f);     // Green
      glVertex3f(-1.0f, -1.0f, 1.0f);
      glColor3f(0.0f, 0.0f, 1.0f);     // Blue
      glVertex3f(1.0f, -1.0f, 1.0f);
 

 

WORLD TO VIEW (View Transform)

Αφού μετασχηματιστούν τα αντικείμενα (μετατόπιση, κλιμάκωση, περιστροφή στο WORLD space) θα περίμενε κανείς ότι αυτά γίνονται αυτόματα ορατά. Κάτι τέτοιο δε συμβαίνει.

 

Πρέπει τα αντικείμενα να εισέλθουν στο οπτικό πεδίο του παρατηρητή (camera).

Η προκαθορισμένη θέση της κάμερας (με world space συντεταγμένες) είναι:

gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, -100.0, 0.0, 1.0, 0.0)

Το μάτι (camera) EYE=(0,0,0)  LookAT=(0,0,-100) κοιτάζει μέσα (στα αρνητικά του άξονα z προς το εσωτερικό της οθόνης), και UP=(0,1,0) πάνω (στην κατεύθυνση του άξονα y).

Στο σημείο EYE τοποθετούνται τα μάτια σου. Τα σημεία EYE και LookΑΤ ορίζουν τον άξονα θέασης (προς τα που βλέπουν τα ευθυτενή μάτια σου). Ο άξονας (διάνυσμα) αυτός ορίζεται από την κίνηση του κεφαλιού σου. Το UP ορίζει το πόσο ψηλά η χαμηλά κοιτούν τα μάτια σου (θεωρούμε ότι τα μάτια δε στρίβουν αλλά η κίνηση αυτή γίνεται μόνο από το κεφάλι).

Το OpenGL graphics rendering pipeline (το HW δηλαδή) πραγματοποιεί WORLD TO VIEW μετασχηματισμό προκειμένου να φέρει το world space στο view space της κάμερας (στην περίπτωση της προκαθορισμένης θέσης της κάμερας δε χρειάζεται μετασχηματισμός). Ο όρος WORLD TO VIEW μετασχηματισμός σημαίνει ότι υπάρχει μοναδικός πίνακας WORLD TO VIEW, τον οποίο μοιράζονται οι MODEL to WORLD πίνακες των επιμέρους αντικειμένων. Καθώς ο κόσμος (World) περιστρέφεται γύρω από το σημείο όπου εδράζεται η κάμερα υφίσταται πράξεις (αλλαγές) ο πίνακας WORLD TO VIEW.

Βίντεο για κατανόηση μετασχηματισμών MODEL TO WORLD και WORLD TO VIEW

Πως αλλάζουν οι Πίνακες κατά τους μετασχηματισμούς; Απάντηση στο ερώτημα δίνει το επόμενο βίντεο του Jamie King.

 

Ο Jamie King περιγράφει με ενάργεια τους μετασχηματισμούς Model to World και World to View (Camera) εστιάζοντας στα 3 εμπλεκόμενα συστήματα συντεταγμένων (τα οποία μπορούν αν ειδωθούν και σαν ένα ενιαίο σύστημα συντεταγμένων).

 Viewport Transform

Αφορά τον επανασχεδιασμό των αντικειμένων στην ωφέλιμη περιοχή του παραθύρου με τρόπο που η δυσανάλογη αλλαγή διαστάσεων του παραθύρου να μην προκαλεί παραμόρφωση στα αντικείμενα. Πραγματοποιείται με τις επόμενες εντολές:

// Set the viewport to cover the new window
   glViewport(0, 0, width, height);

Για λεπτομέρειες ανατρέξτε στο "Μάθημα 2ο - Μετασχηματισμός παραθύρου"

VIEW TO PROJECTION (Projection Transform)

Στον WORLD TO VIEW μετασχηματισμό ο κόσμος της κάμερας είναι τρισδιάστατος. Για να αναπαρασταθεί η σκηνή στην οθόνη πρέπει η τρισδιάστατη απεικόνιση να "στριμωχτεί" στην οθόνη ώστε να χάσει την 3η διάσταση. Αυτό ακριβώς κάνει η προβολή (projection).

Μια κάμερα έχει περιορισμένο οπτικό πεδίο. Η προβολή (projection) μοντελοποιεί την άποψη που συλλαμβάνει η κάμερα. Υπάρχουν δύο είδη προβολής:

Προοπτική προβολή (perspective projection) και ορθογραφική προβολή (orthographic projection). Στην perspective projection, τα απομακρυσμένα από την κάμερα αντικείμενα φαίνονται μικρότερα συγκρινόμενα με ίδιου μεγέθους αντικείμενα, τα οποία βρίσκονται πιο κοντά στην κάμερα.

Στην orthographic projection, τα αντικείμενα φαίνονται ισομεγέθη (όταν είναι και από κατασκευής ισομεγέθη) ανεξάρτητα από το βάθος (τιμή στον άξονα z). Η Orthographic projection είναι μια ειδική περίπτωση perspective projection κατά την οποία η κάμερα είναι τοποθετημένη πολύ μακριά από τα αντικείμενα.

Δείτε εδώ το σχεδιαστικό κομμάτι του projection με χρήση κανόνων γεωμετρίας προκειμένου να γίνει περισσότερο κατανοητό τι σημαίνει projection (προβολή). Ομοίως δείτε εδώ μια προσομοίωση perspective vs orthographic projection.

Για να ορίσουμε την προβολή πρέπει να δουλέψουμε στον πίνακα Projection (ή αλλιώς VIEW TO PROJECTION πίνακα), με χρήση της εντολής

glMatrixMode(GL_PROJECTION);  // To operate on the Projection matrix

ενώ δεν ξεχνάμε να αρχικοποιήσουμε με χρήση της

 glLoadIdentity();             // Reset

 

Χρησιμοποιούμε την εντολή gluPerspective() για να ενεργοποιήσουμε την perspective projection.

   // Enable perspective projection with fovy, aspect, zNear and zFar
   gluPerspective(45.0f, aspect, 0.1f, 100.0f);

όπου fovy = 45ο η γωνία θέασης από την κάτω στην πάνω έδρα της κόλουρης πυραμίδας, aspect ο λόγος width/height, zNear και zFar τα εγγύς και μακράν επίπεδα της κόλουρης πυραμίδας. 

Παρατηρήστε ότι κατά τον μετασχηματισμό MODEL TO WORLD η μετατόπιση

glTranslatef(1.5f, 0.0f, -7.0f);  // Move right and into the screen

και

 glTranslatef(-1.5f, 0.0f, -6.0f);  // Move left and into the screen

οδηγεί τα κέντρα των αντικειμένων εντός της κόλουρης πυραμίδας θέασης.

Ο μετασχηματισμός  VIEW TO PROJECTION μετασχηματίζει την κόλουρη πυραμίδα θέασης σε ορθογώνιο παραλληλεπίπεδο 2x2x1 που λειτουργεί ως απόκομμα όγκου (clipping-volume) κεντραρισμένο στο εγγύς επίπεδο. Ο επακόλουθος Viewport μετασχηματισμός μετασχηματίζει το clipping-volume σε περιοχή θέασης της δισδιάστατης οθόνης (screen space).

Ασκήσεις εμπέδωσης

Πατήστε εδώ για "Ασκήσεις 4ου μαθήματος: 3D Shapes"

 

Hits: 3328