2012-08-21_125825

Φωτογραφία από το sskennel.

Η σημερινή συνεδρία "Ερώτηση και απάντηση" μας προσφέρει ευγενική προσφορά του SuperUser - μια υποδιαίρεση του Stack Exchange, μιας κοινότητας ομάδας ιστότοπων Q&A.

Το ερώτημα

Ο αναγνώστης του SuperUser Sathya έθεσε την ερώτηση:

Εδώ μπορείτε να δείτε ένα στιγμιότυπο οθόνης ενός μικρού προγράμματος C ++ που ονομάζεται Triangle.exe με ένα περιστρεφόμενο τρίγωνο που βασίζεται στο OpenGL API.

Βεβαίως ένα πολύ βασικό παράδειγμα, αλλά νομίζω ότι ισχύει για άλλες λειτουργίες καρτών γραφικών.

Ήμουν απλώς περίεργος και ήθελα να μάθω όλη τη διαδικασία κάνοντας διπλό κλικ στο Triangle.exe κάτω από τα Windows XP έως ότου μπορώ να δω το τρίγωνο να περιστρέφεται στην οθόνη. Τι συμβαίνει, πώς αλληλεπιδρούν η CPU (που χειρίζεται πρώτα το .exe) και η GPU (η οποία εξάγει τελικά το τρίγωνο στην οθόνη);

Υποθέτω ότι εμπλέκεται στην εμφάνιση αυτού του περιστρεφόμενου τριγώνου είναι κυρίως το ακόλουθο υλικό / λογισμικό μεταξύ άλλων:

Σκεύη, εξαρτήματα


  • Μνήμη συστήματος HDD (RAM) Μνήμη βίντεο CPU GPU LCD οθόνη

Λογισμικό


  • Πρόγραμμα οδήγησης DirectX / OpenGL API Nvidia API

Μπορεί κανείς να εξηγήσει τη διαδικασία, ίσως με κάποιο διάγραμμα ροής για απεικόνιση;

Δεν πρέπει να είναι μια περίπλοκη εξήγηση που καλύπτει κάθε βήμα (μαντέψτε ότι θα ξεπερνούσε το πεδίο εφαρμογής), αλλά μια εξήγηση που μπορεί να ακολουθήσει ένας ενδιάμεσος τύπος πληροφορικής.

Είμαι σίγουρος ότι πολλοί άνθρωποι που μάλιστα θα αποκαλούσαν επαγγελματίες IT δεν θα μπορούσαν να περιγράψουν σωστά αυτήν τη διαδικασία.

Η απάντηση

Αν και πολλά μέλη της κοινότητας απάντησαν στην ερώτηση, ο Όλιβερ Σάλτσμπουργκ προσπάθησε ακόμη περισσότερο και απάντησε όχι μόνο με λεπτομερή απάντηση, αλλά και εξαιρετικά γραφικά.

Εικόνα από το JasonC, διατίθεται ως ταπετσαρία εδώ.

Αυτός γράφει:

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

Η παρουσίαση

Τι χρειάζεται για να έχει κανείς αυτήν την ενιαία εικόνα, την οποία δημοσιεύσατε στην ερώτησή σας, στην οθόνη;

Υπάρχουν πολλοί τρόποι για να σχεδιάσετε ένα τρίγωνο στην οθόνη. Για απλότητα, ας υποθέσουμε ότι δεν χρησιμοποιήθηκαν buffers κορυφής. (Ένα buffer κορυφής είναι μια περιοχή μνήμης όπου αποθηκεύετε συντεταγμένες.) Ας υποθέσουμε ότι το πρόγραμμα απλώς είπε στον αγωγό επεξεργασίας γραφικών για κάθε μεμονωμένη κορυφή (μια κορυφή είναι απλώς μια συντεταγμένη στο διάστημα) στη σειρά.

Όμως, πριν μπορέσουμε να σχεδιάσουμε οτιδήποτε, πρέπει πρώτα να τρέξουμε κάποια ικριώματα. Θα δούμε γιατί αργότερα:

