Κάμερες Αρχικά άρχισα να γράφω την κάμερα με τη λογική του να έχει τις βασικές λειτουργίες τύπου pitch, yaw, rll, strafe και mve frward/backwards. Εικόνα pitch, yaw, rll Quaternins (να βρω εικόνες) Αλλά πριν αρχίσω να γράφω είχα αποφασίσει να χρησιμοποιήσω quaternin για τις περιστροφές τις κάμερας. Τι είναι τα quaternin? Σύντομα, είναι ένας cmplex number, λειτουργεί στις 4 διαστάσεις, έχει ένα πραγματικό μέρος και τρία φανταστικά και μπορεί να αναπαρασταθεί αλγεβρικά έτσι q = w + xi + yj + zk. Αυτό που μας ενδιαφέρει βέβαια είναι το πως μπορούμε να το χρησιμοποιήσουμε και γιατί είναι καλύτερο από τα matrices. Το quaternin ουσιαστικά αναπαριστά της μεταβολή της περιστροφής, του rientatin του αντικειμένου. Κάθε αντικείμενο έχει ένα rientatin, το αρχικό του και από default το rientatin είναι quaternin( 1, 0, 0, 0 ). Αυτό το quat αναπαριστά μηδενική περιστροφή. Για να εφαρμόσουμε μια περιστροφή γωνίας Θ επί ενός άξονα χ,ψ,ω, πρέπει να δημιουργήσουμε ένα quaternin που να αναπαριστά αυτή τη μεταβολή. Σύμφωνα με τη θεωρία λοιπόν, αυτό γίνεται ως εξής W = cs( Θ / 2 ) X = χ * sin( w ) Υ = ψ * sin( w ) Ζ = ω * sin( w ) Αφού δημιουργήσουμε το quat το εφαρμόζουμε στο ήδη υπαρχον rientatin ως εξής: current = new * current ( πολ/σμος), και τώρα το rientatin μας έχει περιστραφεί κατά Θ μοίρες επί του (χ,ψ,ω) άξονα Στα quat έχει σημασία η σειρά που τα πολλαπλασιάζουμε. (τελικό -> αρχικό ) Μπορούμε να εφαρμόσουμε πολλές περιστροφές μαζί! Μπορούμε να περιστραφούμε γύρω από οποιοδήποτε/τυχαίο άξονα
Τα τέσσερα cmpnents ενός quaternin πιάνουν 4 * 4 = 16 bytes χώρο (για flats) σε συνδοιασμό με τα 3 flat (ή 4 ανάλογα με τον αν υπαρχει κάποια μεταβλητή για padding ) του vectr της θέσης του αντικειμένου έχουμε σύνολο 28 bytes. Ένα matrix 4x3 για θέση και περιστροφές χρειάζεται 12 * 4 = 48 bytes. Με τη χρήση των quaternins τώρα μπορούμε να χρησιμοποιήσουμε κάποια μαγικά φιλτράκια, μια συνάρτηση εν ονόματι slerp ( spherical linear interplatin ) η οποία λειτουργεί ως εξής: vid slerp( cnst quat &start, cnst quat &end, flat t, quat &result ) αυτό που κάνει αυτή η συνάρτηση είναι να μας επιστρέφει ένα quaternin, μια περιστροφή, μεταξύ της αρχικής και της τελικής. Για Τ=0 μας επιστρέφει της αρχική, για Τ=1 μας επιστρέφει την τελική. Με τη χρήση αυτής, μπορούμε να δημιουργήσουμε εύκολα πολύ απαλές μεταβολές στο rientatin και στη θέση της κάμερας. Πηγαίνοντας λίγο πίσω λοιπόν, για την εφαρμογή των βασικών κινήσεων της κάμερας (pitch, yaw, rll) αλλά και άλλων αντικειμένων χρησιμοποιώ τα quaternin για να αναπαριστήσω εύκολα τις περιστροφές αυτές ή ακόμα και συνδοιασμό περιστροφών. Το μειονέκτημα είναι ότι, για να το εφαρμόσουμε σε κάθε αντικείμενο, επειδή η pengl δουλεύει αποκλειστηκά με matrices, πρέπει να το μετατρέψουμε σε matrix και να το φορτώσουμε στο mdelview πριν κάνουμε το κάθε αντικείμενό μας render. Επιπλέον υπολογισμοί. Η κάμερα σαν αντικείμενο (ΤΕΛΟΣ) Γρήγορα συνειδητοποιούμε σε ένα κλειστό χώρο ότι πρέπει να συμπεριφερθούμε στην κάμερα σαν ένα αντικείμενο όπως όλα τ άλλα. Ειδικότερα για την κάμερα τρίτου προσώπου, καθώς ο χρήστης κινείται και η κάμερα ακολουθεί κατά πόδας, πολλές φορές η κάμερα χρειάζεται να περιορίζεται εντός του χώρου, να ελέγχεται και αυτή για cllisins με τους τοίχους Και πολλές φορές οι κάμερα είναι ουσιαστικά ο ίδιος ο παίχτης, τα μάτια του έστω (στις πρώτου προσώπου προοπτικές). Είναι κάτι που δεν κατάλαβα αμέσως, πρέπει να συμπεριφερόμαστε στην κάμερα το ίδιο όπως και όλα τα αντικείμενα, και ας μην τη βλέπουμε ποτέ... Το minimap (ΠΟΛΥ ΣΥΝΤΟΜΑ) (crp το minimap από εικόνα)
Το μινιμαπ δεν αποτελεί τεχνολογικό επίτευγμα... Αυτό που κάνω είναι αρχικά να φορτώνω ένα καινούριο prjectin matrix, ένα που με ορθοκανοκή προβολή, ώστε να ζωγραφίσω το φτωχό μου HUD σε ένα επίπεδο. Έφτιαξα ένα texture στο phtshp για φόντο, και ξαναζωγράφισα τον κόσμο (το σταθμό και όλα τα αντικείμενα) από μία συγκεκριμένη γωνία σε wireframe. glplygnmde( GL_FRONT, GL_LINES ) Ο παίχτης είναι η πράσινη βούλα, τα εμπόδια τα γαλάζια και οι κακοί τα κόκκινα. Το μεγάλο μπλε στο κέντρο είναι ο πυρήνας, που είναι και το Objective μου, να τον διαλύσω... ΠΡΟΧΩΡΙΜΕΝΟ. Να έχετε στο νου σας ότι το μινιμαπ δεν είναι κάτι που χρειάζεται να ανανεώνεται κάθε frame. Είναι ένα κομμάτι του παιχνιδιού από το οποίο μπορείτε να γλιτώσετε κάποια frames, πράγμα που σημαίνει ότι δε χρειάζεται να το ζωγραφίζεται και συνέχεια. Κρατώντας το σε κάποιο render target μπορείται να το στέλνετε να ζωγραφίζεται σε κάθε frame μεν, αλλά το render target αυτό καθ εαυτό δε χρειάζεται να γίνεται update( να ξαναζωγραφίζεται σε κάθε frame). Particle systems Κύριο χαρακτηριστικό ενός particle system πρέπει να είναι η ταχύτητα Κατά κανόνα τα particle systems ποιάνουν πολύ χώρο στη μνήμη και ταυτόχρονα είναι πάρα πολύ απαιτητικά όσον αφορά στον υπολογιστικό χρόνο Ακόμα πιο απαιτητικά όταν πρέπει να γίνεται και έλεγχος για συγκρούσεις με αντικείμενα στον κόσμο (cllisin detectin) Αυτή η μεγάλη τους υπολογιστική ανάγκη είναι που έκανε τους προγραμματιστές να τα σπρώξουν στη GPU Σε αυτό το παιχνίδι δεν είναι στη GPU!!! Επίσης, να σημειώσω ότι ό,τι έγινε σε αυτό το παιχνίδι δεν είναι βελτιστα σχεδιασμένο Τι γινεται σε αυτό το παιχνίδι Χαρακτηριστικά του particle Χαρακτηριστικά του particle system
Χαρακτηριστικά του particle system manager Κάθε particle system αποτελείται από δύο λίστες μία με τα ζωντανά/ενεργά particles και μία με τα νεκρά/ανενεργά particles Κάθε φορά που δημιουργούμε ένα particle system και δεσμεύουμε χώρο για Ν αριθμό particles, τις διεθύνσεις των οποίων περνάμε στη λίστα με τα ανενεργά. Στη συνέχεια, λαμβάνει χώρα ένας κύκλος που σταματά μόνο όταν λήξει το σύστημα, δηλαδή, όταν λήξει και το τελευταίο particle, οποίος πηγαίνει ως εξής Για κάθε particle στην ενεργή λίστα, κάνουμε update και αν έληξε το μεταφέρουμε στη λίστα με τα ανενεργά Πολύ σημαντικό για την ταχύτητα το γεγονός ότι για κάθε particle που λήγει, στο επόμενο frame έχουμε να κάνουμε update ένα particle λιγότερο Για τις λίστες των particles χρησιμοποιώ std::list το οποίο ειναι dubly-linked list. Σε κάθε άλλη περίπτωση θα χρησιμοποιούσα singly-linked list δική μου ή ακόμα καλύτερα της SGI την slist η οποία είναι διαθέσιμη ως extensin στο std Και φυσικά υπαρχει λόγος, ότι για κάθε particle όπως είναι τώρα χρησιμοποιώ έναν έξτρα δείκτη που σημαίνει ότι για ένα σύστημα 10.000 particles δεσμεύω 10.000 * 4 = 40ΚΒ χωρίς λόγο, Βέβαια σε ένα παιχνίδι τετοιου βεληνεκούς μηδενική σημασία έχει Τα χαρακτηριστικά κάθε particle system διαβάζονται από ένα αρχείο όταν πρωτοφορτώνεται το παιχνίδι και αποθηκεύοται σε templates. Τι είναι τα templates? Είναι cntainer κλασεις που έχουν τα ίδια χαρακτηριστικά με ένα κοινό particle system και κρατάνε τις τιμές που διαβάζονται από το αρχείο, έτσι ώστε όταν δημιουργούμε ένα σύστημα σε run-time να μην διαβάζουμε το αρχείο αλλά να αντιγράφουμε τις τιμές από το template που θέλουμε. Αριθμός των templates είναι ίδιος με τον αριθμό των ειδών των particle systems που χρησιμοποιούμε στο παιχνιδι (εδώ τρία) Καλύτερα παντως να χρησιμοποιεί ένα μαζικό πουλ από particles και να τα κάνει assign ανάλογα με τις ανάγκες του παιχνιδιού Φωτισμός και rendering
Το παιχνίδι χρησιμοποιεί (καθόλου ptimized) per-pixel lighting (GLSL) Χρησιμοποιεί το phng mdel και έχει πολύ όμορφα και φυσικά αποτελέσματα Στον κώδικα του shader χρησιμοποιώ τις built-in gl_lightsurce μεταβλητές Το μειονέκτιμα με τις built-in μεταβλητές είναι ότι περιορίζονται στον αριθμό 8 που είναι legacy στην pengl σαν ο maximum αριθμός επιτρεπόμενων ταυτόχρονα ενεργών φωτών σε μια σκηνή. Με τη χρήση shaders για τον φωτισμό της σκηνής μας όχι μόνο δε χρειάζεται να περιοριστούμε στον αριθμό φωτών αλλα ούτε καν στο μοντέλο φωτισμού (phng, blinn, tn, custm ) Τα μοντέλα είναι vertex arrays και ο σταθμός είναι display list, πράγμα που αποδείχτηκε μεγάλο λάθος για τον εξής λόγο. Όταν κάνουμε render ένα αντικείμενο, το στέλνουμε στην κάρτα, και κάθε pixel και vertex αυτού του αντικειμένου περνάει από τους αντίστοιχους shaders τι γίνεται λοιπόν όταν στέλνουμε εκατομμύρια Pixels, 95 % των οποίων ούτε καν βλέπουμε στην οθόνη (δλδ δεν χρειαζόταν να τα στείλουμε εξ αρχής... καταφέρνουμε ένα μεγάλο bttleneck χωρίς λόγο γιατί τρέχουμε τους shaders για όλα τα τα pixels πιο απλά, σωστότερες τεχνικές περιλαμβάνουν τα εξής, χρήση vertex array, ή ακόμα καλύτερα vertex buffer bjects όχι display lists ακόμα και για στατικά αντικείμενα, όταν αυτά είναι μεγάλα backface culling(standard), frustum culling, (επίσης τυπικό), cclusin culling (πιο προχωρημένο) Cmpnent based-design πρόκειται για εναλλατικό σχεδιασμό του ιεραρχικού τρόπου Αυτό το σύστημα είναι data-driven. Η λογική είναι να δημιουργούμε πολλά μικρά cmpnents. Υπάρχουν φυσικά και άλλες τεχνικές, πχ αντί για πολλά μικρά, να ομαδοποιούνται κοινά στοιχεια του rendering πχ ή της φυσικής σε μεγάλα blbs από δεδομένα.
Στόχος του design αυτού είναι 1. Η ελαχιστοποίηση του inheritance 2. Το μεγαλύτερο δυνατόν decupling 3. Η ευκολότερη συντήρηση του κώδικα [πολλά μικρά αυτόνομα κομμάτια] 4. Το σημαντικότερο, το resusability 5. Μπορούμε πολύ εύκολα να σχεδιάσουμε καινούρια αντικείμενα απο συντάσσοντας ήδη υπάρχοντα cmpnents και μάλιστα χωρίς να γράψουμε κώδικα a. Πολύ σημαντικό, να μπορείς να έχεις έλεγχο των δεδομένων από εξωτερικές εφαρμογές Τα cmpnents χωρίζονται σε οικογένειες (visual, scripts, physics, cllisin, material, texture, shader κλπ.). κάθε bject μπορεί να έχει ένα cmpnent από κάθε οικογένεια. Σωστή χρήση managers. Με το cmpnent-based design ο κάθε manager (texture, shader κλπ) απασχολείται μόνο με το αντίστοιχο cmpnent του αντικειμένου, και όχι με όλο το αντικείμενο.