Πλανητικό τοπίο. Περιβαλλοντικά προβλήματα φυσικού τοπίου

AlexWIN32 20 Αυγούστου 2017 στις 01:36 μ.μ

Πλανητικό τοπίο

  • API,
  • C++
  • Ανάπτυξη παιχνιδιών
  • Φροντιστήριο

Είναι δύσκολο να υποστηρίξουμε ότι το τοπίο είναι αναπόσπαστο μέρος των περισσότερων παιχνιδιών υπολογιστή σε εξωτερικούς χώρους. Η παραδοσιακή μέθοδος εφαρμογής αλλαγών στο ανάγλυφο της επιφάνειας που περιβάλλει τη συσκευή αναπαραγωγής είναι η εξής - παίρνουμε ένα πλέγμα, το οποίο είναι ένα επίπεδο, και για κάθε πρωτόγονο σε αυτό το πλέγμα κάνουμε μια μετατόπιση κατά μήκος του κανονικού σε αυτό το επίπεδο κατά μια συγκεκριμένη τιμή σε αυτό το πρωτόγονο. Με απλά λόγια, έχουμε μια υφή ενός καναλιού 256 επί 256 pixel και ένα επίπεδο πλέγμα. Για κάθε πρωτόγονο, παίρνουμε την τιμή από την υφή με βάση τις συντεταγμένες της στο επίπεδο. Τώρα απλώς μετατοπίζουμε τις συντεταγμένες του αρχέγονου κατά μήκος του κανονικού στο επίπεδο με την τιμή που προκύπτει (Εικ. 1)

Εικ. 1 χάρτης ύψους + επίπεδο = τοπίο

1. Τομέας

Προφανώς, δεν είναι συνετό να χτίσετε ένα τοπίο για ολόκληρη τη σφαίρα ταυτόχρονα - το μεγαλύτερο μέρος του δεν θα είναι ορατό. Επομένως, πρέπει να δημιουργήσουμε μια ορισμένη ελάχιστη περιοχή του χώρου - ένα συγκεκριμένο πρωτόγονο που θα αποτελέσει το ανάγλυφο του ορατού τμήματος της σφαίρας. Θα το ονομάσω τομέα. Πώς μπορούμε να το αποκτήσουμε; Δείτε λοιπόν το σχήμα 2α. Το πράσινο κύτταρο είναι ο τομέας μας. Στη συνέχεια, θα κατασκευάσουμε έξι πλέγματα, καθένα από τα οποία είναι μια όψη ενός κύβου (Εικ. 2β). Τώρα ας κανονικοποιήσουμε τις συντεταγμένες των πρωτόγονων που σχηματίζουν τα πλέγματα (Εικ. 2γ).


Εικ.2

Ως αποτέλεσμα, έχουμε έναν κύβο που προβάλλεται σε μια σφαίρα, όπου ο τομέας είναι η περιοχή σε μία από τις όψεις της. Γιατί λειτουργεί αυτό; Θεωρήστε ένα αυθαίρετο σημείο στο πλέγμα ως διάνυσμα από την αρχή. Τι είναι η κανονικοποίηση διανύσματος; Πρόκειται για τη μετατροπή ενός δεδομένου διανύσματος σε διάνυσμα στην ίδια κατεύθυνση, αλλά με μονάδα μήκους. Η διαδικασία είναι η εξής: πρώτα βρίσκουμε το μήκος του διανύσματος στην Ευκλείδεια μετρική σύμφωνα με το Πυθαγόρειο θεώρημα

Στη συνέχεια, διαιρέστε κάθε ένα από τα διανυσματικά στοιχεία με αυτήν την τιμή

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

Όπου x0, y0, z0 είναι οι συντεταγμένες του κέντρου της σφαίρας και R η ακτίνα της. Στην περίπτωσή μας, το κέντρο της σφαίρας είναι η αρχή και η ακτίνα είναι ίση με ένα. Ας αντικαταστήσουμε γνωστές αξίεςκαι πάρτε τη ρίζα δύο πλευρών της εξίσωσης. Αποδεικνύεται το εξής

Κυριολεκτικά ο τελευταίος μετασχηματισμός μας λέει τα εξής: «Για να ανήκεις στη σφαίρα, το μήκος του διανύσματος πρέπει να είναι ίσο με ένα». Αυτό πετύχαμε μέσω της ομαλοποίησης.

Τι γίνεται αν η σφαίρα έχει αυθαίρετο κέντρο και ακτίνα; Μπορείτε να βρείτε το σημείο που του ανήκει χρησιμοποιώντας την παρακάτω εξίσωση

Όπου pS είναι ένα σημείο της σφαίρας, C είναι το κέντρο της σφαίρας, pNorm είναι το προηγουμένως κανονικοποιημένο διάνυσμα και R είναι η ακτίνα της σφαίρας. Με απλά λόγια, αυτό που συμβαίνει εδώ είναι «κινούμε από το κέντρο της σφαίρας προς ένα σημείο στο πλέγμα σε απόσταση R». Δεδομένου ότι κάθε διάνυσμα έχει μοναδιαίο μήκος, στο τέλος όλα τα σημεία απέχουν ίσα από το κέντρο της σφαίρας κατά την απόσταση της ακτίνας της, γεγονός που καθιστά αληθινή την εξίσωση της σφαίρας.

2. Διαχείριση

Πρέπει να αποκτήσουμε μια ομάδα τομέων που είναι δυνητικά ορατοί από την οπτική γωνία. Αλλά πώς να το κάνουμε αυτό; Ας υποθέσουμε ότι έχουμε μια σφαίρα με κέντρο σε κάποιο σημείο. Έχουμε επίσης έναν τομέα, ο οποίος βρίσκεται στη σφαίρα, και ένα σημείο P, που βρίσκεται στο χώρο κοντά στη σφαίρα. Τώρα ας κατασκευάσουμε δύο διανύσματα - το ένα κατευθύνεται από το κέντρο της σφαίρας στο κέντρο του τομέα, το άλλο - από το κέντρο της σφαίρας στο σημείο θέασης. Κοιτάξτε το Σχ. 3 - ο τομέας μπορεί να είναι ορατός μόνο εάν η απόλυτη τιμή της γωνίας μεταξύ αυτών των διανυσμάτων είναι μικρότερη από 90 μοίρες.


Εικ.3 α - γωνία μικρότερη από 90 - ο τομέας είναι δυνητικά ορατός. β - γωνία μεγαλύτερη από 90 - ο τομέας δεν είναι ορατός

Πώς να αποκτήσετε αυτή τη γωνία; Για να γίνει αυτό, πρέπει να χρησιμοποιήσετε το βαθμωτό γινόμενο των διανυσμάτων. Για την τρισδιάστατη περίπτωση υπολογίζεται ως εξής:

Το κλιμακωτό προϊόν έχει την ιδιότητα διανομής:

Προηγουμένως ορίσαμε την εξίσωση για το μήκος ενός διανύσματος - τώρα μπορούμε να πούμε ότι το μήκος ενός διανύσματος είναι ίσο με τη ρίζα του προϊόν με κουκκίδεςαυτού του φορέα στον εαυτό του. Ή το αντίστροφο - το κλιμακωτό γινόμενο ενός διανύσματος με τον εαυτό του είναι ίσο με το τετράγωνο του μήκους του.

Τώρα ας δούμε τον νόμο των συνημιτόνων. Μία από τις δύο συνθέσεις του μοιάζει με αυτό (Εικ. 4):


Εικ.4 νόμος των συνημιτόνων

Αν πάρουμε το a και το b ως τα μήκη των διανυσμάτων μας, τότε η γωνία άλφα είναι αυτό που ψάχνουμε. Πώς όμως παίρνουμε την τιμή του c; Κοιτάξτε: αν αφαιρέσουμε το a από το b, παίρνουμε ένα διάνυσμα που κατευθύνεται από το a στο b, και αφού ένα διάνυσμα χαρακτηρίζεται μόνο από κατεύθυνση και μήκος, μπορούμε γραφικά να εντοπίσουμε την αρχή του στο τέλος του διανύσματος a. Από αυτό μπορούμε να πούμε ότι το c είναι ίσο με το μήκος του διανύσματος b - a. Λοιπόν, το κάναμε

Ας εκφράσουμε τα τετράγωνα των μηκών ως κλιμακωτά γινόμενα

Ας ανοίξουμε τις αγκύλες χρησιμοποιώντας την ιδιότητα διανομής

Ας το συντομεύσουμε λίγο

Τέλος, διαιρώντας και τις δύο πλευρές της εξίσωσης με μείον δύο, παίρνουμε

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

3. Πλέγμα

Ήρθε η ώρα να σκεφτείτε πώς να σχεδιάσετε πρωτόγονους. Όπως είπα νωρίτερα, ο τομέας είναι το κύριο συστατικό στο διάγραμμά μας, επομένως για κάθε δυνητικά ορατό τομέα θα σχεδιάσουμε ένα πλέγμα του οποίου τα πρωτόγονα θα σχηματίσουν το τοπίο. Κάθε ένα από τα κελιά του μπορεί να εμφανιστεί χρησιμοποιώντας δύο τρίγωνα. Επειδή κάθε κελί έχει γειτονικές όψεις, οι τιμές των περισσότερων κορυφών τριγώνου επαναλαμβάνονται σε δύο ή περισσότερα κελιά. Για να αποφύγουμε την αντιγραφή δεδομένων στο buffer κορυφής, ας γεμίσουμε την προσωρινή μνήμη ευρετηρίου. Εάν χρησιμοποιούνται ευρετήρια, τότε με τη βοήθειά τους ο αγωγός γραφικών καθορίζει ποιο αρχέγονο στο buffer κορυφής πρέπει να επεξεργαστεί. (Εικ. 5) Η τοπολογία που επέλεξα είναι τριγωνική λίστα (D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST)


Εικ.5 Οπτική απεικόνιση δεικτών και αρχικών

Η δημιουργία ξεχωριστού buffer κορυφής για κάθε τομέα είναι πολύ δαπανηρή. Είναι πολύ πιο αποτελεσματικό να χρησιμοποιείτε ένα ενιαίο buffer με συντεταγμένες στο χώρο του πλέγματος, δηλαδή το x είναι μια στήλη και το y μια γραμμή. Αλλά πώς μπορούμε να πάρουμε ένα σημείο στη σφαίρα από αυτά; Ένας τομέας είναι μια τετράγωνη περιοχή με αρχή σε ένα ορισμένο σημείο S. Όλοι οι τομείς έχουν το ίδιο μήκος ακμής - ας τον ονομάσουμε SLen. Το πλέγμα καλύπτει ολόκληρη την περιοχή του τομέα και έχει επίσης τον ίδιο αριθμό σειρών και στηλών, οπότε για να βρούμε το μήκος της άκρης του κελιού μπορούμε να κατασκευάσουμε την ακόλουθη εξίσωση

Όπου СLen είναι το μήκος της άκρης του κελιού, το MSize είναι ο αριθμός των γραμμών ή στηλών του πλέγματος. Διαχωρίστε και τα δύο μέρη κατά MSize και λάβετε CLen


Εικ.6 Οπτική απεικόνιση του σχηματισμού ενός σημείου στο πλέγμα

Για να πάρουμε ένα σημείο στη σφαίρα, χρησιμοποιούμε την εξίσωση που προέκυψε νωρίτερα

4. Ύψος

Όλα όσα έχουμε επιτύχει μέχρι εδώ, ελάχιστα μοιάζουν με το τοπίο. Ήρθε η ώρα να προσθέσετε αυτό που θα το κάνει έτσι - τη διαφορά ύψους. Ας φανταστούμε ότι έχουμε μια σφαίρα μοναδιαίας ακτίνας με κέντρο στην αρχή, καθώς και ένα σύνολο σημείων (P0, P1, P2... PN) που βρίσκονται σε αυτή τη σφαίρα. Κάθε ένα από αυτά τα σημεία μπορεί να αναπαρασταθεί ως μοναδιαίο διάνυσμα από την αρχή. Τώρα φανταστείτε ότι έχουμε ένα σύνολο τιμών, καθεμία από τις οποίες είναι το μήκος ενός συγκεκριμένου διανύσματος (Εικ. 7).

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

Εκτός από το καρτεσιανό, ένα σημείο σε μια σφαίρα μπορεί επίσης να περιγραφεί χρησιμοποιώντας ένα σφαιρικό σύστημα συντεταγμένων. Σε αυτή την περίπτωση, οι συντεταγμένες του θα αποτελούνται από τρία στοιχεία: τη γωνία του αζιμουθίου, την πολική γωνία και την τιμή της μικρότερης απόστασης από την αρχή στο σημείο. Η γωνία αζιμουθίου είναι η γωνία μεταξύ του άξονα Χ και της προβολής της ακτίνας από την αρχή σε ένα σημείο του επιπέδου XZ. Μπορεί να πάρει τιμές από μηδέν έως 360 μοίρες. Η πολική γωνία είναι η γωνία μεταξύ του άξονα Υ και της ακτίνας από την αρχή έως το σημείο. Μπορεί επίσης να ονομάζεται ζενίθ ή κανονικό. Δέχεται τιμές από μηδέν έως 180 μοίρες. (βλ. Εικ. 8)


Εικ.8 Σφαιρικές συντεταγμένες

Για να μετατρέψω από καρτεσιανό σε σφαιρικό χρησιμοποιώ τις ακόλουθες εξισώσεις (υποθέτω ότι ο άξονας Y είναι επάνω):

Όπου d είναι η απόσταση από το σημείο, a είναι η πολική γωνία, b είναι η γωνία αζιμουθίου. Η παράμετρος d μπορεί επίσης να περιγραφεί ως «το μήκος του διανύσματος από την αρχή έως το σημείο» (όπως φαίνεται από την εξίσωση). Εάν χρησιμοποιήσουμε κανονικοποιημένες συντεταγμένες, μπορούμε να αποφύγουμε τη διαίρεση όταν βρίσκουμε την πολική γωνία. Στην πραγματικότητα, γιατί χρειαζόμαστε αυτές τις γωνίες; Διαιρώντας το καθένα από αυτά με το μέγιστο εύρος του, παίρνουμε συντελεστές από το μηδέν έως το ένα και τους χρησιμοποιούμε για να κάνουμε δείγμα από την υφή στο shader. Κατά τη λήψη του συντελεστή για την πολική γωνία, είναι απαραίτητο να ληφθεί υπόψη το τέταρτο στο οποίο βρίσκεται η γωνία. "Αλλά η τιμή της έκφρασης z / x δεν ορίζεται όταν το x είναι ίσο με μηδέν", λέτε. Επιπλέον, θα πω ότι όταν το z είναι ίσο με μηδέν, η γωνία θα είναι μηδέν ανεξάρτητα από την τιμή του x.

Ας προσθέσουμε μερικές ειδικές περιπτώσεις για αυτές τις τιμές. Έχουμε κανονικοποιημένες συντεταγμένες (κανονικές) - ας προσθέσουμε πολλές συνθήκες: αν η τιμή Χ της κανονικής είναι μηδέν και η τιμή Ζ είναι μεγαλύτερη από μηδέν - τότε ο συντελεστής είναι 0,25, αν Χ είναι μηδέν και Ζ είναι μικρότερος από μηδέν - τότε θα είναι 0,75. Εάν η τιμή του Ζ είναι μηδέν και το Χ είναι μικρότερη από το μηδέν, τότε ο συντελεστής θα είναι ίσος με 0,5. Όλα αυτά μπορούν εύκολα να επαληθευτούν σε έναν κύκλο. Αλλά τι να κάνετε εάν το Ζ είναι μηδέν και το Χ μεγαλύτερο από το μηδέν - τελικά, σε αυτήν την περίπτωση και το 0 και το 1 θα είναι σωστά; Ας φανταστούμε ότι επιλέξαμε το 1 - λοιπόν, ας πάρουμε έναν τομέα με ελάχιστη γωνία αζιμουθίου 0 και μέγιστη 90 μοίρες. Τώρα ας δούμε τις τρεις πρώτες κορυφές στην πρώτη σειρά του πλέγματος που εμφανίζει αυτόν τον τομέα. Για την πρώτη κορυφή, ικανοποιήσαμε την συνθήκη και ορίσαμε τη συντεταγμένη υφής X σε 1. Προφανώς, για τις επόμενες δύο κορυφές αυτή η συνθήκη δεν θα πληρούται - οι γωνίες για αυτές βρίσκονται στο πρώτο τέταρτο και ως αποτέλεσμα παίρνουμε κάτι τέτοιο σετ - (1,0, 0,05, 0,1). Αλλά για έναν τομέα με γωνίες από 270 έως 360 για τις τρεις τελευταίες κορυφές στην ίδια σειρά, όλα θα είναι σωστά - η συνθήκη για την τελευταία κορυφή θα λειτουργήσει και θα πάρουμε το σύνολο (0,9, 0,95, 1,0). Εάν επιλέξουμε το μηδέν ως αποτέλεσμα, θα λάβουμε τα σύνολα (0,0, 0,05, 0,1) και (0,9, 0,95, 0,0) - σε κάθε περίπτωση, αυτό θα οδηγήσει σε αρκετά αισθητές επιφανειακές παραμορφώσεις. Ας εφαρμόσουμε λοιπόν τα παρακάτω. Ας πάρουμε το κέντρο του τομέα, μετά κανονικοποιήσουμε το κέντρο του, μεταφέροντάς τον έτσι στη σφαίρα. Τώρα ας υπολογίσουμε το βαθμωτό γινόμενο του κανονικοποιημένου κέντρου και του διανύσματος (0, 0, 1). Τυπικά, αυτό το διάνυσμα είναι κανονικό στο επίπεδο XY, και υπολογίζοντας το βαθμωτό γινόμενο του με το κανονικοποιημένο διάνυσμα κέντρου τομέα, μπορούμε να καταλάβουμε σε ποια πλευρά του επιπέδου βρίσκεται το κέντρο. Εάν είναι μικρότερο από το μηδέν, τότε ο τομέας βρίσκεται πίσω από το επίπεδο και χρειαζόμαστε μια τιμή 1. Εάν το κλιμακωτό γινόμενο είναι μεγαλύτερο από το μηδέν, τότε ο τομέας είναι μπροστά από το επίπεδο και επομένως η οριακή τιμή θα είναι 0. ( βλέπε Εικ. 9)


Εικ.9 Το πρόβλημα της επιλογής μεταξύ 0 και 1 για συντεταγμένες υφής

Εδώ είναι ο κώδικας για τη λήψη συντεταγμένων υφής από σφαιρικές. Παρακαλώ σημειώστε - λόγω σφαλμάτων υπολογισμού, δεν μπορούμε να ελέγξουμε τις κανονικές τιμές για ισότητα προς το μηδέν, αντίθετα πρέπει να συγκρίνουμε τις απόλυτες τιμές τους με κάποια τιμή κατωφλίου (για παράδειγμα 0,001)