// Διαγραφή της οθόνης και του ρυθμιστικού βάθους glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Επαναφορά του τρέχοντος matrix Modelview glMatrixMode (GL_MODELVIEW); glLoadIdentity (); // Σχέδιο με χρήση Τριγώνων glBegin (GL_TRIANGLES); // Κόκκινο glColor3f (1.0f, 0.0f, 0.0f); // Κορυφή τριγώνου (εμπρός) glVertex3f (0.0f, 1.0f, 0.0f); // Πράσινο glColor3f (0,0f, 1,0f, 0,0f); // Left Of Triangle (Μπροστά) glVertex3f (-1.0f, -1.0f, 1.0f); // Μπλε glColor3f (0,0f, 0,0f, 1,0f); // Right Of Triangle (Μπροστά) glVertex3f (1.0f, -1.0f, 1.0f); // Έγινε Σχέδιο glEnd ();

Τι έκανε λοιπόν;

Όταν γράφετε ένα πρόγραμμα που θέλει να χρησιμοποιήσει την κάρτα γραφικών, συνήθως θα επιλέξετε κάποιο είδος διεπαφής στο πρόγραμμα οδήγησης. Μερικές γνωστές διεπαφές με το πρόγραμμα οδήγησης είναι:


  • OpenGL Direct3D CUDA

Για αυτό το παράδειγμα θα διατηρήσουμε το OpenGL. Τώρα, η διεπαφή σας με το πρόγραμμα οδήγησης είναι αυτό που σας δίνει όλα τα εργαλεία που χρειάζεστε για να κάνετε το πρόγραμμά σας να μιλήσει με την κάρτα γραφικών (ή το πρόγραμμα οδήγησης, το οποίο στη συνέχεια μιλά στην κάρτα).

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

Αυτό το API είναι αυτό που βλέπουμε να χρησιμοποιείται στο παραπάνω παράδειγμα. Ας ρίξουμε μια πιο προσεκτική ματιά.

Τα ικριώματα

Για να μπορέσετε πραγματικά να κάνετε οποιοδήποτε πραγματικό σχέδιο, θα πρέπει να εκτελέσετε μια ρύθμιση. Πρέπει να ορίσετε τη θύρα προβολής σας (την περιοχή που θα αποδίδεται πραγματικά), την προοπτική σας (την κάμερα στον κόσμο σας), τι αντι-ψευδώνυμο θα χρησιμοποιείτε (για να εξομαλύνετε τα άκρα του τριγώνου σας)…

Αλλά δεν θα εξετάσουμε κανένα από αυτά. Θα ρίξουμε μια ματιά στα πράγματα που πρέπει να κάνεις σε κάθε πλαίσιο. Αρέσει:

Εκκαθάριση της οθόνης

Ο αγωγός γραφικών δεν πρόκειται να καθαρίσει την οθόνη για κάθε καρέ. Θα πρέπει να το πεις. Γιατί; Αυτός είναι ο λόγος:

Εάν δεν καθαρίσετε την οθόνη, απλώς θα τραβήξετε πάνω της κάθε πλαίσιο. Γι 'αυτό και ονομάζουμε glClear με το σύνολοGL_COLOR_BUFFER_BIT. Το άλλο bit (GL_DEPTH_BUFFER_BIT) λέει στο OpenGL να καθαρίσει το deepbuffer. Αυτό το buffer χρησιμοποιείται για να προσδιορίσει ποια pixel βρίσκονται μπροστά (ή πίσω) από άλλα pixel.

Μεταμόρφωση

Ο μετασχηματισμός είναι το μέρος όπου παίρνουμε όλες τις συντεταγμένες εισόδου (τις κορυφές του τριγώνου μας) και εφαρμόζουμε τον πίνακα ModelView. Αυτός είναι ο πίνακας που εξηγεί πώς το μοντέλο μας (οι κορυφές) περιστρέφεται, κλιμακώνεται και μεταφράζεται (μετακινείται).

Στη συνέχεια, εφαρμόζουμε τον πίνακα προβολής μας. Αυτό μετακινεί όλες τις συντεταγμένες έτσι ώστε να βλέπουν σωστά την κάμερά μας.

Τώρα μεταμορφώνουμε για άλλη μια φορά, με τον πίνακα Viewport. Αυτό το κάνουμε για να κλιμακώσουμε το μοντέλο μας στο μέγεθος της οθόνης μας. Τώρα έχουμε ένα σύνολο κορυφών που είναι έτοιμα για απόδοση!

Θα επιστρέψουμε στον μετασχηματισμό λίγο αργότερα.

Σχέδιο

Για να σχεδιάσετε ένα τρίγωνο, μπορούμε απλά να πούμε στο OpenGL να ξεκινήσει μια νέα λίστα τριγώνων καλώντας το glBegin με τη σταθερά GL_TRIANGLES. Υπάρχουν επίσης άλλες φόρμες που μπορείτε να σχεδιάσετε. Σαν τρίγωνο λωρίδα ή τρίγωνο ανεμιστήρα. Αυτές είναι κυρίως βελτιστοποιήσεις, καθώς απαιτούν λιγότερη επικοινωνία μεταξύ της CPU και της GPU για να σχεδιάσουν το ίδιο ποσό τριγώνων.

Μετά από αυτό, μπορούμε να παρέχουμε μια λίστα με σύνολα 3 κορυφών που θα πρέπει να αποτελούν κάθε τρίγωνο. Κάθε τρίγωνο χρησιμοποιεί 3 συντεταγμένες (όπως είμαστε σε 3D-space). Επιπλέον, παρέχω επίσης ένα χρώμα για κάθε κορυφή, με callingglColor3f πριν καλέσω το glVertex3f.

Η απόχρωση μεταξύ των 3 κορυφών (οι 3 γωνίες του τριγώνου) υπολογίζεται από το OpenGLautomatically. Θα παρεμβάλλει το χρώμα σε ολόκληρη την όψη του πολυγώνου.

ΑΛΛΗΛΕΠΙΔΡΑΣΗ

Τώρα, όταν κάνετε κλικ στο παράθυρο. Η εφαρμογή πρέπει μόνο να καταγράψει το μήνυμα παραθύρου που σηματοδοτεί το κλικ. Στη συνέχεια, μπορείτε να εκτελέσετε οποιαδήποτε ενέργεια στο πρόγραμμά σας θέλετε.

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

Πρώτα πρέπει να ξέρετε με σαφήνεια σε ποιο pixel ο χρήστης έκανε κλικ στο παράθυρο. Στη συνέχεια, λαμβάνοντας υπόψη την προοπτική σας, μπορείτε να υπολογίσετε την κατεύθυνση μιας ακτίνας, από το σημείο του κλικ του ποντικιού στη σκηνή σας. Στη συνέχεια, μπορείτε να υπολογίσετε εάν κάποιο αντικείμενο στη σκηνή σας τέμνει με αυτήν την ακτίνα. Τώρα ξέρετε αν ο χρήστης έκανε κλικ σε ένα αντικείμενο.

Λοιπόν, πώς το κάνετε να περιστρέφεται;

Μεταμόρφωση

Γνωρίζω δύο τύπους μετασχηματισμών που εφαρμόζονται γενικά:


  • Μετασχηματισμός που βασίζεται σε μήτρα Μετασχηματισμός με βάση τα οστά

Η διαφορά είναι ότι τα οστά επηρεάζουν μεμονωμένες κορυφές. Οι πίνακες επηρεάζουν πάντα όλες τις συρμένες κορυφές με τον ίδιο τρόπο. Ας δούμε ένα παράδειγμα.

Παράδειγμα

Νωρίτερα, φορτώσαμε τον πίνακα ταυτότητας πριν σχεδιάσουμε το τρίγωνό μας. Ο πίνακας ταυτότητας είναι αυτός που απλά δεν παρέχει καθόλου μετασχηματισμό. Έτσι, ό, τι σχεδιάζω, επηρεάζεται μόνο από την προοπτική μου. Έτσι, το τρίγωνο δεν θα περιστραφεί καθόλου.

Εάν θέλω να το περιστρέψω τώρα, θα μπορούσα είτε να κάνω τα μαθηματικά ο ίδιος (στην CPU) και απλά να καλέσω το glVertex3f με άλλες συντεταγμένες (που περιστρέφονται). Ή θα μπορούσα να αφήσω την GPU να κάνει όλη τη δουλειά, καλώντας το glRotate πριν σχεδιάσετε:

// Περιστρέψτε το τρίγωνο στον άξονα Υ glRotatef (ποσό, 0,0f, 1,0f, 0,0f); 

Το ποσό είναι, φυσικά, απλώς μια σταθερή τιμή. Αν θέλετε να κάνετε κινούμενη εικόνα, θα πρέπει να παρακολουθείτε την ποσότητα και να την αυξάνετε σε κάθε καρέ.

Λοιπόν, περιμένετε, τι συνέβη σε όλες τις συνομιλίες του πίνακα πριν

Σε αυτό το απλό παράδειγμα, δεν χρειάζεται να ενδιαφερόμαστε για τους πίνακες. Απλώς ονομάζουμε glRotatef και τα φροντίζει όλα αυτά για εμάς.