//norm - κανονικοποιημένες συντεταγμένες του σημείου για το οποίο λαμβάνουμε συντεταγμένες υφής //offset - κανονικοποιημένες συντεταγμένες του κέντρου του τομέα στον οποίο ανήκει η νόρμα //zeroTreshold - τιμή κατωφλίου (0,001) float2 GetTexCoords(float3 norm, float3 offset) ( float tX = 0,0f , tY = 0,0f bool normXIsZero = abs(norm.x)< zeroTreshold; bool normZIsZero = abs(norm.z) < zeroTreshold; if(normXIsZero || normZIsZero){ if(normXIsZero && norm.z >0,0f) tX = 0,25f;< 0.0f && normZIsZero) tX = 0.5f; else if(normXIsZero && norm.z < 0.0f) tX = 0.75f; else if(norm.x >αλλιώς εάν (norm.x< 0.0f) tX = 1.0f; else tX = 0.0f; } }else{ tX = atan(norm.z / norm.x); if(norm.x < 0.0f && norm.z >0,0f && normZIsZero)( if(dot(float3(0,0f, 0,0f, 1,0f), offset)< 0.0f && norm.z < 0.0f) tX += 3.141592; else if(norm.x >0,0f) tX += 3,141592;< 0.0f) tX = 3.141592 * 2.0f + tX; tX = tX / (3.141592 * 2.0f); } tY = acos(norm.y) / 3.141592; return float2(tX, tY); }
αλλιώς εάν (νορμ.χ

0.0f && norm.z

Θα δώσω μια ενδιάμεση έκδοση του vertex shader

//startPos - αρχή της όψης του κύβου //vec1, vec2 - διανύσματα κατεύθυνσης της όψης κύβου //gridStep - μέγεθος κελιού //sideSize - μήκος άκρου τομέα //GetTexCoords() - μετατρέπει τις σφαιρικές συντεταγμένες σε συντεταγμένες υφής VOut ProcessVertex(VIn input) ( float3 planePos = startPos + vec1 * input.netPos.x * gridStep.x + vec2 * input.netPos.y * gridStep.y; float3 sphPos = normalize(planePos); normalize(startPos + (vec1 + vec2) * SizeSize * 0,5f = GetTexCoords(sphPos, normOffset = mainHeightTex.SampleLevel(mainHeightTexSampler, 0).x; output.texCoords = tc επιστροφής;

5. Φωτισμός

Το ίδιο ισχύει και για την τιμή συνημιτόνου -1, μόνο που στην περίπτωση αυτή τα διανύσματα δείχνουν προς αντίθετες κατευθύνσεις. Αποδεικνύεται ότι όσο πιο κοντά είναι το κανονικό διάνυσμα και το διάνυσμα στην πηγή φωτός στην κατάσταση της συγγραμμικότητας, τόσο μεγαλύτερος είναι ο συντελεστής φωτισμού της επιφάνειας στην οποία ανήκει το κανονικό. Υποθέτει επίσης ότι μια επιφάνεια δεν μπορεί να φωτιστεί αν το κανονικό της δείχνει προς την αντίθετη κατεύθυνση από την πηγή - γι' αυτό χρησιμοποιώ μόνο θετικές τιμές συνημιτόνου.

Χρησιμοποιώ μια παράλληλη πηγή, επομένως η θέση της μπορεί να παραμεληθεί. Το μόνο πράγμα που πρέπει να έχουμε κατά νου είναι ότι χρησιμοποιούμε ένα διάνυσμα στην πηγή φωτός. Δηλαδή, εάν η κατεύθυνση των ακτίνων είναι (1,0, -1,0, 0) - πρέπει να χρησιμοποιήσουμε το διάνυσμα (-1,0, 1,0, 0). Το μόνο που μας δυσκολεύει είναι το κανονικό διάνυσμα. Ο υπολογισμός του κανονικού στο επίπεδο είναι απλός - πρέπει να παράγουμε διανυσματικό προϊόνδύο διανύσματα που το περιγράφουν. Είναι σημαντικό να θυμάστε ότι το διανυσματικό γινόμενο είναι αντιμεταθετικό - πρέπει να λάβετε υπόψη τη σειρά των παραγόντων. Στην περίπτωσή μας, μπορούμε να πάρουμε το κανονικό στο τρίγωνο, γνωρίζοντας τις συντεταγμένες των κορυφών του σε χώρο πλέγματος, ως εξής (Παρακαλώ σημειώστε ότι δεν λαμβάνω υπόψη τις οριακές περιπτώσεις για p.x και p.y)

Float3 p1 = GetPosOnSphere(p); float3 p2 = GetPosOnSphere(float2(p.x + 1, p.y)); float3 p3 = GetPosOnSphere(float2(p.x, p.y + 1)); float3 v1 = p2 - p1; float3 v2 = p3 - p1; float3 n = normalzie(cross(v1, v2));
Αλλά δεν είναι μόνο αυτό. Οι περισσότερες από τις κορυφές του πλέγματος ανήκουν σε τέσσερα επίπεδα ταυτόχρονα. Για να έχετε ένα αποδεκτό αποτέλεσμα, πρέπει να υπολογίσετε το μέσο κανονικό ως εξής:

Na = κανονικοποίηση (n0 + n1 + n2 + n3)
Η εφαρμογή αυτής της μεθόδου σε μια GPU είναι αρκετά δαπανηρή - χρειαζόμαστε δύο στάδια για να υπολογίσουμε τα κανονικά και τον μέσο όρο τους. Επιπλέον, η αποτελεσματικότητα αφήνει πολλά να είναι επιθυμητή. Με βάση αυτό, επέλεξα μια άλλη μέθοδο - να χρησιμοποιήσω έναν κανονικό χάρτη (Εικ. 10).


Εικ. 10 Κανονικός χάρτης

Η αρχή της εργασίας με αυτό είναι η ίδια όπως και με έναν χάρτη ύψους - μετατρέπουμε τις σφαιρικές συντεταγμένες της κορυφής του πλέγματος σε υφή και κάνουμε μια επιλογή. Αλλά δεν θα μπορούμε να χρησιμοποιήσουμε αυτά τα δεδομένα απευθείας - τελικά, εργαζόμαστε με μια σφαίρα και η κορυφή έχει τη δική της κανονική που πρέπει να ληφθεί υπόψη. Επομένως, θα χρησιμοποιήσουμε τα κανονικά δεδομένα χάρτη ως συντεταγμένες της βάσης TBN. Τι είναι μια βάση; Εδώ είναι ένα παράδειγμα για εσάς. Φανταστείτε ότι είστε αστροναύτης και κάθεστε σε ένα φάρο κάπου στο διάστημα. Λαμβάνετε ένα μήνυμα από το MCC: "Πρέπει να μετακινηθείτε από τον φάρο 1 μέτρο προς τα αριστερά, 2 μέτρα επάνω και 3 μέτρα προς τα εμπρός." Πώς μπορεί αυτό να εκφραστεί μαθηματικά; (1, 0, 0) * 1 + (0, 1, 0) * 2 + (0, 0, 1) * 3 = (1,2,3). ΣΕ μορφή μήτραςΑυτή η εξίσωση μπορεί να εκφραστεί ως εξής:

Τώρα φανταστείτε ότι κάθεστε επίσης σε ένα φάρο, μόνο που τώρα σας γράφουν από το κέντρο ελέγχου: «Σας στείλαμε διανύσματα κατεύθυνσης εκεί - πρέπει να μετακινηθείτε 1 μέτρο κατά μήκος του πρώτου διανύσματος, 2 μέτρα κατά μήκος του δεύτερου και 3 μέτρα κατά μήκος του τρίτος." Η εξίσωση για τις νέες συντεταγμένες θα είναι:

Η εκρηκτική σημειογραφία μοιάζει με αυτό:

Ή σε μορφή μήτρας:

Άρα, ένας πίνακας με διανύσματα V1, V2 και V3 είναι μια βάση και το διάνυσμα (1,2,3) είναι οι συντεταγμένες στο χώρο αυτής της βάσης.

Ας φανταστούμε τώρα ότι έχετε ένα σύνολο διανυσμάτων (βάση Μ) και ξέρετε πού βρίσκεστε σε σχέση με τον φάρο (σημείο P). Πρέπει να μάθετε τις συντεταγμένες σας στο χώρο αυτής της βάσης - πόσο μακριά πρέπει να μετακινηθείτε κατά μήκος αυτών των διανυσμάτων για να καταλήξετε στο ίδιο μέρος. Ας φανταστούμε τις απαιτούμενες συντεταγμένες (X)

Αν τα P, M και X ήταν αριθμοί, απλά θα διαιρούσαμε και τις δύο πλευρές της εξίσωσης με το M, αλλά δυστυχώς... Ας πάμε αντίστροφα - σύμφωνα με την ιδιότητα του αντίστροφου πίνακα

Όπου εγώ είναι η μήτρα ταυτότητας. Στην περίπτωσή μας μοιάζει με αυτό

Τι μας δίνει αυτό; Δοκιμάστε να πολλαπλασιάσετε αυτόν τον πίνακα με Χ και θα πάρετε

Είναι επίσης απαραίτητο να διευκρινιστεί ότι ο πολλαπλασιασμός πίνακα έχει την ιδιότητα συσχέτισης

Μπορούμε εύλογα να θεωρήσουμε ένα διάνυσμα ως πίνακα 3 επί 1

Λαμβάνοντας υπόψη όλα τα παραπάνω, μπορούμε να συμπεράνουμε ότι για να πάρουμε το Χ στη δεξιά πλευρά της εξίσωσης, χρειαζόμαστε με τη σωστή σειράπολλαπλασιάστε και τις δύο πλευρές με τον αντίστροφο πίνακα M

Αυτό το αποτέλεσμα θα το χρειαστούμε αργότερα.

Τώρα ας επιστρέψουμε στο πρόβλημά μας. Θα χρησιμοποιήσω μια ορθοκανονική βάση - αυτό σημαίνει ότι τα V1, V2 και V3 είναι ορθογώνια μεταξύ τους (σχηματίζουν γωνία 90 μοιρών) και έχουν μήκος μονάδας. Το V1 θα είναι το εφαπτομενικό διάνυσμα, το V2 το διάνυσμα διπλής εφαπτομένης και το V3 θα είναι το κανονικό. Στην παραδοσιακή μεταφερόμενη μορφή του DirectX, ο πίνακας μοιάζει με αυτό:

Όπου T είναι το εφαπτομενικό διάνυσμα, B είναι το διεφαπτομένο διάνυσμα και N είναι το κανονικό. Ας τα βρούμε. Είναι πιο εύκολο με το κανονικό βασικά τα πάντααυτές είναι οι κανονικοποιημένες συντεταγμένες του σημείου. Το διάνυσμα διπλής εφαπτομένης είναι ίσο με το εγκάρσιο γινόμενο του κανονικού και του εφαπτομενικού διανύσματος. Το πιο δύσκολο πράγμα θα είναι με το διάνυσμα εφαπτομένης. Είναι ίση με τη φορά της εφαπτομένης στον κύκλο σε ένα σημείο. Ας δούμε αυτή τη στιγμή. Αρχικά, ας βρούμε τις συντεταγμένες ενός σημείου στον μοναδιαίο κύκλο στο επίπεδο XZ για κάποια γωνία α

Η κατεύθυνση της εφαπτομένης στον κύκλο σε αυτό το σημείο μπορεί να βρεθεί με δύο τρόπους. Το διάνυσμα στο σημείο του κύκλου και το διάνυσμα της εφαπτομένης είναι ορθογώνια - επομένως, αφού οι συναρτήσεις sin και cos είναι περιοδικές - μπορούμε απλά να προσθέσουμε pi/2 στη γωνία a και να πάρουμε την επιθυμητή κατεύθυνση. Σύμφωνα με την ιδιότητα μετατόπισης pi/2:

Πήραμε το ακόλουθο διάνυσμα:

Μπορούμε επίσης να χρησιμοποιήσουμε τη διαφοροποίηση - δείτε το Παράρτημα 3 για περισσότερες λεπτομέρειες. Έτσι, στο Σχήμα 11 μπορείτε να δείτε μια σφαίρα για την οποία έχει δημιουργηθεί μια βάση για κάθε κορυφή. Τα μπλε διανύσματα δηλώνουν κανονικά, τα κόκκινα - εφαπτομενικά διανύσματα, τα πράσινα - διπλά εφαπτομένα διανύσματα.


Εικ.11 Σφαίρα με βάσεις TBN σε κάθε κορυφή. Κόκκινο - εφαπτομενικά διανύσματα, πράσινο - διανύσματα διπλής εφαπτομένης, μπλε διανύσματα - κανονικά

Τακτοποιήσαμε τη βάση - τώρα ας πάρουμε έναν κανονικό χάρτη. Για να το κάνουμε αυτό, θα χρησιμοποιήσουμε το φίλτρο Sobel. Το φίλτρο Sobel υπολογίζει τη διαβάθμιση φωτεινότητας της εικόνας σε κάθε σημείο (χονδρικά μιλώντας, το διάνυσμα αλλαγής φωτεινότητας). Η αρχή του φίλτρου είναι ότι πρέπει να εφαρμόσετε μια συγκεκριμένη μήτρα τιμών, η οποία ονομάζεται "Πυρήνας", σε κάθε pixel και τους γείτονές του εντός της διάστασης αυτού του πίνακα. Ας υποθέσουμε ότι επεξεργαζόμαστε το pixel P με τον πυρήνα K. Εάν δεν βρίσκεται στην άκρη της εικόνας, τότε έχει οκτώ γείτονες - πάνω αριστερά, πάνω, πάνω δεξιά κ.λπ. Ας τα ονομάσουμε tl, t, tb, l, r, bl, b, br. Έτσι, η εφαρμογή του πυρήνα K σε αυτό το pixel έχει ως εξής:

Pn = tl * K(0, 0) + t * K(0,1) + tb * K(0,2) +
          l * K(1, 0) + P * K(1,1) + r * K(1,2) +
          bl * K(2, 0) + b * K(2,1) + br * K(2,2)

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

Η βάση είναι εκεί - μπορείτε να αρχίσετε να την εφαρμόζετε. Πρώτα πρέπει να υπολογίσουμε τη φωτεινότητα του pixel. Χρησιμοποιώ τη μετατροπή από το χρωματικό μοντέλο RGB στο μοντέλο YUV για το σύστημα PAL:

Αλλά επειδή η εικόνα μας είναι αρχικά σε κλίμακα του γκρι, αυτό το βήμα μπορεί να παραλειφθεί. Τώρα πρέπει να «συμπέσει» η αρχική εικόνα με πυρήνες Kx και Ky. Αυτό θα μας δώσει τις συνιστώσες X και Y της διαβάθμισης. Η κανονική τιμή αυτού του διανύσματος μπορεί επίσης να είναι πολύ χρήσιμη - δεν θα τη χρησιμοποιήσουμε, αλλά οι εικόνες που περιέχουν κανονικές κανονικές τιμές διαβάθμισης έχουν ορισμένες χρήσιμες χρήσεις. Με τον όρο κανονικοποίηση εννοώ την ακόλουθη εξίσωση

Όπου V είναι η τιμή που κανονικοποιούμε, το Vmin και το Vmax είναι το εύρος αυτών των τιμών. Στην περίπτωσή μας, οι ελάχιστες και οι μέγιστες τιμές παρακολουθούνται κατά τη διαδικασία παραγωγής. Ακολουθεί ένα παράδειγμα υλοποίησης ενός φίλτρου Sobel:

Float SobelFilter::GetGrayscaleData(const Point2 &Coords) ( Point2 coords; coords.x = Math::Saturate(Coords.x, RangeI(0, image.size.width - 1)); coords.y = Math::Saturate( Coords.y, RangeI(0, image.size.height - 1)); επιστροφή (image.pixelFormat == PXL_FMT_R8) : (0,30f * pixel + //R 0,59f * pixel + //G 0,11f * pixel ) void SobelFilter::Process() (ΕύροςF dirXVr), dirYVr, magNormVr για(int32_t y = 0; y< image.size.height; y++) for(int32_t x = 0; x < image.size.width; x++){ float tl = GetGrayscaleData({x - 1, y - 1}); float t = GetGrayscaleData({x , y - 1}); float tr = GetGrayscaleData({x + 1, y - 1}); float l = GetGrayscaleData({x - 1, y }); float r = GetGrayscaleData({x + 1, y }); float bl = GetGrayscaleData({x - 1, y + 1}); float b = GetGrayscaleData({x , y + 1}); float br = GetGrayscaleData({x + 1, y + 1}); float dirX = -1.0f * tl + 0.0f + 1.0f * tr + -2.0f * l + 0.0f + 2.0f * r + -1.0f * bl + 0.0f + 1.0f * br; float dirY = -1.0f * tl + -2.0f * t + -1.0f * tr + 0.0f + 0.0f + 0.0f + 1.0f * bl + 2.0f * b + 1.0f * br; float magNorm = sqrtf(dirX * dirX + dirY * dirY); int32_t ind = y * image.size.width + x; dirXData = dirX; dirYData = dirY; magNData = magNorm; dirXVr.Update(dirX); dirYVr.Update(dirY); magNormVr.Update(magNorm); } if(normaliseDirections){ for(float &dirX: dirXData) dirX = (dirX - dirXVr.minVal) / (dirXVr.maxVal - dirXVr.minVal); for(float &dirY: dirYData) dirY = (dirY - dirYVr.minVal) / (dirYVr.maxVal - dirYVr.minVal); } for(float &magNorm: magNData) magNorm = (magNorm - magNormVr.minVal) / (magNormVr.maxVal - magNormVr.minVal); }
Πρέπει να πούμε ότι το φίλτρο Sobel έχει την ιδιότητα της γραμμικής διαχωρισιμότητας, επομένως αυτή η μέθοδος μπορεί να βελτιστοποιηθεί.

Το δύσκολο μέρος έχει τελειώσει - το μόνο που μένει είναι να γράψουμε τις συντεταγμένες X και Y της κατεύθυνσης της κλίσης στα κανάλια R και G των κανονικών εικονοστοιχείων χάρτη Για τη συντεταγμένη Z χρησιμοποιώ ένα. Χρησιμοποιώ επίσης ένα τρισδιάστατο διάνυσμα συντελεστών για να προσαρμόσω αυτές τις τιμές. Το παρακάτω είναι ένα παράδειγμα δημιουργίας ενός κανονικού χάρτη με σχόλια:

//ImageProcessing::ImageData Image - αρχική εικόνα. Ο κατασκευαστής περιέχει τη μορφή pixel και τα δεδομένα εικόνας ImageProcessing::SobelFilter sobelFilter; sobelFilter.Init(Εικόνα); sobelFilter.NormaliseDirections() = false; sobelFilter.Process(); const auto &resX =sobelFilter.GetFilteredData(ImageProcessing::SobelFilter::SOBEL_DIR_X); const auto &resY =sobelFilter.GetFilteredData(ImageProcessing::SobelFilter::SOBEL_DIR_Y); ImageProcessing::ImageData destImage = (DXGI_FORMAT_R8G8B8A8_UNORM, Image.size); size_t dstImageSize = Image.size.width * Image.size.height * destImage.pixelSize; std:: vector dstImgPixels(dstImageSize); for(int32_t d = 0; d< resX.size(); d++){ //используем вектор настроечных коэффициентов. У меня он равен (0.03, 0.03, 1.0) Vector3 norm = Vector3::Normalize({resX[d] * NormalScalling.x, resY[d] * NormalScalling.y, 1.0f * NormalScalling.z}); Point2 coords(d % Image.size.width, d / Image.size.width); int32_t offset = (coords.y * Image.size.width + coords.x) * destImage.pixelSize; uint8_t *pixel = &dstImgPixels; //переводим значения из области [-1.0, 1.0] в а затем в область pixel = (0.5f + norm.x * 0.5f) * 255.999f; pixel = (0.5f + norm.y * 0.5f) * 255.999f; pixel = (0.5f + norm.z * 0.5f) * 255.999f; } destImage.pixels = &dstImgPixels; SaveImage(destImage, OutFilePath);
Τώρα θα δώσω ένα παράδειγμα χρήσης ενός κανονικού χάρτη σε shader:

//texCoords - συντεταγμένες υφής που λάβαμε χρησιμοποιώντας τη μέθοδο που περιγράφεται στην παράγραφο 4 //normalL - κορυφή κανονική //lightDir - διάνυσμα στην πηγή φωτός //Ld - χρώμα της πηγής φωτός //Kd - χρώμα του υλικού του φωτιζόμενη επιφάνεια float4 normColor = mainNormalTex SampleLevel(mainNormalTexSampler, texCoords, 0); //μεταφέρετε την τιμή από την περιοχή στο [-1.0, 1.0] και κανονικοποιήστε το αποτέλεσμα float3 normalT = normalize(2.0f * mainNormColor.rgb - 1.0f); //μετάφραση της συντεταγμένης υφής X από την περιοχή σε float ang = texCoords.x * 3.141592f * 2.0f; float3 εφαπτομένη; εφαπτομένη.x = -sin(ang); εφαπτομένη.y = 0.0f; εφαπτομένη.z = cos(ang); float3 bitangent = normalize(cross(normalL, εφαπτομένη)); float3x3 tbn = float3x3(εφαπτομένη, διπλή, κανονικήL); float3 resNormal = mul(normalT, tbn); float diff = saturate(dot(resNormal, lightDir.xyz)); float4 resColor = Ld * Kd * diff;

6. Επίπεδο Λεπτομέρειας

Λοιπόν, τώρα το τοπίο μας φωτίστηκε! Μπορείτε να πετάξετε στη Σελήνη - προσθέστε έναν χάρτη ύψους, ορίστε το χρώμα του υλικού, φορτώστε τους τομείς, ορίστε το μέγεθος πλέγματος σε (16, 16) και... Ναι, κάτι είναι πολύ μικρό - θα βάλω (256 , 256) - Ω, κάτι επιβραδύνει τα πάντα , και γιατί η υψηλή λεπτομέρεια σε μακρινούς τομείς; Επιπλέον, όσο πιο κοντά βρίσκεται ο παρατηρητής στον πλανήτη, τόσο λιγότερους τομείς μπορεί να δει. Ναι... έχουμε πολλή δουλειά ακόμα! Ας καταλάβουμε πρώτα πώς να κόψουμε περιττούς τομείς. Η καθοριστική τιμή εδώ θα είναι το ύψος του παρατηρητή από την επιφάνεια του πλανήτη - όσο υψηλότερο είναι, τόσο περισσότερους τομείς μπορεί να δει (Εικ. 12)


Εικ. 12 Εξάρτηση του ύψους του παρατηρητή από τον αριθμό των επεξεργασμένων τομέων

Βρίσκουμε το ύψος ως εξής - κατασκευάζουμε ένα διάνυσμα από τη θέση του παρατηρητή στο κέντρο της σφαίρας, υπολογίζουμε το μήκος του και αφαιρούμε την τιμή της ακτίνας της σφαίρας από αυτό. Προηγουμένως είπα ότι εάν το κλιμακωτό γινόμενο του διανύσματος από τον παρατηρητή και του διανύσματος από το κέντρο του τομέα είναι μικρότερο από μηδέν, τότε αυτός ο τομέας δεν μας ενδιαφέρει - τώρα αντί για μηδέν θα χρησιμοποιήσουμε μια τιμή που εξαρτάται γραμμικά από το ύψος. Αρχικά, ας ορίσουμε τις μεταβλητές - έτσι θα έχουμε μια ελάχιστη και μέγιστη τιμή για το γινόμενο κουκίδων και μια ελάχιστη και μέγιστη τιμή για το ύψος. Ας κατασκευάσουμε το παρακάτω σύστημα εξισώσεων

Τώρα ας εκφράσουμε το Α στη δεύτερη εξίσωση

Ας αντικαταστήσουμε το Α από τη δεύτερη εξίσωση στην πρώτη

Ας εκφράσουμε το Β από την πρώτη εξίσωση

Αντικαταστήστε το Β από την πρώτη εξίσωση στη δεύτερη

Τώρα ας αντικαταστήσουμε τις μεταβλητές στη συνάρτηση

Και θα λάβουμε

Όπου Hmin και Hmax είναι οι ελάχιστες και μέγιστες τιμές ύψους, οι Dmin και Dmax είναι οι ελάχιστες και οι μέγιστες τιμές του κλιμακωτού γινομένου. Αυτό το πρόβλημα μπορεί να λυθεί διαφορετικά - βλέπε Παράρτημα 4.

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

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

Αφού το λύσαμε, παίρνουμε

Χρησιμοποιώντας αυτούς τους συντελεστές, ορίζουμε τη συνάρτηση

Όπου Rmax είναι το εύρος του βαθμωτού προϊόντος (D(H) - Dmin), το Rmin είναι το ελάχιστο εύρος που καθορίζεται από το επίπεδο. Χρησιμοποιώ τιμή 0,01. Τώρα πρέπει να αφαιρέσουμε το αποτέλεσμα από το Dmax

Χρησιμοποιώντας αυτή τη λειτουργία θα λάβουμε περιοχές για όλα τα επίπεδα. Εδώ είναι ένα παράδειγμα:

Const float dotArea = dotRange.maxVal - dotRange.minVal; const float Rmax = dotArea, Rmin = 0,01f; float lodsCnt = lods.size(); float A = Rmax; float B = powf(Rmin / Rmax, 1.0f / (lodsCnt - 1.0f)); για(μέγεθος_t g = 0; g< lods.size(); g++){ lods[g].dotRange.minVal = dotRange.maxVal - A * powf(B, g); lods[g].dotRange.maxVal = dotRange.maxVal - A * powf(B, g + 1); } lods.dotRange.maxVal = 1.0f;
Τώρα μπορούμε να προσδιορίσουμε σε ποιο επίπεδο λεπτομέρειας ανήκει ο τομέας (Εικ. 13).


Εικ. 13 Χρωματική διαφοροποίηση τομέων ανάλογα με τα επίπεδα λεπτομέρειας

Στη συνέχεια, πρέπει να υπολογίσετε το μέγεθος των πλεγμάτων. Θα είναι πολύ ακριβό να αποθηκεύσετε το δικό σας πλέγμα για κάθε επίπεδο - είναι πολύ πιο αποτελεσματικό να αλλάζετε τη λεπτομέρεια ενός πλέγματος εν κινήσει χρησιμοποιώντας το tessellation. Για να γίνει αυτό, χρειαζόμαστε, εκτός από τους συνηθισμένους σκιαδιστές κορυφής και εικονοστοιχείων, να εφαρμόσουμε επίσης σκιαδιστές γάστρας και τομέα. Στο Shader Hull, το κύριο καθήκον είναι η προετοιμασία σημείων ελέγχου. Αποτελείται από δύο μέρη - την κύρια συνάρτηση και τη συνάρτηση που υπολογίζει τις παραμέτρους του σημείου ελέγχου. Πρέπει να καθορίσετε τιμές για τα ακόλουθα χαρακτηριστικά:

τομέα
διαμέριση
εξόδου
σημεία ελέγχου εξόδου
patchconstantfunc
Ακολουθεί ένα παράδειγμα σκίαστρου Hull για τρίγωνα πλακάκια:

Struct PatchData ( ακμές float : SV_TessFactor; float inside: SV_InsideTessFactor; ); PatchData GetPatchData(InputPatch Patch, uint PatchId: SV_PrimitiveID) ( Έξοδος PatchData; flloat tessFactor = 2.0f; output.edges = tessFactor; output.edges = tessFactor; output.edges = tessFactor; output.inside = return outputnProcess;Hull; Patch, Uint PointId: SV_OutputControlPointID, Uint PatchId: SV_PrimitiveID) (Επιστροφή Ενημερωμένης έκδοσης κώδικα; )
Βλέπετε, η κύρια εργασία γίνεται στο GetPatchData(). Καθήκον του είναι να καθορίσει τον παράγοντα tessellation. Θα το συζητήσουμε αργότερα, τώρα ας περάσουμε στο Domain shader. Λαμβάνει σημεία ελέγχου από τον Shader Hull και συντεταγμένες από τον tessellator. Η νέα τιμή των συντεταγμένων θέσης ή υφής στην περίπτωση διαίρεσης με τρίγωνα πρέπει να υπολογιστεί χρησιμοποιώντας τον ακόλουθο τύπο

N = C1 * F.x + C2 * F.y + C3 * F.z

Όπου C1, C2 και C3 είναι οι τιμές των σημείων ελέγχου, F είναι οι συντεταγμένες του tessellator. Επίσης στο Domain shader θα πρέπει να ορίσετε το χαρακτηριστικό domain, η τιμή του οποίου αντιστοιχεί σε αυτή που καθορίζεται στο Hull shader. Ακολουθεί ένα παράδειγμα σκίασης τομέα:

Cbuffer buff0: register(b0) ( matrix worldViewProj; ) struct PatchData ( float edges : SV_TessFactor; float inside: SV_InsideTessFactor; ); Pin ProcessDomain(PatchData Patch, float3 Coord: SV_DomainLocation, const OutputPatch
Tri) ( float3 posL = Tri.posL * Coord.x + Tri.posL * Coord.y + Tri.posL * Coord.z; float2 texCoords = Tri.texCoords * Coord.x + Tri.texCoords * Coord.y + Tri .texCoords * Pin output.posH = mul(float4(posL, 1.0f), output.normalW = Tri.normalW; επιστροφή εξόδου;

Ο ρόλος του vertex shader σε αυτή την περίπτωση μειώνεται στο ελάχιστο - για μένα απλά "περνάει" τα δεδομένα στο επόμενο στάδιο.

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

Λύνοντάς το με τον ίδιο τρόπο όπως πριν, παίρνουμε
Όπου το Tmin και το Tmax είναι οι ελάχιστοι και μέγιστοι συντελεστές αποτύπωσης, το Hmin και το Hmax είναι οι ελάχιστες και μέγιστες τιμές ύψους του παρατηρητή. Ο ελάχιστος συντελεστής μου είναι ένας. το μέγιστο ορίζεται ξεχωριστά για κάθε επίπεδο

(για παράδειγμα 1, 2, 4, 16).

Ας πάρουμε τον δεκαδικό λογάριθμο των δύο πλευρών της εξίσωσης

Σύμφωνα με την ιδιότητα των λογαρίθμων, μπορούμε να ξαναγράψουμε την εξίσωση ως εξής

Τώρα το μόνο που έχουμε να κάνουμε είναι να διαιρέσουμε και τις δύο πλευρές με το αρχείο καταγραφής (2)

Αλλά δεν είναι μόνο αυτό. Το Χ είναι περίπου 2,58. Στη συνέχεια, πρέπει να επαναφέρετε το κλασματικό μέρος και να αυξήσετε το δύο στη δύναμη του αριθμού που προκύπτει. Εδώ είναι ο κώδικας για τον υπολογισμό των συντελεστών τεσσαλοποίησης για επίπεδα λεπτομέρειας

Float h = camera->GetHeight(); const ΕύροςF &hR = Εύρος ύψους; for(LodsStorage::Lod &lod: lods)( //προέρχεται από το σύστημα //A + B * Hmax = Lmin //A + B * Hmin = Lmax //και να πάρει το A και στη συνέχεια την αντικατάσταση B στη δεύτερη ισότητα float mTf = (float )lod.GetMaxTessFactor(); = pow(2.0f, όροφος(log(tessFactor) / log(2)));

7. Θόρυβος

Ας δούμε πώς μπορούμε να αυξήσουμε τη λεπτομέρεια του τοπίου χωρίς να αλλάξουμε το μέγεθος του χάρτη ύψους. Αυτό που μου έρχεται στο μυαλό είναι να αλλάξω την τιμή του ύψους στην τιμή που προκύπτει από την υφή του θορύβου κλίσης. Οι συντεταγμένες στις οποίες θα κάνουμε δείγμα θα είναι Ν φορές μεγαλύτερες από τις κύριες. Κατά τη δειγματοληψία, θα χρησιμοποιηθεί ο τύπος κατοπτρικής διεύθυνσης (D3D11_TEXTURE_ADDRESS_MIRROR) (βλ. Εικ. 14).


Εικ. 14 Σφαίρα με χάρτη ύψους + σφαίρα με χάρτη θορύβου = σφαίρα με τελικό ύψος

Στην περίπτωση αυτή, το ύψος θα υπολογιστεί ως εξής:

//float2 tc1 - συντεταγμένες υφής που λαμβάνονται από ένα κανονικοποιημένο σημείο, όπως //περιγράφηκε νωρίτερα //texCoordsScale - πολλαπλασιαστής συντεταγμένων υφής. Στην περίπτωσή μου, ισούται με την τιμή 300 //mainHeightTex, mainHeightTexSampler - υφή χάρτη ύψους //distHeightTex, distHeightTexSampler - υφή θορύβου κλίσης //maxTerrainHeight - μέγιστο ύψος οριζόντιου. Στην περίπτωσή μου 0,03 float2 tc2 = tc1 * texCoordsScale; float4 mainHeightTexColor = mainHeightTex.SampleLevel(mainHeightTexSampler, tc1, 0); float4 distHeighTexColor = distHeightTex.SampleLevel(distHeightTexSampler, tc2, 0); ύψος πλωτήρας = (mainHeighTexColor.x + distHeighTexColor.x) * maxTerrainHeight;
Μέχρι στιγμής ο περιοδικός χαρακτήρας εκφράζεται σημαντικά, αλλά με την προσθήκη φωτισμού και υφής η κατάσταση θα αλλάξει προς το καλύτερο. Τι είναι η υφή θορύβου κλίσης; Σε γενικές γραμμές, είναι ένα πλέγμα τυχαίων τιμών. Ας δούμε πώς να ταιριάξουμε τα μεγέθη του πλέγματος στο μέγεθος της υφής. Ας υποθέσουμε ότι θέλουμε να δημιουργήσουμε μια υφή θορύβου μεγέθους 256 επί 256 pixel. Όλα είναι απλά, αν οι διαστάσεις του πλέγματος συμπίπτουν με τις διαστάσεις της υφής, θα πάρουμε κάτι παρόμοιο με τον λευκό θόρυβο σε μια τηλεόραση. Τι γίνεται όμως αν το πλέγμα μας έχει διαστάσεις, ας πούμε, 2 επί 2; Η απάντηση είναι απλή - χρησιμοποιήστε παρεμβολή. Μια διατύπωση γραμμικής παρεμβολής μοιάζει με αυτό:

Αυτή είναι η πιο γρήγορη, αλλά ταυτόχρονα η λιγότερο κατάλληλη επιλογή για εμάς. Είναι καλύτερα να χρησιμοποιήσετε παρεμβολή με συνημιτονικό:

Αλλά δεν μπορούμε απλώς να παρεμβάλουμε τις τιμές στις άκρες της διαγώνιας (κάτω αριστερή και πάνω δεξιά γωνία του κελιού). Στην περίπτωσή μας, η παρεμβολή θα χρειαστεί να εφαρμοστεί δύο φορές. Ας φανταστούμε ένα από τα κελιά πλέγματος. Έχει τέσσερις γωνίες - ας τις ονομάσουμε V1, V2, V3, V4. Επίσης, μέσα σε αυτό το κελί θα υπάρχει το δικό του δισδιάστατο σύστημα συντεταγμένων, όπου το σημείο (0, 0) αντιστοιχεί στο V1 και το σημείο (1, 1) στο V3 (βλ. Εικ. 15a). Για να λάβουμε μια τιμή με συντεταγμένες (0,5, 0,5), πρέπει πρώτα να λάβουμε δύο τιμές παρεμβολής X - μεταξύ V1 και V4 και μεταξύ V2 και V3, και τέλος Y- παρεμβολή μεταξύ αυτών των τιμών (Εικ. 15β).

Εδώ είναι ένα παράδειγμα:

Float2 coords(0.5f, 0.5f) float4 P1 = lerp(V1, V4, coords.x); float4 P2 = lerp(V2, V3, coords.x); float4 P = lerp(P1, P2, coords.y)


Εικ. 15 α - Εικόνα κυψέλης πλέγματος με συντεταγμένες V1, V2, V3 και V4. β - Ακολουθία δύο παρεμβολών χρησιμοποιώντας ένα κελί ως παράδειγμα

Τώρα ας κάνουμε τα εξής - για κάθε εικονοστοιχείο της υφής θορύβου, πάρτε την τιμή παρεμβολής για το πλέγμα 2x2 και, στη συνέχεια, προσθέστε την τιμή παρεμβολής για το πλέγμα 4x4, πολλαπλασιαζόμενη επί 0,5, στη συνέχεια για το πλέγμα 8x8, πολλαπλασιαζόμενη επί 0,25 κ.λπ. μέχρι ένα ορισμένο όριο - αυτό ονομάζεται οκτάβες πρόσθεσης (Εικ. 16). Ο τύπος μοιάζει με αυτό:


Εικ. 16 Παράδειγμα προσθήκης οκτάβων

Ακολουθεί ένα παράδειγμα υλοποίησης:

For(int32_t x = 0; x< size.width; x++) for(int32_t y = 0; y < size.height; y++){ float val = 0.0f; Vector2 normPos = {(float)x / (float)(sideSize - 1), (float)y / (float)(sideSize - 1)}; for(int32_t o = 0; o < octavesCnt; o++){ float frequency = powf(2.0f, (float)(startFrequency + o)); float intencity = powf(intencityFactor, (float)o); Vector2 freqPos = normPos * frequency; Point2 topLeftFreqPos = Cast(freqPos);
Επίσης για τα V1, V2, V3 και V4 μπορείτε να πάρετε το άθροισμα από την ίδια την τιμή και τους γείτονές της ως εξής:

Float GetSmoothValue(const Point2 &Coords) ( float corners = (GetValue((Coords.x - 1, Coords.y - 1)) + GetValue((Coords.x + 1, Coords.y - 1)) + GetValue((Coords .x - 1, Coords.y + 1)) + GetValue((Coords.x + 1, Coords.y + 1))) / 16.0f float sides = (GetValue((Coords.x - 1, Coords.y )) + GetValue((Coords.x + 1, Coords.y)) + GetValue((Coords.x, Coords.y - 1)) + GetValue((Coords.x, Coords.y + 1))) / 8.0 f;
και χρησιμοποιήστε αυτές τις τιμές για παρεμβολή. Εδώ είναι ο υπόλοιπος κώδικας:

Float GetInterpolatedValue(const Point2 &TopLeftCoord, const Point2 &BottomRightCoord, float XFactor, float YFactor) ( Point2 tlCoords(TopLeftCoord.x, TopLeftCoord.y); Point2 trCoords(BottomxCoordsCoord.2TopRight ord.x, BottomRightCoord .y) ; ΗΠΑ) ?
Ολοκληρώνοντας την υποενότητα, θέλω να πω ότι όλα όσα περιέγραψα μέχρι αυτό το σημείο είναι μια ελαφρώς διαφορετική υλοποίηση του θορύβου Perlin από την κανονική.
Τακτοποιήσαμε τα ύψη - τώρα ας δούμε τι να κάνουμε με τα κανονικά. Όπως και με τον κύριο χάρτη ύψους, πρέπει να δημιουργήσουμε έναν κανονικό χάρτη από την υφή του θορύβου. Στη συνέχεια στο shader προσθέτουμε απλά το κανονικό από τον κύριο χάρτη με το κανονικό από το noise texture. Πρέπει να πω ότι αυτό δεν είναι απολύτως σωστό, αλλά δίνει ένα αποδεκτό αποτέλεσμα. Εδώ είναι ένα παράδειγμα:
//float2 texCoords1 - συντεταγμένες υφής που λαμβάνονται από το κανονικοποιημένο σημείο, όπως περιγράφηκε προηγουμένως //mainNormalTex, mainNormalTexSampler - κύριος κανονικός χάρτης //distNormalTex, distNormalTexSampler - θόρυβος κλίσης κανονικός χάρτης float2 texCoords2 = texCoords1 * texCoord;s; float4 mainNormColor = mainNormalTex.SampleLevel(mainNormalTexSampler, TexCoords1, 0); float4 distNormColor = distNormalTex.SampleLevel(distNormalTexSampler, TexCoords2, 0); float3 mainNormal = 2.0f * mainNormColor.rgb - 1.0f; float3 distNormal = 2.0f * distNormColor.rgb - 1.0f; float3 normal = normalize(mainNormal + distNormal);

8. Παρουσίαση υλικού

Ας κάνουμε βελτιστοποίηση. Τώρα ο κύκλος σχεδίασης τομέων σε ψευδοκώδικα μοιάζει με αυτό

Κάντε βρόχο σε όλους τους τομείς, υπολογίστε το κλιμακωτό γινόμενο ενός διανύσματος κατά ένα σημείο και ενός διανύσματος κατά το κέντρο του τομέα, εάν είναι μεγαλύτερο από μηδέν, ορίστε την τιμή του S στα δεδομένα shader, ορίστε την τιμή του V1 στο shader δεδομένα, ορίστε την τιμή του V2 στα δεδομένα shader, σχεδιάστε το πλέγμα, τέλος συνθήκης, τέλος βρόχου σε όλους τους τομείς
Η απόδοση αυτής της προσέγγισης είναι εξαιρετικά χαμηλή. Υπάρχουν πολλές επιλογές βελτιστοποίησης - μπορείτε να δημιουργήσετε ένα τετραδέντρο για κάθε επίπεδο του κύβου, ώστε να μην υπολογίσετε το βαθμωτό γινόμενο για κάθε τομέα. Μπορείτε επίσης να ενημερώσετε τις τιμές των V1 και V2 όχι για κάθε τομέα, αλλά για τα έξι επίπεδα του κύβου στον οποίο ανήκουν. Διάλεξα την τρίτη επιλογή - Instancing. Εν συντομία για το τι είναι. Ας πούμε ότι θέλετε να σχεδιάσετε ένα δάσος. Έχετε ένα μοντέλο δέντρου και έχετε επίσης ένα σύνολο πινάκων μετασχηματισμού - θέσεις δέντρων, πιθανή κλιμάκωση ή περιστροφή. Μπορείτε να δημιουργήσετε ένα buffer που θα περιέχει τις κορυφές όλων των δέντρων που έχουν μετατραπεί σε παγκόσμιο διάστημα - αυτή είναι μια καλή επιλογή, το δάσος δεν τρέχει γύρω από τον χάρτη. Αλλά τι γίνεται αν χρειαστεί να εφαρμόσετε μετασχηματισμούς - ας πούμε, δέντρα που ταλαντεύονται στον άνεμο. Μπορείτε να το κάνετε αυτό - αντιγράψτε τα δεδομένα των κορυφών του μοντέλου N φορές σε ένα buffer, προσθέτοντας τον δείκτη δέντρου (από 0 έως N) στα δεδομένα κορυφής. Στη συνέχεια, ενημερώνουμε τον πίνακα των πινάκων μετασχηματισμού και τον περνάμε ως μεταβλητή στο shader. Στο shader επιλέγουμε τον επιθυμητό πίνακα από τον δείκτη δέντρου. Πώς μπορείτε να αποφύγετε την επικάλυψη δεδομένων; Αρχικά, θα ήθελα να επιστήσω την προσοχή σας στο γεγονός ότι αυτές οι κορυφές μπορούν να συλλεχθούν από πολλά buffers. Για να περιγράψετε μια κορυφή, πρέπει να καθορίσετε το ευρετήριο πηγής στο πεδίο InputSlot της δομής D3D11_INPUT_ELEMENT_DESC. Αυτό μπορεί να χρησιμοποιηθεί κατά την εφαρμογή μεταμόρφωσης κινούμενων εικόνων προσώπου - ας υποθέσουμε ότι έχετε δύο buffer κορυφών που περιέχουν δύο καταστάσεις προσώπου και θέλετε να παρεμβάλετε γραμμικά αυτές τις τιμές. Δείτε πώς μπορείτε να περιγράψετε μια κορυφή:

D3D11_INPUT_ELEMENT_DESC desc = ( /*part1*/ ("POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0), ("NORMAL_XGI,30FLOAT" 12, D3D11_INPUT_PER_VERTEX_DATA, 0), ("TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0), /*part2*/ ("POSITION", 1, DXGI_FORMAT_R32G32B32_FLOAT, 30D_1 NORM" AL", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 12 , D3D11_INPUT_PER_VERTEX_DATA , 0), ("TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 24, D3D11_INPUT_PER_VERTEX_DATA, 0) )
στο shader η κορυφή πρέπει να περιγράφεται ως εξής:

Struct Vin (float3 position1: POSITION0; float3 normal1: NORMAL0; float2 tex1: TEXCOORD0; float3 position2: POSITION1; float3 normal2: POSITION0; float2 tex2: TEXCOORD1; )
τότε απλώς παρεμβάλλετε τις τιμές

Float3 res = lerp(input.position1, input.position2, factor);
Γιατί το λέω αυτό; Ας επιστρέψουμε στο παράδειγμα των δέντρων. Θα συλλέξουμε την κορυφή από δύο πηγές - η πρώτη θα περιέχει τη θέση στον τοπικό χώρο, τις συντεταγμένες υφής και την κανονική, η δεύτερη θα περιέχει τον πίνακα μετασχηματισμού με τη μορφή τεσσάρων τετραδιάστατων διανυσμάτων. Η περιγραφή της κορυφής μοιάζει με αυτό:

D3D11_INPUT_ELEMENT_DESC desc = ( /*part1*/ ("POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0), ("NORMAL_XGI,30FLOAT" 12, D3D11_INPUT_PER_VERTEX_DATA, 0), ("TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0), /*part2*/ ("WORLD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT3 1), ( "WORLD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA , 1), ("WORLD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D11_INPUT_PER_INSTANCE_DATA, 1), ("WORLD", 3, DXGI_FORMAT_R32G31_BAT2, DATA )
Λάβετε υπόψη ότι στο δεύτερο μέρος, το πεδίο InputSlotClass είναι ίσο με D3D11_INPUT_PER_INSTANCE_DATA και το πεδίο InstanceDataStepRate είναι ίσο με ένα (Για μια σύντομη περιγραφή του πεδίου InstanceDataStepRate, βλ. Παράρτημα 1). Σε αυτήν την περίπτωση, ο συλλέκτης θα χρησιμοποιήσει τα δεδομένα ολόκληρου του buffer από την πηγή με τύπο D3D11_INPUT_PER_VERTEX_DATA για κάθε στοιχείο από την πηγή με τύπο D3D11_INPUT_PER_INSTANCE_DATA. Σε αυτήν την περίπτωση, στο shader αυτές οι κορυφές μπορούν να περιγραφούν ως εξής:

Struct Vin ( float3 posL: POSITION; float3 normalL: NORMAL; float2 tex: TEXCOORD; row_major float4x4 world: WORLD; );
Δημιουργώντας ένα δεύτερο buffer με τα χαρακτηριστικά D3D11_USAGE_DYNAMIC και D3D11_CPU_ACCESS_WRITE, μπορούμε να το ενημερώσουμε από την πλευρά της CPU. Πρέπει να σχεδιάσετε αυτό το είδος γεωμετρίας χρησιμοποιώντας κλήσεις προς DrawInstanced() ή DrawIndexedInstanced(). Υπάρχουν επίσης κλήσεις προς DrawInstancedIndirect() και DrawIndexedInstancedIndirect() - σχετικά με αυτές, δείτε το Παράρτημα 2.

Ακολουθεί ένα παράδειγμα ρύθμισης buffer και χρήσης της συνάρτησης DrawIndexedInstanced():

//vb - buffer vertex //tb - buffer "instance" //ib - index buffer //vertexSize - μέγεθος του στοιχείου στην buffer κορυφής //instanceSize - μέγεθος του στοιχείου στην προσωρινή μνήμη "instance" //indicesCnt - αριθμός ευρετηρίων //instancesCnt - αριθμός "instances" std::vector buffers = (vb, tb); std:: vector strides = (vertexSize, instanceSize); std:: vector μετατοπίσεις = (0, 0); deviceContext->IASetVertexBuffers(0,buffers.size(),&buffers,&strides,&offsets); deviceContext->IASetIndexBuffer(ib, DXGI_FORMAT_R32_UINT, 0); deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); deviceContext->DrawIndexedInstanced(indexesCnt, instancesCnt, 0, 0, 0);
Τώρα ας επιστρέψουμε επιτέλους στο θέμα μας. Ο τομέας μπορεί να περιγραφεί από ένα σημείο στο επίπεδο στο οποίο ανήκει και δύο διανύσματα που περιγράφουν αυτό το επίπεδο. Επομένως, η κορυφή θα αποτελείται από δύο πηγές. Το πρώτο είναι οι συντεταγμένες του χώρου πλέγματος, το δεύτερο είναι τα δεδομένα τομέα. Η περιγραφή της κορυφής μοιάζει με αυτό:

Στδ::διάνυσμα meta = ( //συντεταγμένες στο χώρο του πλέγματος ("POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0) //διάνυσμα πρώτης όψης ("TEXCOORD", 0, FORMAT_2,201 _PER_INSTANCE_DATA, 1), / /Δεύτερο διάνυσμα προσώπου ("TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 12, D3D11_INPUT_PER_INSTANCE_DATA, 1), //έναρξη προσώπου ("TEXCOORD", 2, DXGI_32,2FLOAT_ _INPUT_PER_ INSTANCE_DATA, 1) )
Σημειώστε ότι χρησιμοποιώ ένα τρισδιάστατο διάνυσμα για την αποθήκευση των συντεταγμένων του χώρου πλέγματος (δεν χρησιμοποιείται συντεταγμένη z)

9. Σφαγή φρούτου

Ένα άλλο σημαντικό στοιχείο βελτιστοποίησης είναι η αποκοπή της πυραμίδας ορατότητας (Frustum cullling). Η πυραμίδα ορατότητας είναι η περιοχή της σκηνής που «βλέπει» η κάμερα. Πώς να το φτιάξετε; Αρχικά, να θυμάστε ότι ένα σημείο μπορεί να είναι σε τέσσερα συστήματα συντεταγμένων - τοπικά, παγκόσμια, συστήματα συντεταγμένων προβολής και προβολής. Η μετάβαση μεταξύ τους πραγματοποιείται μέσω πίνακες - κόσμος, είδος και μήτρα προβολής, και οι μετασχηματισμοί πρέπει να γίνονται διαδοχικά - από τοπικό σε κόσμο, από κόσμο σε είδος και τέλος από είδος σε χώρο προβολής. Όλοι αυτοί οι μετασχηματισμοί μπορούν να συνδυαστούν σε έναν πολλαπλασιάζοντας αυτούς τους πίνακες.

Χρησιμοποιούμε μια προοπτική προβολή, η οποία περιλαμβάνει τη λεγόμενη "ομοιόμορφη διαίρεση" - μετά τον πολλαπλασιασμό του διανύσματος (Px, Py, Pz, 1) με τον πίνακα προβολής, τα συστατικά του πρέπει να διαιρεθούν με το στοιχείο W αυτού του διανύσματος. Μετά τη μετάβαση στο χώρο προβολής και την ομοιόμορφη διαίρεση, το σημείο καταλήγει στο χώρο NDC. Ο χώρος NDC είναι ένα σύνολο τριών συντεταγμένων x, y, z, όπου τα x και y ανήκουν στο [-1, 1] και z - (Πρέπει να πούμε ότι στο OpenGL οι παράμετροι είναι ελαφρώς διαφορετικές).

Τώρα ας πάμε να λύσουμε το πρόβλημά μας. Στην περίπτωσή μου, η πυραμίδα βρίσκεται στο χώρο προβολής. Χρειαζόμαστε έξι επίπεδα που να το περιγράφουν (Εικ. 17α). Ένα επίπεδο μπορεί να περιγραφεί χρησιμοποιώντας ένα κανονικό και ένα σημείο που ανήκει σε αυτό το επίπεδο. Αρχικά, ας πάρουμε τα σημεία - για αυτό παίρνουμε το ακόλουθο σύνολο συντεταγμένων στο χώρο NDC:

Στδ::διάνυσμα Σημεία N = ( (-1.0f, -1.0f, 0.0f, 1.0f), (-1.0f, 1.0f, 0.0f, 1.0f), (1.0f, 1.0f, 0.0f, 1.0f), (1.0 f, -1,0f, 0,0f, 1,0f), (-1,0f, -1,0f, 1,0f, 1,0f), (-1,0f, 1,0f, 1,0f, 1,0f), ( 1,0f, 1,0f , 1.0f, 1.0f), (1.0f, -1.0f, 1.0f, 1.0f) );
Κοιτάξτε, στα πρώτα τέσσερα σημεία η τιμή z είναι 0 - αυτό σημαίνει ότι ανήκουν στο κοντινό επίπεδο αποκοπής, στα τελευταία τέσσερα το z είναι ίσο με 1 - ανήκουν στο μακρινό επίπεδο αποκοπής. Τώρα αυτά τα σημεία πρέπει να μετατραπούν σε χώρο προβολής. Αλλά πως;

Θυμηθείτε το παράδειγμα για τον αστροναύτη - έτσι είναι το ίδιο εδώ. Πρέπει να πολλαπλασιάσουμε τα σημεία με τον πίνακα αντίστροφης προβολής. Είναι αλήθεια ότι μετά από αυτό πρέπει ακόμα να διαιρέσουμε καθένα από αυτά με τη συντεταγμένη του W. Ως αποτέλεσμα, παίρνουμε τις απαραίτητες συντεταγμένες (Εικ. 17β). Ας δούμε τώρα τις κανονικές - πρέπει να κατευθύνονται μέσα στην πυραμίδα, επομένως πρέπει να επιλέξουμε την απαραίτητη σειρά για τον υπολογισμό του γινομένου του διανύσματος.

Matrix4x4 invProj = Matrix4x4::Inverse(camera->GetProjMatrix()); std:: vector PointsV; for(const Point4F &pN: pointsN)( Point4F pV = invProj.Transform(pN); pV /= pV.w; pointsV.push_back(Cast (pV)); ) επίπεδα = (pointsV, pointsV, pointsV); //κοντά σε επίπεδα = (pointsV, pointsV, pointsV); //μακριά επίπεδα επίπεδα = (pointsV, pointsV, pointsV); //αριστερό επίπεδο = (pointsV, pointsV, pointsV); //δεξιά επίπεδα = (pointsV, pointsV, pointsV); //top plane planes = (pointsV, pointsV, pointsV); //κάτω επίπεδα επίπεδα.normal *= -1.0f; αεροπλάνα.κανονική *= -1,0f;


Εικ. 17 Πυραμίδα ορατότητας

Η πυραμίδα είναι χτισμένη - ήρθε η ώρα να τη χρησιμοποιήσετε. Δεν σχεδιάζουμε αυτούς τους τομείς που δεν εμπίπτουν μέσα στην πυραμίδα. Για να προσδιορίσουμε εάν ένας τομέας βρίσκεται μέσα στην πυραμίδα ορατότητας, θα ελέγξουμε την οριοθέτηση που βρίσκεται στο κέντρο αυτού του τομέα. Αυτό δεν δίνει ακριβή αποτελέσματα, αλλά σε αυτήν την περίπτωσηΔεν βλέπω τίποτα κακό με τη σχεδίαση πολλών επιπλέον τομέων. Η ακτίνα της σφαίρας υπολογίζεται ως εξής:

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

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

Όπου P είναι ένα σημείο στο επίπεδο και S το κέντρο της σφαίρας. Τώρα ας υπολογίσουμε το βαθμωτό γινόμενο αυτού του διανύσματος και το κανονικό του επιπέδου. Ο προσανατολισμός μπορεί να προσδιοριστεί χρησιμοποιώντας το πρόσημο του προϊόντος κουκίδων - όπως αναφέρθηκε προηγουμένως, εάν είναι θετικό, τότε η σφαίρα είναι μπροστά από το επίπεδο, εάν είναι αρνητική, τότε η σφαίρα είναι πίσω. Μένει να καθοριστεί εάν η σφαίρα τέμνει το επίπεδο. Ας πάρουμε δύο διανύσματα - N (κανονικό διάνυσμα) και V. Τώρα ας κατασκευάσουμε ένα διάνυσμα από το N στο V - ας το ονομάσουμε K. Άρα, πρέπει να βρούμε ένα τέτοιο μήκος N ώστε να σχηματίζει μια γωνία 90 μοιρών με το K (τυπικά μιλώντας, έτσι ώστε το Ν και το Κ να είναι ορθογώνια). Oki doki, κοιτάξτε το Σχ. 18α - από τις ιδιότητες ενός ορθογωνίου τριγώνου το γνωρίζουμε

Πρέπει να βρούμε το συνημίτονο. Χρησιμοποιώντας την προαναφερθείσα ιδιότητα προϊόντος με κουκκίδες

Διαιρέστε και τις δύο πλευρές με |V|*|N| και παίρνουμε

Ας χρησιμοποιήσουμε αυτό το αποτέλεσμα:

Αφού |V| είναι απλώς ένας αριθμός, μπορούμε να τον μειώσουμε κατά |V|, και μετά παίρνουμε

Εφόσον το διάνυσμα N είναι κανονικοποιημένο, το τελευταίο βήμα είναι απλώς να το πολλαπλασιάσουμε με την τιμή που προκύπτει, διαφορετικά το διάνυσμα θα πρέπει να κανονικοποιηθεί - σε αυτήν την περίπτωση η τελική εξίσωση μοιάζει με αυτό:

Όπου D είναι το νέο μας διάνυσμα. Αυτή η διαδικασία ονομάζεται «Διανυσματική Προβολή» (Εικ. 18β). Αλλά γιατί το χρειαζόμαστε αυτό; Γνωρίζουμε ότι ένα διάνυσμα καθορίζεται από το μήκος και την κατεύθυνσή του και δεν αλλάζει με κανέναν τρόπο ανάλογα με τη θέση του - αυτό σημαίνει ότι αν τοποθετήσουμε το D έτσι ώστε να δείχνει στο S, τότε το μήκος του θα είναι ίσο με την ελάχιστη απόσταση από το S στο επίπεδο (Εικ. 18γ)


Εικ. 18 a Προβολή του N σε V, b Οπτική εμφάνιση του μήκους του προβαλλόμενου N σε σχέση με ένα σημείο, c Οπτική απεικόνιση
μήκος του προβαλλόμενου N όπως εφαρμόζεται σε μια σφαίρα με κέντρο το S

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

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

Bool Frustum::TestSphere(const Point3F &Pos, float Radius, const Matrix4x4 &WorldViewMatrix) const ( Point3F posV = WorldViewMatrix.Transform(Pos); for(const Plane &pl: planes)( Vector3 toSphV -V=pl. ::Dot(toSphPos, pl.normal)< -Radius) return false; } return true; }

10. Ρωγμές

Ένα άλλο πρόβλημα που πρέπει να λύσουμε είναι οι ρωγμές στα όρια των επιπέδων λεπτομέρειας (Εικ. 19).


Εικ. 19 επίδειξη ρωγμών τοπίου

Πρώτα απ 'όλα, πρέπει να εντοπίσουμε εκείνους τους τομείς που βρίσκονται στα όρια των επιπέδων λεπτομέρειας. Με την πρώτη ματιά, φαίνεται ότι πρόκειται για ένα έργο έντασης πόρων - σε τελική ανάλυση, ο αριθμός των τομέων σε κάθε επίπεδο αλλάζει συνεχώς. Αλλά αν χρησιμοποιείτε δεδομένα γειτνίασης, η λύση γίνεται πολύ πιο απλή. Τι είναι τα δεδομένα γειτνίασης; Κοιτάξτε, κάθε τομέας έχει τέσσερις γείτονες. Το σύνολο των αναφορών σε αυτά - είτε είναι δείκτες είτε ευρετήρια - είναι τα δεδομένα γειτνίασης. Με τη βοήθειά τους, μπορούμε εύκολα να προσδιορίσουμε ποιος τομέας βρίσκεται στα σύνορα - απλώς ελέγξτε σε ποιο επίπεδο ανήκουν οι γείτονές του.

Λοιπόν, ας βρούμε τους γείτονες κάθε τομέα. Και πάλι, δεν χρειάζεται να περιηγηθούμε σε όλους τους τομείς. Ας φανταστούμε ότι εργαζόμαστε με έναν τομέα με συντεταγμένες X και Y στο χώρο του πλέγματος.

Εάν δεν αγγίζει την άκρη του κύβου, τότε οι συντεταγμένες των γειτόνων του θα είναι οι εξής:

Κορυφαίος γείτονας - (X, Y - 1)
Κάτω γείτονας - (X, Y + 1)
Αριστερός γείτονας - (X - 1, Y)
Δεξιός γείτονας - (X + 1, Y)

Αν ο τομέας ακουμπήσει σε μια άκρη, τότε τον τοποθετούμε σε ειδικό δοχείο. Μετά την επεξεργασία και των έξι όψεων, θα περιέχει όλους τους οριακούς τομείς του κύβου. Σε αυτό το κοντέινερ πρέπει να εκτελέσουμε την αναζήτηση. Ας υπολογίσουμε εκ των προτέρων τα άκρα για κάθε τομέα:

Struct SectorEdges ( CubeSectors::Sector *owner; typedef std::pair Ακρη; άκρες άκρων? ) std:: vector ΤομείςΑκμές; //borderSectors - ένα κοντέινερ με τομείς συνόρων για(CubeSectors::Sector &sec: borderSectors)( // κάθε τομέας περιέχει δύο διανύσματα που περιγράφουν την όψη του κύβου // στον οποίο ανήκει Vector3 v1 = sec.vec1 * sec.sideSize ? , sec.startPos + v2);
Ακολουθεί η ίδια η αναζήτηση

For(SectorEdges &edgs: sektorsEdges) for(size_t e = 0; e< 4; e++) if(edgs.owner->adjacency[e] == nullptr) FindSectorEdgeAdjacency(edgs, (AdjacencySide)e, sektorsEdges);
Η συνάρτηση FindSectorEdgeAdjacency() μοιάζει με αυτό

Void CubeSectors::FindSectorEdgeAdjacency(SectorEdges &Sector, CubeSectors::AdjacencySide Side, std::vector &Neibs) ( SectorEdges::Edge &e = Sector.edges; for(SectorEdges &edgs2: Neibs)( if(edgs2.owner == Sector.owner) συνέχεια; for(size_t e = 0; e< 4; e++){ SectorEdges::Edge &e2 = edgs2.edges[e]; if((Math::Equals(e.first, e2.first) && Math::Equals(e.second, e2.second)) || (Math::Equals(e.second, e2.first) && Math::Equals(e.first, e2.second))) { Sector.owner->adjacency = edgs2.owner;
edgs2.owner->adjacency[e] = Sector.owner;

ΕΠΙΣΤΡΟΦΗ; )))))
Σημειώστε ότι ενημερώνουμε τα δεδομένα γειτνίασης για δύο τομείς - τον αναζητούμενο (Κλάδος) και τον γείτονα που βρέθηκε.

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

For(LodsStorage::Lod &lod: lods)(const std::vector §ors = lod.GetSectors();
bool lastLod = lod.GetInd() == lods.GetCount() - 1;

for(Sector *s: sektors)( int32_t tessFacor = s->GetTessFactor(); s->GetBorderTessFactor() = ( GetNeibTessFactor(s, Sector::ADJ_BOTTOM, tessFacor, lastLod), GetF:TessFactor() tessFacor, lastLod), GetNeibTessFactor(s, Sector::ADJ_TOP, tessFacor, lastLod), GetNeibTessFactor(s, Sector::ADJ_RIGHT, tessFacor, lastLod) )< TessFactor) ? (float)neibTessFactor: 0.0f; }
Μια συνάρτηση που αναζητά έναν γειτονικό παράγοντα tessellation:

Float Terrain::GetNeibTessFactor(Sector *Sec, Sector::AdjacencySide Side, int32_t TessFactor, bool IsLastLod) ( Sector *neib = Sec->GetAdjacency(); int32_t neibTessFactor(aibTessFactor =neibTessFactor);

Αν επιστρέψουμε το μηδέν, τότε ο γείτονας από την πλευρά του Side δεν μας ενδιαφέρει. Επιτρέψτε μου να πηδήξω μπροστά και να πω ότι πρέπει να εξαλείψουμε τις ρωγμές από το πλάι του επιπέδου με υψηλό συντελεστή διόγκωσης.
Τώρα ας περάσουμε στο shader. Επιτρέψτε μου να σας υπενθυμίσω ότι πρώτα πρέπει να πάρουμε τις συντεταγμένες του πλέγματος χρησιμοποιώντας τις συντεταγμένες του tessellator. Στη συνέχεια, αυτές οι συντεταγμένες μετατρέπονται σε ένα σημείο στην επιφάνεια του κύβου, αυτό το σημείο κανονικοποιείται - και τώρα έχουμε ένα σημείο στη σφαίρα:

Float3 p = Tri.netPos * Coord.x + Tri.netPos * Coord.y + Tri.netPos * Coord.z; float3 planePos = Tri.startPos + Tri.vec1 * p.x * gridStep.x + Tri.vec2 * p.y * gridStep.y; float3 sphPos = normalize(planePos);
Πρώτα πρέπει να καταλάβουμε αν η κορυφή ανήκει είτε στην πρώτη είτε στην τελευταία γραμμή του πλέγματος, είτε στην πρώτη ή στην τελευταία στήλη - σε αυτήν την περίπτωση, η κορυφή ανήκει σε μια άκρη του τομέα. Αλλά αυτό δεν είναι αρκετό - πρέπει να προσδιορίσουμε εάν η κορυφή ανήκει στο όριο LOD. Για να το κάνουμε αυτό, χρησιμοποιούμε πληροφορίες σχετικά με γειτονικούς τομείς, ή μάλλον τα επίπεδα σύνθεσής τους: Float4 bTf = Tri.borderTessFactor; bool isEdge = (bTf.x != 0.0f && p.y == 0.0f) || //κάτω (bTf.y != 0.0f && p.x == 0.0f) || //αριστερά (bTf.z != 0.0f && p.y == gridSize.y) || //top (bTf.w != 0.0f && p.x == gridSize.x) //δεξιά- ουσιαστικά εξαλείφει τις ρωγμές. Κοιτάξτε το Σχ. 20. Η κόκκινη γραμμή είναι το πρόσωπο μιας κορυφής που ανήκει στο δεύτερο επίπεδο λεπτομέρειας. Οι δύο μπλε γραμμές είναι οι άκρες του τρίτου επιπέδου λεπτομέρειας. Χρειαζόμαστε το V3 να ανήκει στην κόκκινη γραμμή - δηλαδή να ξαπλώνουμε στην άκρη του δεύτερου επιπέδου. Καθώς τα ύψη V1 και V2 είναι ίσα και για τα δύο επίπεδα, το V3 μπορεί να βρεθεί με γραμμική παρεμβολή μεταξύ τους


Εικ. 20 Επίδειξη των όψεων που σχηματίζουν μια ρωγμή με τη μορφή γραμμών

Μέχρι στιγμής δεν έχουμε ούτε V1 και V2, ούτε τον συντελεστή F. Πρώτα πρέπει να βρούμε τον δείκτη του σημείου V3. Δηλαδή, εάν το πλέγμα έχει μέγεθος 32 επί 32 και ο συντελεστής τεμαχισμού είναι τέσσερις, τότε αυτός ο δείκτης θα είναι από μηδέν έως 128 (32 * 4). Έχουμε ήδη συντεταγμένες στο χώρο πλέγματος p - εντός αυτό το παράδειγμαμπορούν να είναι για παράδειγμα (15.5, 16). Για να λάβετε τον δείκτη, πρέπει να πολλαπλασιάσετε μία από τις συντεταγμένες p με τον συντελεστή tessellation. Χρειαζόμαστε επίσης την αρχή του προσώπου και την κατεύθυνση προς το τέλος του - μια από τις γωνίες του τομέα.

Float edgeVertInd = 0,0f; float3 edgeVec = float3(0.0f, 0.0f, 0.0f); float3 startPos = float3(0.0f, 0.0f, 0.0f); uint neibTessFactor = 0; if(bTf.x != 0.0f && p.y == 0.0f)( // κάτω άκροVertInd = p.x * Tri.tessFactor; edgeVec = Tri.vec1; startPos = Tri.startPos; neibTessFactor = (uint)Tri.borderTessFactor. ;)alse if(bTf.y != 0.0f && p.x == 0.0f)( // αριστερό άκροVertInd = p.y * Tri.tessFactor; edgeVec = Tri.vec2; startPos = Tri.startPos; neibTessFactor = (uint)Tri. borderTessFactor.y; )alse if(bTf.z != 0.0f && p.y == gridSize.y)( // top edgeVertInd = p.x * Tri.tessFactor; edgeVec = Tri.vec1; startPos = Tri.startPos + Tri.vec2 * (gridStep.x * gridSize.x); .tessFactor = Tri.vec2 startPos = Tri.startPos + Tri.vec1 * (gridStep.x * gridSize.x);
Στη συνέχεια πρέπει να βρούμε τους δείκτες για τα V1 και V2. Φανταστείτε ότι έχετε τον αριθμό 3. Πρέπει να βρείτε τους δύο πλησιέστερους αριθμούς που είναι πολλαπλάσια του δύο. Για να το κάνετε αυτό, υπολογίζετε το υπόλοιπο όταν διαιρείτε τρία με δύο - είναι ίσο με ένα. Στη συνέχεια αφαιρείτε ή προσθέτετε αυτό το υπόλοιπο στο τρία και έχετε το επιθυμητό αποτέλεσμα. Το ίδιο και με τα ευρετήρια, αλλά αντί για δύο θα έχουμε μια αναλογία συντελεστών tessellation των επιπέδων λεπτομέρειας. Δηλαδή, εάν το τρίτο επίπεδο έχει συντελεστή 16 και το δεύτερο έχει 2, τότε η αναλογία θα είναι ίση με 8. Τώρα, για να λάβετε τα ύψη, πρέπει πρώτα να πάρετε τα αντίστοιχα σημεία στη σφαίρα κανονικοποιώντας τα σημεία στο πρόσωπο. Έχουμε ήδη προετοιμάσει την αρχή και την κατεύθυνση της άκρης - το μόνο που μένει είναι να υπολογίσουμε το μήκος του διανύσματος από το V1 στο V2. Δεδομένου ότι το μήκος της άκρης του αρχικού κελιού πλέγματος είναι gridStep.x, το μήκος που χρειαζόμαστε είναι gridStep.x / Tri.tessFactor. Στη συνέχεια, από τα σημεία της σφαίρας θα πάρουμε το ύψος, όπως περιγράφηκε προηγουμένως.

Float GetNeibHeight(float3 EdgeStartPos, float3 EdgeVec, float Triume, float3 NormOffset) ( float3 neibPos = EdgeStartPos + EdgeVec * VecLen; neibPos = normalize(neibPosPos); return GetHeight(neibFffset,). . tessFactor; uint tessRatio = (uint)tessFactor / (uint)neibTessFactor; uint ind = (uint)edgeVertInd % tessRatio; uint leftNeibInd = (uint)edgeVertInd - ind; float leftNeibHeight = GetNeibHeight(startPos, edgeVec, vertOffset * leftNeibInd, normOffset); uint rightNeibInd = (uint)edgeVertInd + ind; float rightNeibHeight = GetNeibHeight(startPos, edgeVec, vertOffset * rightNeibInd, normOffset);
Λοιπόν, το τελευταίο συστατικό είναι ο παράγοντας F. Το παίρνουμε διαιρώντας το υπόλοιπο της διαίρεσης με τον λόγο των συντελεστών (ind) με το λόγο των συντελεστών (tessRatio)

Συντελεστής float = (float)ind / (float)tessRatio;
Το τελικό στάδιο είναι η γραμμική παρεμβολή των υψών και η απόκτηση μιας νέας κορυφής

Float m.Height = lerp(αριστεράNeibHeight, rightNeibHeight, παράγοντας); posL = sphPos * (Radius σφαίρας + μέσο ύψος);
Μπορεί επίσης να εμφανιστεί μια ρωγμή στο σημείο όπου οι τομείς με συντεταγμένες ακμής ίσες με 1 ή 0 περιθώριο, παίρνω τη μέση τιμή μεταξύ των υψών για τις δύο συντεταγμένες:

Float GetHeight(float2 TexCoords) ( float2 texCoords2 = TexCoords * texCoordsScale; float mHeight = mainHeightTex.SampleLevel(mainHeightTexSampler, TexCoords, 0).x; float dHeight = distHeightTex ).x; επιστροφή (mΎψος + dHeight) * maxTerrainHeight ) float GetHeight(float3 SphPos, float3 NormOffset) (float2 texCoords1 = GetTexCoords(SphPos, NormOffset); ( float2(0.0f, texCoords1.y)); ) ; lerp επιστροφής (ύψος, ύψος2, 0,5f);

11. Επεξεργασία GPU

Ας μεταφέρουμε την επεξεργασία τομέα στη GPU. Θα έχουμε δύο Compute shaders - ο πρώτος θα κάνει clipping κατά μήκος της πυραμίδας της ορατότητας και θα καθορίσει το επίπεδο λεπτομέρειας, ο δεύτερος θα λάβει τους συντελεστές οριακής διάρθρωσης για την εξάλειψη των ρωγμών. Η διαίρεση σε δύο στάδια είναι απαραίτητη γιατί, όπως στην περίπτωση της CPU, δεν μπορούμε να προσδιορίσουμε σωστά τους γείτονες για τομείς μέχρι να εκτελέσουμε το κλάδεμα. Δεδομένου ότι και οι δύο shader θα χρησιμοποιούν δεδομένα LOD και θα δουλεύουν με τομείς, εισήγαγα δύο γενική δομή: Sector and Lod

Struct Sector ( float3 vec1, vec2; float3 startPos; float3 normCenter; int adjacency; float borderTessFactor; int lod; ); struct Lod ( RangeF dotRange; float tessFactor; float padding; float4 color; );
Θα χρησιμοποιήσουμε τρία κύρια buffer - είσοδο (περιέχει αρχικές πληροφορίες για τομείς), ενδιάμεσο (περιλαμβάνει δεδομένα από τομείς που ελήφθησαν ως αποτέλεσμα του πρώτου σταδίου) και τελικό (θα μεταδοθεί για σχεδίαση). Τα δεδομένα της προσωρινής μνήμης εισόδου δεν θα αλλάξουν, επομένως είναι λογικό να χρησιμοποιηθεί η τιμή D3D11_USAGE_IMMUTABLE στο πεδίο Χρήση της δομής D3D11_BUFFER_DESC Απλώς θα γράψουμε τα δεδομένα όλων των τομέων σε αυτό με τη μόνη διαφορά ότι για δεδομένα γειτνίασης θα χρησιμοποιήσουμε δείκτες τομέα. , αντί να τους υποδεικνύει. Για το επίπεδο του δείκτη λεπτομέρειας και των συντελεστών οριακής ανάλυσης, ορίστε τις τιμές στο μηδέν:

Static const size_t τομέαSize = sizeof(Vector3) + //vec1 sizeof(Vector3) + //vec2 sizeof(Point3F) + //normCenter sizeof(Point3F) + //startPos sizeof(Point4) + //adjacency sizeof(Vector4) + //borderTessFactor sizeof(int32_t);//lod size_t sektorsDataSize = sektors.GetSectors().size() * areaSize; std:: vector sektorsData(sectorsDataSize); char* ptr = const Τομέας* firstPtr = §ors.GetSectors(); for(const Sector &sec: sektors)( Utils::AddToStream (ptr, sec.GetVec1()); Utils::AddToStream (ptr, sec.GetVec2()); Utils::AddToStream (ptr, sec.GetStartPos()); (ptr, sec.GetStartPos()); (ptr, sec.GetStartPos()); Utils::AddToStream (ptr, sec.GetNormCenter());
Utils::AddToStream

(ptr, sec.GetAdjacency() - firstPtr);
Utils::AddToStream

(ptr, Vector4()); Utils::AddToStream (ptr, 0); ) inputData = Utils::DirectX::CreateBuffer(§orsData,//Raw data sektorsDataSize,//Μέγεθος buffer D3D11_BIND_SHADER_RESOURCE,//bind flags D3D11_USAGE_IMMUTABLE,//usage_IMMUTABLE,//usage_IMMUTABLE,//usage_IMMUTABLE ED,//mis c σημαίες τομέαΜέγεθος) ; //διασκελισμός byte δομής< dotRange.minVal || dotVal >Τώρα λίγα λόγια για το ενδιάμεσο buffer. Θα παίξει δύο ρόλους - έξοδο για το πρώτο shader και είσοδο για το δεύτερο, οπότε θα καθορίσουμε την τιμή D3D11_BIND_UNORDERED_ACCESS στο πεδίο BindFlags | D3D11_BIND_SHADER_RESOURCE. Θα δημιουργήσουμε επίσης δύο προβολές για αυτό - UnorderedAccessView, που θα επιτρέψει στο shader να γράψει το αποτέλεσμα της δουλειάς του σε αυτό και το ShaderResourceView, με το οποίο θα χρησιμοποιήσουμε το buffer ως είσοδο. Το μέγεθός του θα είναι το ίδιο με το buffer εισόδου που δημιουργήθηκε προηγουμένως< 4; l++){ Lod lod = lods[l]; if(dotVal >UINT miscFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; intermediateData = Utils::DirectX::CreateBuffer(sectors.GetSectors().size() * sectionSize,//Buffer size miscFlags, D3D11_USAGE_DEFAULT,//χρήση 0,//Σημαίες πρόσβασης CPU D3D11_RESOURCE_Size/MIST_mis); /structure byte stride intermediateUAW = Utils::DirectX::CreateUnorderedAccessView(intermediateData, D3D11_BUFFER_UAV(0, sektors.GetSectors().size(), 0)); intermediateSRV = Utils::DirectX::CreateShaderResourceView(intermediateData, D3D11_BUFFEREX_SRV(0, sektors.GetSectors().size(), 0));<= lod.dotRange.maxVal) sector.lod = l + 1; } outputData = sector; }
Αφού υπολογίσουμε το γινόμενο κουκίδων, ελέγχουμε αν ο τομέας βρίσκεται στην δυνητικά ορατή περιοχή. Στη συνέχεια, διευκρινίζουμε το γεγονός της ορατότητάς του χρησιμοποιώντας την κλήση IsVisible(), η οποία είναι πανομοιότυπη με την κλήση Frustum::TestSphere() που παρουσιάστηκε νωρίτερα. Η λειτουργία της συνάρτησης εξαρτάται από τις μεταβλητές worldView, sphereRadius, frustumPlanesPosV και frustumPlanesNormalsV, οι τιμές των οποίων πρέπει να μεταβιβαστούν εκ των προτέρων στο shader. Στη συνέχεια ορίζουμε το επίπεδο λεπτομέρειας. Λάβετε υπόψη ότι υποδεικνύουμε τον δείκτη επιπέδου ξεκινώντας από ένα - αυτό είναι απαραίτητο για να απορρίψουμε στο δεύτερο στάδιο εκείνους τους τομείς των οποίων το επίπεδο λεπτομέρειας είναι ίσο με μηδέν.

Τώρα πρέπει να προετοιμάσουμε τα buffer για το δεύτερο στάδιο. Θέλουμε να χρησιμοποιήσουμε την προσωρινή μνήμη ως έξοδο για τον υπολογισμό σκίασης και μια είσοδο για το tessellator - για αυτό πρέπει να καθορίσουμε την τιμή D3D11_BIND_UNORDERED_ACCESS στο πεδίο BindFlags | D3D11_BIND_VERTEX_BUFFER. Θα πρέπει να εργαστούμε απευθείας με τα δεδομένα της προσωρινής μνήμης, επομένως θα υποδείξουμε την τιμή D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS στο πεδίο MiscFlags Για να εμφανίσουμε ένα τέτοιο buffer, θα χρησιμοποιήσουμε την τιμή DXGI_FORMAT_R32_TYPELESS στο πεδίο Flags και στο πεδίο NumeElements. buffer διαιρούμενο με τέσσερα

Size_t instancesByteSize = instanceByteSize * sektors.GetSectors().size(); outputData = Utils::DirectX::CreateBuffer(instancesByteSize, D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DEFAULT, 0, D3D11_RESOURCE_VIEW_ALLSOW); D3D11_BUFFER_UAV uavParams = (0, instancesByteSize / 4, D3D11_BUFFER_UAV_FLAG_RAW); outputUAW = Utils::DirectX::CreateUnorderedAccessView(outputData, uavParams, DXGI_FORMAT_R32_TYPELESS);
Χρειαζόμαστε και έναν πάγκο. Με τη βοήθειά του, απευθυνόμαστε στη μνήμη στο shader και χρησιμοποιούμε την τελική της τιμή στο όρισμα instanceCount της κλήσης DrawIndexedInstanced(). Εφάρμοσα τον μετρητή ως buffer 16 byte. Επίσης, κατά τη δημιουργία μιας αντιστοίχισης στο πεδίο Σημαίες του πεδίου D3D11_BUFFER_UAV, χρησιμοποίησα την τιμή D3D11_BUFFER_UAV_FLAG_COUNTER

Counter = Utils::DirectX::CreateBuffer(sizeof(UINT), D3D11_BIND_UNORDERED_ACCESS, D3D11_USAGE_DEFAULT, 0, D3D11_RESOURCE_MISC_BUFFER_STRUCTURED, 4); D3D11_BUFFER_UAV uavParams = (0, 1, D3D11_BUFFER_UAV_FLAG_COUNTER); counterUAW = Utils::DirectX::CreateUnorderedAccessView(counter, uavParams);
Ήρθε η ώρα να δώσετε τον κωδικό για το δεύτερο shader

(ptr, Vector4()); inputData: register(t0); RWByteAddressBuffer OutputData: register(u0); RWStructuredBuffer μετρητής: register(u1);
void Process(int3 TId: SV_DispatchThreadID) ( int ind = TId.x; Sector sektor = inputData; if(sector.lod != 0)( sektor.borderTessFactor = GetNeibTessFactor(sector, 0); //Bottom sektori. (τομέας, 1). (c * dataSize + 0, asuint(sector.startPos.x)); .startPos.z)); (c * dataSize + 20, asuint(sector.vec1.z) outputData.Store(c * dataSize + 24, asuint(sector.vec2.x) (c * dataSize + 28 , asuint(sector .vec2.y));

outputData.Store(c * dataSize + 40, asuint(sector.borderTessFactor));
outputData.Store(c * dataSize + 44, asuint(sector.borderTessFactor)); outputData.Store(c * dataSize + 48, asuint(sector.borderTessFactor));Για τις μεθόδους DrawIndexedInstancedIndirect() και CopyStructureCount(), ανατρέξτε στο Παράρτημα 2

12. Κάμερα

Σίγουρα γνωρίζετε πώς να φτιάξετε ένα μοντέλο μιας απλής κάμερας FPS (First Person Shooter). Ακολουθώ αυτό το σενάριο:
  • 1. Από δύο γωνίες παίρνω ένα διάνυσμα κατεύθυνσης
  • 2. χρησιμοποιώντας το διάνυσμα κατεύθυνσης και το διάνυσμα (0, 1, 0) παίρνω τη βάση
  • 3. Σύμφωνα με το διάνυσμα κατεύθυνσης και το vacctor προς τα δεξιά, που ελήφθησαν στο βήμα 2, αλλάζω τη θέση της κάμερας

Στην περίπτωσή μας, η κατάσταση είναι κάπως πιο περίπλοκη - πρώτον, πρέπει να κινηθούμε σε σχέση με το κέντρο του πλανήτη και δεύτερον, όταν κατασκευάζουμε μια βάση, αντί για το διάνυσμα (0, 1, 0), πρέπει να χρησιμοποιήσουμε το κανονικό του η σφαίρα στο σημείο που βρισκόμαστε τώρα. Προκειμένου να επιτευχθεί επιθυμητά αποτελέσματα, θα χρησιμοποιήσω δύο βάσεις. Σύμφωνα με την πρώτη, η θέση θα αλλάξει, η δεύτερη θα περιγράφει τον προσανατολισμό της κάμερας. Οι βάσεις είναι αλληλοεξαρτώμενες, αλλά υπολογίζω πρώτα τη βάση της θέσης, οπότε θα ξεκινήσω με αυτό. Ας υποθέσουμε ότι έχουμε μια βάση αρχικής θέσης (pDir, pUp, pRight) και ένα διάνυσμα κατεύθυνσης vDir κατά μήκος του οποίου θέλουμε να κινηθούμε σε κάποια απόσταση. Πρώτα απ 'όλα, πρέπει να υπολογίσουμε τις προβολές του vDir σε pDir και pRight. Προσθέτοντας τα, παίρνουμε ένα ενημερωμένο διάνυσμα κατεύθυνσης (Εικ. 21).


Εικ. 21 Οπτική διαδικασία λήψης projDir

Όπου P είναι η θέση της κάμερας, τα mF και mS είναι συντελεστές, δηλαδή πόσο πρέπει να κινηθούμε προς τα εμπρός ή προς τα πλάγια.

Δεν μπορούμε να χρησιμοποιήσουμε το PN ως νέα θέση κάμερας επειδή το PN δεν ανήκει στη σφαίρα. Αντίθετα, βρίσκουμε την κανονική της σφαίρας στο σημείο PN, και αυτή η κανονική θα είναι η νέα τιμή του ανοδικού διανύσματος. Τώρα μπορούμε να σχηματίσουμε μια ενημερωμένη βάση

Vector3 nUp = Vector3::Normalize(PN - spherePos); Διάνυσμα3 nDir = projDir Διάνυσμα3 nΔεξιά = Διάνυσμα3::Normalize(Vector3::Cross(pUp, pDir))
όπου το spherePos είναι το κέντρο της σφαίρας.

Πρέπει να βεβαιωθούμε ότι καθένα από τα διανύσματά του είναι ορθογώνιο ως προς τα άλλα δύο. Σύμφωνα με την ιδιότητα cross product, το nRight ικανοποιεί αυτήν την προϋπόθεση. Απομένει να πετύχουμε το ίδιο για nUp και nDir. Για να το κάνετε αυτό, προβάλετε το nDir στο nUp και αφαιρέστε το διάνυσμα που προκύπτει από το nDir (Εικ. 22)


Εικ.22 Ορθογωνοποίηση του nDir ως προς το nUp

Θα μπορούσαμε να κάνουμε το ίδιο με το nUp, αλλά μετά θα άλλαζε κατεύθυνση, κάτι που στην περίπτωσή μας είναι απαράδεκτο. Τώρα κανονικοποιούμε το nDir και λαμβάνουμε μια ενημερωμένη βάση ορθοκανονικής κατεύθυνσης.

Το δεύτερο βασικό στάδιο είναι η κατασκευή μιας βάσης προσανατολισμού. Η κύρια δυσκολία είναι η απόκτηση του διανύσματος κατεύθυνσης. η καταλληλότερη λύση είναι να μετατρέψουμε ένα σημείο με πολική γωνία a, γωνία αζιμουθίου b και απόσταση από την αρχή ίση με ένα από σφαιρικές συντεταγμένες σε καρτεσιανή. Μόνο αν πραγματοποιήσουμε μια τέτοια μετάβαση για ένα σημείο με πολική γωνία ίση με μηδέν, θα έχουμε ένα διάνυσμα που κοιτάζει προς τα πάνω. Αυτό δεν είναι απολύτως κατάλληλο για εμάς, καθώς θα αυξάνουμε τις γωνίες και υποθέτοντας ότι ένα τέτοιο διάνυσμα θα κοιτάζει προς τα εμπρός. Η απλή μετατόπιση της γωνίας κατά 90 μοίρες θα λύσει το πρόβλημα, αλλά είναι πιο κομψό να χρησιμοποιήσετε τον κανόνα μετατόπισης γωνίας, ο οποίος δηλώνει ότι

Ας το κάνουμε. Ως αποτέλεσμα, παίρνουμε τα εξής

Όπου a είναι η πολική γωνία, b είναι η γωνία αζιμουθίου.

Αυτό το αποτέλεσμα δεν είναι απολύτως κατάλληλο για εμάς - πρέπει να κατασκευάσουμε ένα διάνυσμα κατεύθυνσης σε σχέση με τη βάση της θέσης. Ας ξαναγράψουμε την εξίσωση για το vDir:

Όλα είναι σαν τους αστροναύτες - τόσο προς αυτήν την κατεύθυνση, τόσο προς αυτήν την κατεύθυνση. Θα πρέπει τώρα να είναι προφανές ότι αν αντικαταστήσουμε τα τυπικά διανύσματα βάσης με pDir, pUp και pRight, θα πάρουμε την κατεύθυνση που χρειαζόμαστε. Σαν αυτό

Μπορείτε να αναπαραστήσετε το ίδιο πράγμα με τη μορφή πολλαπλασιασμού πίνακα

Το διάνυσμα vUp θα είναι αρχικά ίσο με pUp. Υπολογίζοντας το διασταυρούμενο γινόμενο των vUp και vDir, παίρνουμε vRight

Τώρα θα βεβαιωθούμε ότι το vUp είναι ορθογώνιο με τα υπόλοιπα διανύσματα βάσης. Η αρχή είναι η ίδια όπως όταν εργάζεστε με το nDir

Τακτοποιήσαμε τα βασικά - το μόνο που μένει είναι να υπολογίσουμε τη θέση της κάμερας. Γίνεται έτσι

Όπου το spherePos είναι το κέντρο της σφαίρας, το sphereRadius είναι η ακτίνα της σφαίρας και το ύψος είναι το ύψος πάνω από την επιφάνεια της σφαίρας. Εδώ είναι ο κωδικός λειτουργίας για την κάμερα που περιγράφεται:

Float moveFactor = 0,0f, sideFactor = 0,0f, heightFactor = 0,0f; DirectInput::GetInsance()->ProcessKeyboardDown(( (DIK_W, [&]())(moveFactor = 1.0f;)), (DIK_S, [&]())(moveFactor = -1.0f;)), (DIK_D , [ &]())(sideFactor = 1.0f;)), (DIK_A, [&]())(sideFactor = -1.0f;)), (DIK_Q, [&]())(heightFactor = 1.0f; )), (DIK_E , [&]())(heightFactor = -1.0f;)) )); if(moveFactor != 0.0f || sideFactor != 0.0f)( Vector3 newDir = Vector3::Normalize(pDir * Vector3::Dot(pDir, vDir) + pRight * Vector3::Dot(pRight, vDir)); Point3F newPos = pos + (newDir * moveFactor + pRight * sideFactor) * Tf * pUp = Vector3::Normalize(newPos - spherePos ) ; pDir)); sphereRadius + height ) DirectInput::MouseState mState = DirectInput::GetInsance()->GetMouseDelta( ); if(mState.x != 0 || mState.y != 0 || moveFactor != 0.0f || sideFactor != 0.0f)( if(mState.x != 0) angles.x = angles.x + mState .x / 80.0f; if(mState.y != 0) angles.y = Math::Saturate(angles.y + mState.y / 80.0f, RangeF(-Pi * 0.499f, Pi * 0.499f)); vDir = Vector3::Normalize(pΔεξιά * sinf(γωνίες.x) * cosf(γωνίες.y) + pUp * -sinf(γωνίες.y) + pDir * cosf(γωνίες.x) * cosf(γωνίες.y)); vUp = pUp = Vector3::Normalize(Vector3::Cross(vUp, vDir)); ((vRight, 0.0f), (vUp, 0.0f), (vDir, 0.0f), (pos, 1.0f)));
Σημειώστε ότι επαναφέρουμε τις γωνίες.x αφού ενημερώσουμε τη βάση θέσης. Αυτό είναι κρίσιμο. Ας φανταστούμε ότι αλλάζουμε ταυτόχρονα τη γωνία θέασης και κινούμαστε γύρω από τη σφαίρα. Πρώτα θα προβάλουμε το διάνυσμα κατεύθυνσης σε pDir και pRight, θα λάβουμε τη μετατόπιση (newPos) και θα ενημερώσουμε τη βάση θέσης με βάση αυτήν. Η δεύτερη συνθήκη θα λειτουργήσει επίσης και θα αρχίσουμε να ενημερώνουμε τη βάση προσανατολισμού. Αλλά επειδή το pDir και το pRight έχουν ήδη αλλάξει ανάλογα με το vDir, χωρίς επαναφορά της γωνίας αζιμουθίου (γωνίες.x) η περιστροφή θα είναι πιο "απότομη"

συμπέρασμα

Ευχαριστώ τον αναγνώστη για το ενδιαφέρον σας για το άρθρο. Ελπίζω ότι οι πληροφορίες που περιέχονται σε αυτό ήταν προσβάσιμες, ενδιαφέρουσες και χρήσιμες για αυτόν. Προτάσεις και σχόλια μπορούν να μου σταλούν μέσω ταχυδρομείου. [email προστατευμένο]ή αφήστε ως σχόλια.

Σου εύχομαι επιτυχία!

Παράρτημα 1

το πεδίο InstanceDataStepRate περιέχει πληροφορίες σχετικά με το πόσες φορές πρέπει να αντληθούν τα δεδομένα D3D11_INPUT_PER_VERTEX_DATA για ένα στοιχείο D3D11_INPUT_PER_INSTANCE_DATA. Στο παράδειγμά μας, όλα είναι απλά - ένα προς ένα. «Αλλά γιατί χρειάζεται να σχεδιάσουμε το ίδιο πράγμα πολλές φορές;» - εσύ ρωτάς. Λογική ερώτηση. Το αρμενικό ραδιόφωνο απαντά - ας υποθέσουμε ότι έχουμε 99 μπάλες τριών διαφορετικών χρωμάτων. Μπορούμε να περιγράψουμε την κορυφή ως εξής:

UINT ColorRate = 99 / 3; std:: vector meta = (("POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0), ("NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT 3_D1_2 0), ( "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0), ("WORLD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1), ("WORLD_32F AT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1), ("WORLD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D11_INPUT_PER_INSTANCE_DATA, 1), ("WORLD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, DAINT, 1, PUT_1 COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 2, 0, D3D11_INPUT_PER_INSTANCE_DATA, ColorRate), );
Λάβετε υπόψη ότι η κορυφή συλλέγεται από τρεις πηγές και τα δεδομένα της τελευταίας ενημερώνονται μία φορά κάθε 33 «περιπτώσεις». Ως αποτέλεσμα, θα λάβουμε 33 περιπτώσεις του πρώτου χρώματος, άλλες 33 του δεύτερου, κ.λπ. Τώρα ας δημιουργήσουμε τα buffer. Επιπλέον, καθώς τα χρώματα δεν αλλάζουν, μπορούμε να δημιουργήσουμε ένα buffer με χρώματα με τη σημαία D3D11_USAGE_IMMUTABLE. Αυτό σημαίνει ότι μετά την προετοιμασία του buffer, μόνο η GPU θα έχει πρόσβαση μόνο για ανάγνωση στα δεδομένα της. Εδώ είναι ο κώδικας για τη δημιουργία των buffer:

MatricesTb = Utils::DirectX::CreateBuffer(sizeof(Matrix4x4) * 99, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); χρώματαTb = Utils::DirectX::CreateBuffer(colors, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE, 0);
στη συνέχεια, εάν είναι απαραίτητο, ενημερώνουμε το buffer με πίνακες (χρησιμοποιώ τις λειτουργίες της βιβλιοθήκης μου - ελπίζω ότι όλα θα είναι ξεκάθαρα)

Utils::DirectX::Χάρτης (matricesTb, [&](Matrix4x4 *Data) ( //πρώτα γράφουμε δεδομένα για μπάλες του πρώτου χρώματος στο buffer //μετά για το δεύτερο, κ.λπ. Σημειώστε ότι η απαραίτητη αντιστοιχία //δεδομένων στα χρώματα πρέπει να είναι εξασφαλιστεί στο στάδιο της δημιουργίας των δεδομένων buffer )).
Η πρόσβαση δεδομένων στο shader μπορεί να υλοποιηθεί με τον ίδιο τρόπο που περιέγραψα προηγουμένως


Παράρτημα 2

Σε αντίθεση με την DrawIndexedInstanced(), η κλήση της DrawIndexedInstancedIndirect() λαμβάνει ως όρισμα ένα buffer που περιέχει όλες τις πληροφορίες που χρησιμοποιείτε για να καλέσετε την DrawIndexedInstanced(). Επιπλέον, αυτό το buffer πρέπει να δημιουργηθεί με τη σημαία D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS. Ακολουθεί ένα παράδειγμα δημιουργίας buffer:

//indicesCnt - ο αριθμός των ευρετηρίων που θέλουμε να εμφανίσουμε //instancesCnt - ο αριθμός των "instances" που θέλουμε να εμφανίσουμε std::vector args = ( indicesCnt, //IndexCountPerInstance instancesCnt, //InstanceCount 0, //StartIndexLocation 0,//BaseVertexLocation 0//StartInstanceLocation ); D3D11_BUFFER_DESC bd = (); bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof(UINT) * args.size(); bd.BindFlags = 0; bd.CPUAccessFlags = 0; bd.MiscFlags = D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; bd.StructureByteStride = 0; ID3D11Buffer* buffer; D3D11_SUBRESOURCE_DATA initData = (); initData.pSysMem = HR(DeviceKeeper::GetDevice()->CreateBuffer(&bd, &initData, &buffer)); παράδειγμα κλήσης DrawIndexedInstancedIndirect(): DeviceKeeper::GetDeviceContext()->DrawIndexedInstancedIndirect(indirectArgs, 0);
Ως δεύτερο όρισμα περνάμε το offset σε byte από την αρχή του buffer από το οποίο πρέπει να ξεκινήσουμε την ανάγνωση δεδομένων. Πώς μπορεί να χρησιμοποιηθεί αυτό; Για παράδειγμα, κατά την εφαρμογή αποκοπής αόρατης γεωμετρίας στη GPU. Σε γενικές γραμμές, η χρονολογία είναι η εξής - πρώτα, στον shader Υπολογισμός, γεμίζουμε το AppendStructuredBuffer, το οποίο περιέχει ορατά δεδομένα γεωμετρίας. Στη συνέχεια, χρησιμοποιώντας την CopyStructureCount() ορίζουμε τον αριθμό των παρουσιών που θέλουμε να εμφανίσουμε στον αριθμό των καταχωρήσεων σε αυτό το buffer και καλούμε την DrawIndexedInstancedIndirect()


Παράρτημα 3

Ας υποθέσουμε ότι η τιμή της συντεταγμένης x είναι ίση με το αποτέλεσμα της συνάρτησης X με το όρισμα a, και η τιμή της συντεταγμένης z είναι το αποτέλεσμα της συνάρτησης Z με το ίδιο όρισμα:

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

Το οποίο τελικά μας δίνει το ίδιο αποτέλεσμα:

Γιατί μπορούμε να χρησιμοποιήσουμε τιμές ταχύτητας ως συστατικά ενός διανύσματος κατεύθυνσης; Έτσι το καταλαβαίνω. Φανταστείτε ότι έχουμε μια διανυσματική συνάρτηση (για t >= 0):

Ας υπολογίσουμε την παράγωγο για τη συντεταγμένη Χ

Τώρα για τον Υ

Βρήκαμε ότι το διάνυσμα της ταχύτητας είναι ίσο με (2, 3), τώρα ας βρούμε το σημείο εκκίνησης

Ως αποτέλεσμα, μπορούμε να εκφράσουμε τη συνάρτηση P(t) ως εξής:

Το οποίο με απλά λόγια μπορεί να περιγραφεί ως «το σημείο κινείται από την αρχή με συντεταγμένες (3, 2) στο t προς την κατεύθυνση (2, 3).» Ας πάρουμε τώρα ένα άλλο παράδειγμα:

Ας υπολογίσουμε ξανά την παράγωγο για τη συντεταγμένη Χ

Και για τη συντεταγμένη Υ

Τώρα το διάνυσμα της ταχύτητας αλλάζει ανάλογα με το όρισμα. Σε αυτή την περίπτωση, με απλά λόγια η κατάσταση μπορεί να περιγραφεί ως εξής: «Το σημείο μετακινείται από την αρχή με συντεταγμένες (3, 2) και η κατεύθυνση της κίνησής του αλλάζει συνεχώς».


Παράρτημα 4

Ας ορίσουμε μια συνάρτηση F(H) που θα πάρει το ύψος στην περιοχή και θα επιστρέψει μια τιμή μεταξύ 0 και 1, όπου F(Hmin) = 0 και F(Hmax) = 1. Επίλυση του συστήματος εξισώσεων

Εχω

Ως αποτέλεσμα, η συνάρτηση F παίρνει τη μορφή

Τώρα χρειαζόμαστε μια συνάρτηση που παίρνει έναν συντελεστή ύψους μεταξύ 0 και 1 και επιστρέφει την ελάχιστη τιμή για το γινόμενο κουκίδων. Πρέπει να λάβουμε υπόψη ότι όσο πιο κοντά βρίσκεται ο παρατηρητής στην επιφάνεια, τόσο μεγαλύτερη είναι. Αυτό έχει ως αποτέλεσμα την ακόλουθη εξίσωση:

Ας ανοίξουμε τις αγκύλες

Ας απλοποιήσουμε και ας πάρουμε

Τώρα εκφράζουμε D(F(H)) και παίρνουμε

Ετικέτες:

  • directx11
  • απόδοση εδάφους
Προσθέστε ετικέτες

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

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

Ωστόσο, κατά την εξέλιξη του γεωγραφικού περιβλήματος, οι αλλαγές στα χερσαία του συστήματα συνδέθηκαν επίσης με βαθιές διεργασίες και φαινόμενα, εν μέρει εκφρασμένα στην επιφάνεια (ζώνες ηφαιστειότητας, σεισμικότητας, ορεινή δόμηση κ.λπ.). Ταυτόχρονα, μαζί με άμεσες αλλαγές στη λιθογενή βάση των τοπίων και στο γεωγραφικό περίβλημα συνολικά, το τελευταίο έλαβε πρόσθετη ύλη και ενέργεια, η οποία αντικατοπτρίστηκε στη λειτουργία των επιμέρους στοιχείων του και του συστήματος συνολικά. Αυτή η «συμπληρωματικότητα» (κάποιες φορές, πιθανώς σημαντική) εκδηλώθηκε όχι μόνο ποσοτικά, στην παγκόσμια κυκλοφορία ύλης και ενέργειας, αλλά και σε ποιοτικές αλλαγές σε επιμέρους συστατικά. Ο ρόλος των διαδικασιών απαέρωσης της Γης και η ανταλλαγή ενέργειας-μάζας τους με την ατμόσφαιρα και την υδρόσφαιρα δεν έχει ακόμη μελετηθεί επαρκώς. Μόνο από τα μέσα του 20ου αιώνα. εμφανίστηκαν πληροφορίες σχετικά με την υλική σύνθεση της ύλης του μανδύα και τα ποσοτικά χαρακτηριστικά της.

Η έρευνα του V.I Bgatov έχει αποδείξει ότι το ατμοσφαιρικό οξυγόνο δεν είναι τόσο φωτοσυνθετικής προέλευσης όσο βαθιάς προέλευσης. Το γενικά αποδεκτό σχήμα του κύκλου του άνθρακα στη φύση πρέπει να διορθωθεί με την παροχή των ενώσεων του από τα έγκατα της γης, ιδιαίτερα κατά τη διάρκεια ηφαιστειακών εκρήξεων. Προφανώς, δεν εισέρχονται μικρότερες ποσότητες της ουσίας στο υδάτινο κέλυφος κατά τη διάρκεια υποβρύχιων εκρήξεων, ειδικά σε ζώνες εξάπλωσης, ηφαιστειακά τόξα νησιών και σε μεμονωμένα θερμά σημεία. Η συνολική ετήσια ποσότητα ενώσεων άνθρακα που προέρχονται από το υπέδαφος στον ωκεανό και την ατμόσφαιρα είναι συγκρίσιμη με τη μάζα του ετήσιου σχηματισμού ανθρακικών σε υδάτινα σώματα και, προφανώς, υπερβαίνει τον όγκο συσσώρευσης οργανικού άνθρακα από τα φυτά της ξηράς.

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

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

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

Μια αλλαγή στην ενέργεια του γεωγραφικού κελύφους ή των τμημάτων του προκαλεί αναδιάρθρωση της εσωτερικής δομής και των διαδικασιών λειτουργίας του γεωσυστήματος και των σχετικών φαινομένων. Αυτή η διαδικασία είναι πολύπλοκη και ρυθμίζεται από πολλαπλές απευθείας συνδέσεις και συνδέσεις ανάδρασης (Εικ. 9.4). Οι ανθρωπογενείς επιπτώσεις στο γεωγραφικό περιβάλλον προκαλούν αλλαγές στη σύνθεση και την κατάσταση του περιβάλλοντος, διαταράσσουν την ποσοτική και ποιοτική σύνθεση της ζωντανής ύλης (μέχρι μεταλλάξεις) και τροποποιούν τα υπάρχοντα συστήματα ανταλλαγής ενέργειας, μάζας και υγρασίας. Ωστόσο, επί του παρόντος διαθέσιμα στοιχεία υποδηλώνουν ότι οι ανθρωπογενείς αλλαγές δεν αντικατοπτρίζονται θεμελιωδώς στο γεωγραφικό περίβλημα. Η σχετική ισορροπία της ύπαρξής του και η βιωσιμότητα της ανάπτυξης διασφαλίζονται κυρίως από φυσικά αίτια, η κλίμακα των οποίων υπερβαίνει την ανθρώπινη επιρροή. Από αυτό δεν προκύπτει ότι το ίδιο το γεωγραφικό περίβλημα θα υπερνικά πάντα την αυξανόμενη ανθρωπογενή πίεση. Οι παρεμβάσεις στη φύση πρέπει να ρυθμίζονται από την άποψη της σκοπιμότητας των εκδηλώσεών τους - προς όφελος της ανθρωπότητας και χωρίς σημαντική βλάβη στο φυσικό περιβάλλον. Οι έννοιες που αναπτύσσονται προς αυτή την κατεύθυνση ονομάζονται βιώσιμη (ισορροπημένη) ανάπτυξη. Θα πρέπει να βασίζονται σε γενικά γεωλογικά πρότυπα και χαρακτηριστικά της τρέχουσας κατάστασης και ανάπτυξης του γεωγραφικού περιβλήματος.

Εν κατακλείδι, ας αγγίξουμε τη διαφαινόμενη δήλωση που γίνεται το σύγχρονο γεωγραφικό περίβλημα ανθρωπόσφαιρα,ή μέρος της αναδυόμενης νοοσφαίρα.Σημειώστε ότι η έννοια της «νοόσφαιρας» είναι σε μεγάλο βαθμό φιλοσοφικής φύσης. Η ανθρώπινη επίδραση στο περιβάλλον και η εμπλοκή των απορριμμάτων σε αυτό είναι ένα αναμφισβήτητο φαινόμενο. Είναι σημαντικό να καταλάβουμε ότι πιο συχνά ένα άτομο αλλάζει τον βιότοπό του όχι συνειδητά, αλλά μέσω απρόβλεπτων συνεπειών. Επιπλέον, αυτές οι υλοποιήσεις δεν στοχεύουν σε όλα τα στοιχεία του γεωγραφικού περιβλήματος, αλλά μόνο στα συστατικά που είναι απαραίτητα για τους ανθρώπους (δάσος, έδαφος, πρώτες ύλες κ.λπ.). Έτσι, υπάρχουν μόνο θύλακες αλλαγής, αν και μερικές φορές πολύ σημαντικές και σοβαρές, και παρόλο που η ανθρώπινη δραστηριότητα αυξάνεται, η φύση εξακολουθεί να αναπτύσσεται κυρίως υπό την επίδραση φυσικών διεργασιών. Ως εκ τούτου, επί του παρόντος θα πρέπει να μιλάμε για ορισμένες περιοχές του γεωγραφικού περιβλήματος, όπου το φυσικό περιβάλλον έχει αλλάξει σημαντικά και αναπτύσσεται υπό την επίδραση ανθρωπο-ρυθμιζόμενων διαδικασιών.

Ρύζι. 9.4. Μερικά σχόλια που διέπουν το παγκόσμιο κλίμα

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

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

Ωστόσο, κατά την εξέλιξη του γεωγραφικού περιβλήματος, οι αλλαγές στα χερσαία του συστήματα συνδέθηκαν επίσης με βαθιές διεργασίες και φαινόμενα, εν μέρει εκφρασμένα στην επιφάνεια (ζώνες ηφαιστειότητας, σεισμικότητας, ορεινή δόμηση κ.λπ.). Ταυτόχρονα, μαζί με άμεσες αλλαγές στη λιθογενή βάση των τοπίων και στο γεωγραφικό περίβλημα συνολικά, το τελευταίο έλαβε πρόσθετη ύλη και ενέργεια, η οποία αντικατοπτρίστηκε στη λειτουργία των επιμέρους στοιχείων του και του συστήματος συνολικά. Αυτή η «συμπληρωματικότητα» (κάποιες φορές, πιθανώς σημαντική) εκδηλώθηκε όχι μόνο ποσοτικά, στην παγκόσμια κυκλοφορία ύλης και ενέργειας, αλλά και σε ποιοτικές αλλαγές σε επιμέρους συστατικά. Ο ρόλος των διαδικασιών απαέρωσης της Γης και η ανταλλαγή ενέργειας-μάζας τους με την ατμόσφαιρα και την υδρόσφαιρα δεν έχει ακόμη μελετηθεί επαρκώς. Μόνο από τα μέσα του 20ου αιώνα. εμφανίστηκαν πληροφορίες σχετικά με την υλική σύνθεση της ύλης του μανδύα και τα ποσοτικά χαρακτηριστικά της.

Η έρευνα του V.I Bgatov έχει αποδείξει ότι το ατμοσφαιρικό οξυγόνο δεν είναι τόσο φωτοσυνθετικής προέλευσης όσο βαθιάς προέλευσης. Το γενικά αποδεκτό σχήμα του κύκλου του άνθρακα στη φύση πρέπει να διορθωθεί με την παροχή των ενώσεων του από τα έγκατα της γης, ιδιαίτερα κατά τη διάρκεια ηφαιστειακών εκρήξεων. Προφανώς, δεν εισέρχονται μικρότερες ποσότητες της ουσίας στο υδάτινο κέλυφος κατά τη διάρκεια υποβρύχιων εκρήξεων, ειδικά σε ζώνες εξάπλωσης, ηφαιστειακά τόξα νησιών και σε μεμονωμένα θερμά σημεία. Η συνολική ετήσια ποσότητα ενώσεων άνθρακα που προέρχονται από το υπέδαφος στον ωκεανό και την ατμόσφαιρα είναι συγκρίσιμη με τη μάζα του ετήσιου σχηματισμού ανθρακικών σε υδάτινα σώματα και, προφανώς, υπερβαίνει τον όγκο συσσώρευσης οργανικού άνθρακα από τα φυτά της ξηράς.

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

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

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

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

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

4) Η δημιουργία υδροηλεκτρικών δομών σε ποτάμια, φράγματα με το σχηματισμό καταρράξεων πτωτικών υδάτων όλο το χρόνο αλλάζουν το ετήσιο καθεστώς των ποταμών, διαταράσσουν την κατάσταση του πάγου, την κατανομή των μεταφερόμενων ιζημάτων και μεταμορφώνουν το σύστημα ποταμού-ατμόσφαιρας. Οι μη παγωμένες δεξαμενές με συνεχή ομίχλη και εξάτμιση από την επιφάνεια του νερού (ακόμα και το χειμώνα) επηρεάζουν την πορεία των θερμοκρασιών, την κυκλοφορία των υδάτινων μαζών, την επιδείνωση των καιρικών συνθηκών και την αλλαγή του οικοτόπου των ζωντανών οργανισμών. Επίδραση του υδροηλεκτρικού σταθμού σε μεγάλα ποτάμια(Γενισέι, Ανγκάρα, Κολύμα, Βόλγα, κ.λπ.) γίνεται αισθητή δεκάδες χιλιόμετρα κατάντη και σε όλα τα φράγματα των ταμιευτήρων, και οι γενικές αλλαγές στο κλίμα καλύπτουν εκατοντάδες τετραγωνικά χιλιόμετρα. Η αργή παροχή ιζημάτων ποταμών και η ανακατανομή τους οδηγεί σε διακοπή των γεωμορφολογικών διεργασιών και καταστροφή των εκβολών και των όχθες των ποταμών πισίνες νερού(για παράδειγμα, η καταστροφή του Δέλτα του Νείλου και του νοτιοανατολικού τμήματος της ακτής της Μεσογείου μετά την κατασκευή του φράγματος του Ασουάν και η αναχαίτιση σημαντικού μέρους του στερεού ιζήματος που μεταφέρει ο ποταμός).

5) Οι εργασίες αποκατάστασης, που συνοδεύονται από αποστράγγιση μεγάλων χώρων, διαταράσσουν το υπάρχον καθεστώς θερμότητας, υγρασίας και ανταλλαγής και συμβάλλουν στην ανάπτυξη βρόχων αρνητικής ανάδρασης κατά τη μεταμόρφωση των τοπίων. Έτσι, η υπερβολική ξήρανση των ελωδών συστημάτων σε ορισμένες περιοχές (Polesie, Novgorod, περιοχή Irtysh) οδήγησε στον θάνατο της φυσικής βλάστησης και στην εμφάνιση διεργασιών ξεφουσκώματος, οι οποίες ακόμη και σε περιοχές με επαρκή υγρασία σχημάτιζαν μεταβαλλόμενη άμμο. Ως αποτέλεσμα, η σκόνη της ατμόσφαιρας αυξήθηκε, η τραχύτητα της επιφάνειας αυξήθηκε και το καθεστώς ανέμου άλλαξε.

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

7) Διάφοροι ρύποι που εισέρχονται σε τεράστιες ποσότητες σε όλα τα φυσικά περιβάλλοντα αλλάζουν, πρώτα απ 'όλα, την υλική σύνθεση και τις ενεργειακές ικανότητες του αέρα, του νερού, των επιφανειακών σχηματισμών κ.λπ. καθώς και διάφορες αλληλεπιδράσεις με το περιβάλλον περιβάλλον και άλλους φυσικούς παράγοντες.

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

Μια αλλαγή στην ενέργεια του γεωγραφικού κελύφους ή των τμημάτων του προκαλεί αναδιάρθρωση της εσωτερικής δομής και των διαδικασιών λειτουργίας του γεωσυστήματος και των σχετικών φαινομένων. Αυτή η διαδικασία είναι πολύπλοκη και ρυθμίζεται από πολλαπλές απευθείας συνδέσεις και συνδέσεις ανάδρασης (Εικ. 9.4). Οι ανθρωπογενείς επιπτώσεις στο γεωγραφικό περιβάλλον προκαλούν αλλαγές στη σύνθεση και την κατάσταση του περιβάλλοντος, διαταράσσουν την ποσοτική και ποιοτική σύνθεση της ζωντανής ύλης (μέχρι μεταλλάξεις) και τροποποιούν τα υπάρχοντα συστήματα ανταλλαγής ενέργειας, μάζας και υγρασίας. Ωστόσο, επί του παρόντος διαθέσιμα στοιχεία υποδηλώνουν ότι οι ανθρωπογενείς αλλαγές δεν αντικατοπτρίζονται θεμελιωδώς στο γεωγραφικό περίβλημα. Η σχετική ισορροπία της ύπαρξής του και η βιωσιμότητα της ανάπτυξης διασφαλίζονται κυρίως από φυσικά αίτια, η κλίμακα των οποίων υπερβαίνει την ανθρώπινη επιρροή. Από αυτό δεν προκύπτει ότι το ίδιο το γεωγραφικό περίβλημα θα υπερνικά πάντα την αυξανόμενη ανθρωπογενή πίεση. Οι παρεμβάσεις στη φύση πρέπει να ρυθμίζονται από την άποψη της σκοπιμότητας των εκδηλώσεών τους - προς όφελος της ανθρωπότητας και χωρίς σημαντική βλάβη στο φυσικό περιβάλλον. Οι έννοιες που αναπτύσσονται προς αυτή την κατεύθυνση ονομάζονται βιώσιμη (ισορροπημένη) ανάπτυξη. Θα πρέπει να βασίζονται σε γενικά γεωλογικά πρότυπα και χαρακτηριστικά της τρέχουσας κατάστασης και ανάπτυξης του γεωγραφικού περιβλήματος.

Εν κατακλείδι, ας αγγίξουμε τη διαφαινόμενη δήλωση που γίνεται το σύγχρονο γεωγραφικό περίβλημα ανθρωπόσφαιρα,ή μέρος της αναδυόμενης νοοσφαίρα.Σημειώστε ότι η έννοια της «νοόσφαιρας» είναι σε μεγάλο βαθμό φιλοσοφικής φύσης. Η ανθρώπινη επίδραση στο περιβάλλον και η εμπλοκή των απορριμμάτων σε αυτό είναι ένα αναμφισβήτητο φαινόμενο. Είναι σημαντικό να καταλάβουμε ότι πιο συχνά ένα άτομο αλλάζει τον βιότοπό του όχι συνειδητά, αλλά μέσω απρόβλεπτων συνεπειών. Επιπλέον, αυτές οι υλοποιήσεις δεν στοχεύουν σε όλα τα στοιχεία του γεωγραφικού περιβλήματος, αλλά μόνο στα συστατικά που είναι απαραίτητα για τους ανθρώπους (δάσος, έδαφος, πρώτες ύλες κ.λπ.). Έτσι, υπάρχουν μόνο θύλακες αλλαγής, αν και μερικές φορές πολύ σημαντικές και σοβαρές, και παρόλο που η ανθρώπινη δραστηριότητα αυξάνεται, η φύση εξακολουθεί να αναπτύσσεται κυρίως υπό την επίδραση φυσικών διεργασιών. Ως εκ τούτου, επί του παρόντος θα πρέπει να μιλάμε για ορισμένες περιοχές του γεωγραφικού περιβλήματος, όπου το φυσικό περιβάλλον έχει αλλάξει σημαντικά και αναπτύσσεται υπό την επίδραση ανθρωπο-ρυθμιζόμενων διαδικασιών.

Ρύζι. 9.4. Μερικά σχόλια που διέπουν το παγκόσμιο κλίμα

Ερωτήσεις ελέγχου

Ποια φαινόμενα ταξινομούνται ως παγκόσμιες αλλαγές στο γεωγραφικό περίβλημα;

Ποιες είναι οι ιδιαιτερότητες των παγκόσμιων αλλαγών στα τέλη του 20ού και στις αρχές του 21ου αιώνα;

Τι είναι το φαινόμενο του θερμοκηπίου και ποιες οι συνέπειές του;

Τι είναι ένα κοινό πρόβλημαανθρωπογενοποίηση του γεωγραφικού περιβλήματος;

Ποιο είναι το πρόβλημα με την υπερθέρμανση του κλίματος;

Ποιοι είναι οι κίνδυνοι της ρύπανσης από πετρέλαιο;

Τι είναι η παγκόσμια περιβαλλοντική κρίση, πώς και πού εκδηλώνεται;

Τι νόημα έχουν οι αισιόδοξες και απαισιόδοξες απόψεις για την ανάπτυξη του πλανήτη Γη;

Τι αντίκτυπο Πολικός πάγοςέχουν αντίκτυπο στο γεωγραφικό περίβλημα;

Τι είναι οι αλλαγές στο επίγειο τοπίο;

ΒΙΒΛΙΟΓΡΑΦΙΑ

Alpatiev A. M.Ανάπτυξη, μεταμόρφωση και προστασία του φυσικού περιβάλλοντος. - Λ., 1983.

Balandin R.K., Bondarev L.G.Φύση και πολιτισμός. - Μ., 1988.

Βιολογική ένδειξη στην ανθρωποοικολογία. - Λ., 1984.

Bitkaeva L.Kh., Nikolaev V.A.Τοπία και ανθρωπογενής ερημοποίηση του Terek Sands. - Μ., 2001.

Bokov V.A., Lushchik A.V.Βασικές αρχές της περιβαλλοντικής ασφάλειας. - Συμφερούπολη, 1998.

Vernadsky V.I.Βιόσφαιρα και νοόσφαιρα. - Μ., 1989.

Γεωγραφικά προβλήματα του τέλους του 20ου αιώνα / Rep. εκδ. Yu. P. Seliverstov. - Αγία Πετρούπολη, 1998.

Γεωγραφία και περιβάλλον / Υπεύθυνος. εκδ. N. S. Kasimov, S. M. Malkhazova. - Μ., 2000.

Παγκόσμιες αλλαγές στο φυσικό περιβάλλον (κλίμα και υδάτινο καθεστώς) / Rep. εκδ. N.S. Kasimov. - Μ., 2000.

Οι παγκόσμιες και περιφερειακές κλιματικές αλλαγές και οι φυσικές και κοινωνικοοικονομικές τους συνέπειες / Υπεύθυνος. εκδ. V.M. Kotlyakov. - Μ., 2000.

Παγκόσμια οικολογικά προβλήματαστο κατώφλι του 21ου αιώνα / Rep. εκδ. F.T. Yanshina. - Μ., 1998.

Govorushko S. M.Η επίδραση των φυσικών διεργασιών στην ανθρώπινη δραστηριότητα. - Βλαδιβοστόκ, 1999.

Golubev G.N.Γεωοικολογία. - Μ., 1999.

Gorshkov V. G.Φυσικά και βιολογικά θεμέλια της βιωσιμότητας της ζωής. - Μ., 1995.

Gorshkov SP.Εννοιολογικές βάσεις της γεωοικολογίας. - Σμολένσκ, 1998.

Γκριγκόριεφ Α. Α.Οικολογικά μαθήματα του παρελθόντος και του παρόντος. - Λ., 1991.

Grigoriev A. A., Kondratiev K. Ya.Οικοδυναμική και γεωπολιτική. - Τ. 11. Περιβαλλοντικές καταστροφές. - Αγία Πετρούπολη, 2001.

Gumilyov L. N.Εθνογένεση και βιόσφαιρα της Γης. - Λ., 1990.

Danilov A.D., Korol I.L.Ατμοσφαιρικό όζον - αισθήσεις και πραγματικότητα. - Λ., 1991.

Dotto L.Ο πλανήτης Γη βρίσκεται σε κίνδυνο. - Μ., 1988.

Zaletaev V.S.Οικολογικά αποσταθεροποιημένο περιβάλλον. Οικοσυστήματα άνυδρων ζωνών σε μεταβαλλόμενο υδρολογικό καθεστώς. - Μ., 1989.

Γη και ανθρωπότητα. Παγκόσμια προβλήματα/ Χώρες και λαοί. - Μ., 1985.

Zubakov V. A. Ecogea - House Earth. Εν συντομία για το μέλλον. Περιγράμματα της έννοιας του ecogay για μια διέξοδο από την παγκόσμια περιβαλλοντική κρίση. - Αγία Πετρούπολη, 1999.

Zubakov V. A.Σπίτι Γη. Περιγράμματα οικογεωσοφικής κοσμοθεωρίας. (Επιστημονική ανάπτυξη στρατηγικής συντήρησης). - Αγία Πετρούπολη, 2000.

Isachenko A. G.Βελτιστοποίηση του φυσικού περιβάλλοντος. - Μ., 1980.

Isachenko A. G.Οικολογική γεωγραφία της Ρωσίας. - Αγία Πετρούπολη, 2001.

Kondratyev K. Ya.Παγκόσμιο κλίμα. - Μ., 1992.

Kotlyakov V. M.Η επιστήμη. Κοινωνία. Περιβάλλον. - Μ., 1997.

Kotlyakov V.M., Groswald M.G., Lorius K.Παλαιότερα κλίματα από τα βάθη των φύλλων πάγου. - Μ., 1991.

Lavrov S.B., Sdasyuk G.V.Αυτός ο αντίθετος κόσμος. - Μ., 1985.

Περιβάλλον / Εκδ. A. M. Ryabchikova. - Μ., 1983.

Βασικές αρχές Γεωοικολογίας / Εκδ. V. G. Morachevsky. - Αγία Πετρούπολη, 1994.

Petrov K. M.Φυσικές διαδικασίες αποκατάστασης κατεστραμμένων εδαφών. - Αγία Πετρούπολη, 1996.

Προβλήματα οικολογίας στη Ρωσία / Υπεύθυνος. εκδ. V. I. Danilov-Danilyan, V. M. Kotlyakov. - Μ., 1993.

Η Ρωσία στον κόσμο γύρω μας: 1998. Αναλυτική συλλογή / Εκδ. εκδ. N.N. Moiseeva, S.A. Stepanova. - Μ., 1998.

Rown S.Κρίση του όζοντος. Η δεκαπενταετής εξέλιξη μιας απροσδόκητης παγκόσμιας απειλής. - Μ., 1993.

Ρωσική Γεωγραφική Εταιρεία: νέες ιδέες και μονοπάτια / Rep. εκδ. A.O Brinken, S.B Lavrov, Yu.P. - Αγία Πετρούπολη, 1995.

Seliverstov Yu.Το πρόβλημα του παγκόσμιου περιβαλλοντικού κινδύνου // Νέα της Ρωσικής Γεωγραφικής Εταιρείας. - 1994. - Τεύχος. 2.

Seliverstov Yu.Ανθρωπογενοποίηση της φύσης και το πρόβλημα της περιβαλλοντικής κρίσης // Vestnik Αγίας Πετρούπολης. Πανεπιστήμιο. - 1995. - Σερ. 7. - Τεύχος. 2.

Seliverstov Yu.Πλανητική περιβαλλοντική κρίση: αιτίες και πραγματικότητες // Vestnik Αγίας Πετρούπολης. Πανεπιστήμιο. - 1995. - Σερ. 7. - Τεύχος. 4.

Fortescue J.Γεωχημεία του περιβάλλοντος. - Μ., 1985.

Οικολογική εναλλακτική / Εκδ. εκδ. M.Ya. - Μ., 1990.

Περιβαλλοντικές επιταγές της αειφόρου ανάπτυξης της Ρωσίας / Εκδ. V.T.Pulyaeva.-L., 1996.

Περιβαλλοντικά προβλήματα: τι συμβαίνει, ποιος φταίει και τι πρέπει να γίνει; / Εκδ. V.I.Danilov-Danilyan. - Μ., 1997.

Yanshin A.L., Melua A.I.Μαθήματα από περιβαλλοντικές κρίσεις. - Μ., 1991.

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

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

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

Ηφαιστειακά βουνά μπορούν επίσης να σχηματιστούν υποβρύχια. Για παράδειγμα, τα νησιά της Χαβάης είναι οι κορυφές των ηφαιστειακών βουνών.

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

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

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

Αυτός ο πάγος, με τη σειρά του, προκάλεσε το σχηματισμό παγετώνων, που είναι αργά κινούμενα ποτάμια πάγου.

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

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

Μετά το τέλος της Εποχής των Παγετώνων, ο πάγος και το χιόνι άρχισαν να λιώνουν. Καθώς οι πάγοι έλιωναν, το πλάτος των ποταμών αυξήθηκε. Την ίδια ώρα, η στάθμη της θάλασσας ανέβαινε. Έτσι, στη θέση των ποταμών σχηματίστηκαν φιόρδ.

Οι ακτές των φιόρδ είναι συνήθως βραχώδεις πλαγιές, που μερικές φορές φτάνουν σε ύψος τα 1.000 μέτρα (3.000 πόδια).

Μερικά φιόρδ είναι τόσο βαθιά που τα πλοία μπορούν να κινηθούν μέσα από αυτά.

Ένας μεγάλος αριθμός φιόρδ βρίσκεται στις ακτές της Φινλανδίας και της Γροιλανδίας. Όμως τα πιο όμορφα φιόρδ βρίσκονται στη Νορβηγία. Το μακρύτερο φιόρδ βρίσκεται επίσης στη Νορβηγία. Λέγεται Sognefjord. Το μήκος του είναι 180 χιλιόμετρα (113 μίλια).

Όταν οι πάγοι λιώνουν, οι μορένιες -συσσωρεύσεις θραυσμάτων βράχου- παραμένουν πίσω και σχηματίζουν ζιγκ-ζαγκ βουνοκορφές. Ποτάμια χαράζουν χαράδρες σε χαλαρούς βράχους και σε ορισμένα σημεία τεράστια φαράγγια (βαθιές κοιλάδες ποταμών με απότομες κλιμακωτές πλαγιές), όπως το Γκραντ Κάνυον στην Αριζόνα (ΗΠΑ). Εκτείνεται σε μήκος 349 χιλιομέτρων.

Οι βροχές και οι άνεμοι είναι αληθινοί γλύπτες και σκαλίζουν πραγματικές γλυπτικές ομάδες και διάφορες φιγούρες. Στην Αυστραλία υπάρχουν οι λεγόμενοι Wind Cliffs και όχι μακριά από το Krasnoyarsk υπάρχουν πέτρινοι πυλώνες. Και οι δύο σχηματίστηκαν ως αποτέλεσμα της αιολικής διάβρωσης.

Η διάβρωση της επιφάνειας της γης απέχει πολύ από το να είναι μια αβλαβής διαδικασία. Κάθε χρόνο, χάρη σε αυτό, εξαφανίζονται πολλές δεκάδες εκτάρια καλλιεργήσιμης γης. Μεγάλη ποσότητα γόνιμου εδάφους μεταφέρεται στα ποτάμια, ο σχηματισμός του οποίου σε φυσικές συνθήκες διαρκεί εκατοντάδες χρόνια. Ως εκ τούτου, οι άνθρωποι προσπαθούν με κάθε δυνατό τρόπο να καταπολεμήσουν τη διάβρωση.

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

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