Το glRotate παράγει περιστροφή μοίρες γωνίας γύρω από το διάνυσμα xyz. Ο τρέχων πίνακας (seeglMatrixMode) πολλαπλασιάζεται με έναν πίνακα περιστροφής με το προϊόν που αντικαθιστά τον τρέχοντα πίνακα, καθώς το ifglMultMatrix κλήθηκε με τον ακόλουθο πίνακα ως επιχείρημά του: x 2 2 1 - c + cx ⁢ y ⁡ 1 - c - z ⁢ sx ⁢ z ⁡ 1 - c + y ⁢ s 0 y ⁢ x ⁡ 1 - c + z ⁢ sy 2 ⁡ 1 - c + cy ⁢ z ⁡ 1 - c - x ⁢ s 0 x ⁢ z ⁡ 1 - c - y ⁢ sy ⁢ z ⁡ 1 - c + x ⁢ sz 2 ⁡ 1 - c + c 0 0 0 0 1

Λοιπόν, ευχαριστώ για αυτό!

συμπέρασμα

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

Το μόνο πράγμα που μας λέει το OpenGL σε αυτό το παράδειγμα είναι όταν ολοκληρωθεί. Κάθε λειτουργία θα διαρκέσει ορισμένο χρόνο. Κάποια λειτουργία διαρκεί απίστευτα πολύ, άλλες είναι απίστευτα γρήγορες.

Η αποστολή κορυφής στην GPU θα είναι τόσο γρήγορη, δεν θα ήξερα καν πώς να την εκφράσω. Η αποστολή χιλιάδων κορυφών από την CPU στην GPU, κάθε μεμονωμένο πλαίσιο, είναι, πιθανότατα, κανένα ζήτημα.

Η εκκαθάριση της οθόνης μπορεί να διαρκέσει ένα χιλιοστό του δευτερολέπτου ή χειρότερη (λάβετε υπόψη ότι συνήθως έχετε περίπου 16 χιλιοστά του δευτερολέπτου χρόνου για να σχεδιάσετε κάθε πλαίσιο), ανάλογα με το πόσο μεγάλη είναι η θύρα προβολής σας. Για να το καθαρίσετε, το OpenGL πρέπει να σχεδιάσει κάθε εικονοστοιχείο στο χρώμα που θέλετε να διαγράψετε, που θα μπορούσε να είναι εκατομμύρια pixel.

Εκτός από αυτό, μπορούμε να ρωτήσουμε το OpenGL μόνο για τις δυνατότητες του προσαρμογέα γραφικών μας (μέγιστη ανάλυση, μέγιστο anti-aliasing, μέγιστο βάθος χρώματος,…).

Αλλά μπορούμε επίσης να γεμίσουμε μια υφή με pixel που το καθένα έχει ένα συγκεκριμένο χρώμα. Κάθε εικονοστοιχείο διατηρεί έτσι μια τιμή και η υφή είναι ένα τεράστιο «αρχείο» γεμάτο με δεδομένα. Μπορούμε να το φορτώσουμε στην κάρτα γραφικών (δημιουργώντας ένα buffer υφής), στη συνέχεια να φορτώσουμε ένα shader, να πούμε στο shader να χρησιμοποιήσει την υφή μας ως είσοδο και να εκτελέσουμε μερικούς εξαιρετικά βαρύς υπολογισμούς στο "αρχείο" μας.

Στη συνέχεια, μπορούμε να «αποδώσουμε» το αποτέλεσμα του υπολογισμού μας (με τη μορφή νέων χρωμάτων) σε μια νέα υφή.

Έτσι μπορείτε να κάνετε τη GPU να λειτουργεί για εσάς με άλλους τρόπους. Υποθέτω ότι το CUDA έχει παρόμοια απόδοση, αλλά δεν είχα ποτέ την ευκαιρία να συνεργαστώ με αυτό.

Στην πραγματικότητα αγγίξαμε ελαφρώς ολόκληρο το θέμα. Ο τρισδιάστατος προγραμματισμός γραφικών είναι μια κόλαση ενός θηρίου.

Έχετε κάτι να προσθέσετε στην εξήγηση; Ακούστε στα σχόλια. Θέλετε να διαβάσετε περισσότερες απαντήσεις από άλλους χρήστες τεχνολογίας Stack Exchange; Δείτε ολόκληρο το νήμα συζήτησης εδώ.