I den første delen, der vi tar en grundigere titt på 3D-spilloppretting, fokuserer vi helt på det øverste trinnet i prosessen. Det betyr å dra mattebøkene våre, pusse på lineær algebra, matriser og trigonometri - å ja!

Vi driver hvordan 3D-modeller transformeres og hvordan lyskilder beregnes. Forskjellene mellom hjørne- og geometryskygger vil bli grundig undersøkt, og du vil se hvor tessellasjonen passer. For å hjelpe til med forklaringer, bruker vi diagrammer og kodeeksempler for å vise hvordan matematikk og tall håndteres i et spill. Hvis du ikke er klar for alt dette, ikke bekymre deg - du kan komme i gang. 3D-spillrendering 101. Når den er klar, kan du lese videre for vårt første glimt av 3D-grafikkverdenen.

Hva er hensikten?

I matematikkens verden er et punkt rett og slett en posisjon innenfor et geometrisk felt. Det er ingenting mindre enn et punkt, da det ikke har størrelse, så de kan brukes til å tydelig definere hvor objekter som linjer, plan og volumer begynner og slutter.




For 3D-grafikk er denne informasjonen avgjørende for å bestemme hvordan alt skal se ut, ettersom alt som vises er en linje, et plan osv. Samling. Bildet nedenfor er et skjermbilde fra 2015-utgivelsen av Bethesda. Nedfall 4:




Det kan være litt vanskelig å se hvordan dette bare er en stor haug med punkter og linjer, så vi viser deg hvordan den samme scenen ser ut i 'wireframe'-modus. 3D-gjengivelsesmotoren hopper over teksturene og effektene som er laget i pikselstadiet og tegner ikke annet enn fargede linjer som forbinder prikkene.







Alt ser veldig annerledes ut nå, men vi kan se forskjellige objekter, miljøet og alle linjene som kommer sammen for å skape bakgrunnen. Noen er bare en håndfull linjer, som bergarter i forgrunnen, mens andre har nok linjer til å se solide ut.

Hvert punkt i begynnelsen og slutten av hver linje er gjennomarbeidet med mye matematikk. Noen av disse beregningene er veldig raske og enkle; andre er mye vanskeligere. Det er betydelige ytelsesgevinster ved å samarbeide om grupper av punkter, spesielt i en trekantform, så la oss se nærmere på disse.




Hva trengs for en trekant?

Navn triangel det forteller oss at formen har 3 indre vinkler; For dette trenger vi 3 hjørner og 3 linjer som forbinder hjørnene. Riktig navn på hjørnet høyde (hjørner er flertallsordet) og hver er definert av et punkt. Siden vi er basert på en 3D-geometrisk verden, Kartesisk koordinatsystem for poeng. Dette skrives vanligvis sammen som 3 verdier, for eksempel (1, 8, -3) eller mer generelt (X ve Z).

Herfra kan vi legge til to hjørner for å få en trekant:




Merk at linjene som vises ikke egentlig er nødvendige - vi kan bare ha poeng, og vi kan fortelle systemet at disse 3 toppunktene utgjør en trekant. Alle toppunktdata lagres i en sammenhengende minneblokk kalt a hjørne støtfanger; Informasjon om formen de vil lage blir direkte kodet i gjengivelsesprogrammet eller lagret i en annen minneblokk. katalogbuffer.

Når det gjelder førstnevnte, kalles forskjellige former som kan dannes fra hjørnene primitiv Direct3D presenterer liste, striper og vifter i prikk-, linje- og trekantform. Brukt riktig, trekantede striper bidrar til å forbedre ytelsen ved å bruke hjørner til flere trekanter. I eksemplet nedenfor kan vi se at bare 4 hjørner er nødvendig for å bli med 2 trekanter - hvis de var separate, ville vi trengt 6 hjørner.

Hvis du vil dekke en større samling hjørner, f.eks. en NPC-modell i spillet, deretter en - dette er nok en minneblokk, men består av flere buffere (toppunkt, indeks osv.) og teksturressurser for modellen. Microsoft gir en rask introduksjon til disse bufferbrukene. elektroniske dokumenter kilde.

For nå, la oss fokusere på hva som er gjort med disse hjørnene i et 3D-spill, hver gang en ny ramme opprettes (hvis du ikke er sikker på hva det betyr, gjør du en rask feie igjen) gjengi 101). Enkelt sagt gjøres en eller to ting mot dem:

  • Flytt toppunktet til et nytt sted
  • Endre fargen på hjørnet

Klar for litt matte? God! For det er slik det gjøres.

Skriv inn vektoren

Tenk deg at det er en trekant på skjermen, og du trykker på en knapp for å flytte den til venstre. Naturligvis (X ve Z) tallene på hvert toppunkt varierer tilsvarende, og de er; men, Hvordan Dette kan virke litt uvanlig. De aller fleste gjengivelsessystemer for 3D-grafikk, i stedet for å endre koordinatene, bruker et spesifikt matematisk verktøy for å få jobben gjort: vektorer.

En vektor kan betraktes som en pil som peker mot et bestemt sted i rommet og kan ha hvilken som helst ønsket lengde. Hjørner er faktisk beskrevet ved hjelp av vektorer basert på kartesiske koordinater på denne måten:

Legg merke til hvordan den blå pilen starter på et sted (i dette tilfellet Opprinnelse) og strekker seg til toppen. Vi brukte det som ble kalt columnotasjon for å beskrive denne vektoren roing notasjon fungerer også. Du vil også merke at det er en ekstra verdi - det fjerde tallet er vanligvis med w komponent og brukes til å indikere om vektoren brukes til å beskrive posisjonen til et toppunkt ( vektorposisjon) eller et generelt aspekt (a natt vektor). Når det gjelder sistnevnte, vil det se slik ut:

Denne vektoren peker i samme retning og har samme lengde som den forrige posisjonsvektoren, så (X ve Z) verdiene vil være de samme; Men w-komponentvektorer er null i stedet for 1. Bruken av retningsvektorer vil bli avklart senere i denne artikkelen, men foreløpig skal vi vurdere det faktum at alle hjørnepunkter i 3D-scenen vil bli definert på denne måten. Hvorfor? For med dette formatet blir det mye lettere å begynne å flytte dem rundt.

Matematikk, matte og mer matte

Husk at vi har en grunnleggende trekant og vil flytte den til venstre. Hvert toppunkt er definert av en posisjonsvektor, så vi må gjøre den 'bevegelige matematikken' ( konverteringer) må jobbe med disse vektorene. Skriv inn neste kjøretøy: serie (eller mor for en av disse). Dette er en serie verdier skrevet som rader og kolonner, litt som et Excel-regneark.

For hver type transformasjon vi ønsker å gjøre, er det en tilhørende matrise som følger med den, og det er bare tilfelle å multiplisere transformasjonsmatrisen og posisjonsvektoren sammen. Vi går ikke gjennom de spesifikke detaljene for hvordan og hvorfor dette skjer, men vi kan se hvordan det ser ut.

Flytt toppunktet i 3D-rom oversettelse og beregningen som kreves er:

x0etc. verdier representerer de opprinnelige koordinatene til toppunktet; delta-x Verdiene indikerer hvor mye toppen må bevege seg. Matrise-vektorberegning resulterer i å bare legge de to sammen ( w komponenten forblir uberørt, så det endelige svaret er fremdeles en posisjonsvektor).

I tillegg til å flytte ting rundt, vil vi kanskje rotere trekanten eller skalere den til en større eller mindre størrelse - det er transformasjoner for begge disse.

WebGL-drevet grafisk verktøy Nettsted for sanntidsrendering å visualisere disse beregningene på en hel figur. La oss starte med en kuboid på et standardsted:

I dette nettverktøyet refererer modellpunktet til posisjonsvektoren, jordmatrisen er transformasjonsmatrisen, og jordspacepunktet er posisjonsvektoren for det transformerte toppunktet.

La oss nå bruke forskjellige transformasjoner til kuboidet:

På bildet over, figuren oversettelse 5 enheter i hver retning. Vi kan se disse verdiene i den midtre store matrisen, den siste kolonnen. Den opprinnelige posisjonsvektoren (4, 5, 3, 1) forblir som den skal, men det transformerte toppunktet blir nå oversatt som (9, 10, 8, 1).

I denne transformasjonen ble alt skalert to ganger: kuboidet har nå sider dobbelt så lenge. Det siste eksemplet å se på er et vendepunkt:

Kuboidet roteres i en vinkel på 45 °, men matrisen deres ve cosinus vinkler den. En rask sjekk på hvilken som helst vitenskapelig kalkulator uten (45 °) = 0,7071 ... runder til verdien 0,71 vist. Vi får det samme svaret cosinus verdi.

Matriser og vektorer trenger ikke brukes; et vanlig alternativ for håndtering av spesielt komplekse rotasjoner, komplekse tall og kuaterniyonlar. Denne matte er et stort steg opp fra vektorer, så vi vil gjennomgå transformasjoner.

Kraften til toppskyggen

På dette stadiet må vi vurdere at alle disse må løses av menneskene som programmerer byggekoden. Hvis en spillutvikler bruker en tredjepartsmotor (som Unity eller Unreal), vil dette allerede være gjort for dem, men alle som gjør sine egne beregninger fra bunnen av, må finne ut hvilke hjørner de trenger å gjøre.

Så hvordan ser dette ut når det gjelder kode?

Vi vil bruke eksempler fra det utmerkede nettstedet for å hjelpe deg med dette. Braynzar Soft. Det er et flott sted å lære det grunnleggende og mer avanserte ting hvis du vil starte 3D-programmering selv.

Dette eksemplet er transformasjonen "alt i ett". Oppretter tilsvarende transformasjonsmatriser basert på tastaturinngang og bruker dem deretter på den opprinnelige posisjonsvektoren i en enkelt operasjon. Husk at dette alltid gjøres i en bestemt rekkefølge (skala - roter - snu) fordi enhver annen måte vil ødelegge resultatet helt.

Disse typene kodeblokker kalles hill shaders og de kan variere sterkt avhengig av hva de gjør, størrelse og kompleksitet. Eksemplet ovenfor er like enkelt og ubestridelig som de kommer kun en skyggelegging fordi den ikke bruker skyggenes fullt programmerbare natur. Et mer komplekst utvalg av skyggeleggere kan transformere det i 3D-rom, beregne hvordan scenen vil vises på kameraet, og deretter overføre dataene til neste trinn i gjengivelsesprosessen. Vi ser på flere eksempler når vi går gjennom hjørnesaksekvensen.

Selvfølgelig kan de brukes til mye mer, og husk at hver gang du spiller et spill som spilles i 3D, blir alle bevegelsene du kan se utført av grafikkprosessoren, i henhold til instruksjonene i hjørnehuggene.

Dette var imidlertid ikke alltid tilfelle. Går vi tilbake til midten av slutten av 1990-tallet i tid, hadde ikke grafikkort fra den tiden evnen til å håndtere hjørner og primitiver selv, alt gjort helt på CPU.

En av de første prosessorene som gir dedikert maskinvareakselerasjon for denne typen behandling Nvidias originale GeForce ble utgitt i 2000 og denne eiendommen er merket Konvertering og belysning av maskinvare (eller forkortet Hardware TnL). Operasjonene som denne maskinvaren kunne håndtere var veldig strenge og konstante når det gjaldt kommandoer, men det endret seg raskt da nye grafikkbrikker ble utgitt. I dag er det ingen egen maskinvare for hjørnebehandling, og de samme enhetene håndterer alt: punkter, primitive, piksler, teksturer, etc.

Snakker om belysning, det bør bemerkes at alt vi ser er selvfølgelig forårsaket av lys, så la oss se hvordan dette kan håndteres på toppstadiet. For å gjøre dette bruker vi noe vi har nevnt tidligere i denne artikkelen.

Lyser kameramotor!

Tenk deg denne scenen: skuespilleren står i et mørkt rom opplyst av en enkelt lyskilde til høyre. Midt i rommet er det en gigantisk flytende tykk tekanne. Ok, vi trenger sannsynligvis litt hjelp til å visualisere dette Nettsted for sanntidsrendering, for å se noe slikt i aksjon:

Legg merke til at dette objektet er en samling av rette trekanter sydd sammen; Dette betyr at planet til hver trekant vil peke i en bestemt retning. Noen ser på kameraet, andre den andre veien, mens andre er skjevt. Lyset fra kilden treffer hvert plan og spretter i en viss vinkel.

Avhengig av hvor lyset skal, vil fargen og lysstyrken på flyet endres, og alle disse må beregnes og beregnes for å sikre at fargen på objektet ser riktig ut.

Til å begynne med må vi vite hvilken retning flyet vender og for dette normal vektor fly. Dette er en annen pil, men i motsetning til posisjonsvektoren spiller ikke størrelsen noen rolle (faktisk skalerer de alltid etter beregning, slik at de er nøyaktig 1 enhet lange) og alltid dik (rett vinkel) plan.

Normalen til planet for hver trekant, vektorproduktet til de to retningsvektorene (p ve q Vist over) danner sidene av trekanten. Det er faktisk bedre å jobbe for hvert toppunkt og ikke for hver trekant, men med tanke på at det alltid vil være mer enn det forrige sammenlignet med det siste, er det raskere å gjøre det bare for trekanter.

Etter at du har nådd det normale på en overflate, kan du begynne å ta hensyn til lyskilden og kameraet. Lys kan være av forskjellige typer i 3D-gjengivelse, men kun for formålet med denne artikkelen retningsbestemt lys, f.eks. et søkelys. Som flyet til en trekant, vil søkelyset og kameraet peke i en bestemt retning, kanskje noe sånt:

Lysvektoren og den normale vektoren kan brukes til å finne vinkelen lyset treffer overflaten i (ved å bruke forholdet mellom punktproduktet til vektorene og produktet av deres dimensjoner). Hjørnene i trekanten vil ha ytterligere informasjon om farger og materialer - i sistnevnte tilfelle forklarer det hva som skjer med lyset når det treffer overflaten.

En glatt, metallisk overflate vil reflektere nesten alt innkommende lys i vinkelen den kommer fra, og vil nesten ikke endre farge. I kontrast sprer et grovt, kjedelig materiale lys på en mindre forutsigbar måte og skifter farge subtilt. For å ta hensyn til dette må hjørnepunktene ha flere verdier:

  • Original fargebase
  • Omgivende materialegenskap - en verdi som bestemmer hvor mye 'bakgrunnslys' toppunktet kan absorbere og reflektere
  • Diffusert materialegenskap - en annen verdi, men denne gangen viser hvor grov toppen er, dette påvirker hvor mye det spredte lyset absorberes og reflekteres
  • Spekulative materialegenskaper - to verdier som gir et mål på hvor 'lys' toppen er

Ulike lysmodeller bruker forskjellige matematiske formler for å gruppere dem alle sammen, og beregningen produserer en vektor for det innfallende lyset. Dette kombineres med kameraets vektor, og det generelle utseendet til trekanten kan bestemmes.

Vi har gått gjennom de finere detaljene her og av en bedre grunn: Ta tak i hvilken som helst lærebok om 3D-gjengivelse, og du vil se alle kapitlene som er viet til denne prosessen. Imidlertid utfører moderne spill ofte det meste av lysberegningene og materialeffektene på pikselberegningstrinnet, så vi vil dekke dette emnet igjen i en annen artikkel.

Alt vi har dekket så langt, er gjort ved hjelp av hill shaders, og det kan virke som om det er nesten ingenting de ikke kan gjøre; Dessverre er det. Vertex shaders kan ikke opprette nye hjørnepunkter, og hver skyggelegger må fungere på hvert toppunkt. Det hadde vært nyttig hvis det var en måte å bruke noen koder for å lage flere trekanter, en måte å få en skyggelegging til å jobbe mellom det vi allerede har (for å forbedre den visuelle kvaliteten) og over alt primitive (for å få fart på ting). før). Med moderne grafikkprosessorer, Kan Gjør dette!

Vennligst sir, jeg vil ha litt mer (trekanter)

De nyeste grafikkbrikkene er ekstremt kraftige og i stand til millioner av matrise-vektorberegninger hvert sekund; de kan lett passere over en stor haug med hjørner på kort tid. På den annen side er det veldig tidkrevende å lage svært detaljerte modeller, og hvis modellen kommer til å være litt utenfor scenen, vil alle disse ekstra detaljene være bortkastet.

Det vi trenger er en måte å fortelle prosessoren om å dele den i en større primitiv, en samling av mindre trekanter som alle er koblet inn i den originale større, som en enkelt rett trekant vi ser på. Navnet på denne prosessen: tesselasjon og grafikkbrikker har klart å gjøre dette en god stund; Det som har forbedret seg gjennom årene er hvor mye kontroll programmerere har over operasjonen.

For å se dette i aksjon bruker vi: Unigines Paradise benchmark-verktøy, da det lar oss bruke varierende mengder tessellasjon på de spesifikke modellene som ble brukt i testen.

For å begynne, la oss ta en plass i referanseindeksen og undersøke den uten mosaikken som er brukt. Vær forsiktig med at brosteinene på gulvet ser for falske ut - teksturen som brukes er effektiv, men ser ikke riktig ut. La oss bruke litt mosaikk på scenen; Unigine bruker motoren bare på visse deler, men forskjellen er dramatisk.

Gulvet, bygningskantene og døren ser nå mye mer realistisk ut. Hvis vi kjører prosessen igjen, kan vi se hvordan dette oppnås, men denne gangen med kantene på primitivene uthevet (trådrammemodus):

Vi ser tydelig hvorfor gulvet ser så rart ut - helt flatt! Døren er også i flukt med veggene, og bygningskantene er ikke mer enn enkle kuber.

I Direct3D kan primitive elementer deles i en mindre gruppe av deler ( nedre rom) Ved å kjøre en 3-trinns sekvens. Først programmerere kroppsskygge - i utgangspunktet er denne koden en geometri lapp. Tenk deg dette som et kart som forteller prosessoren hvor de nye prinsippene og linjene vil vises inne i den første primitive.

Deretter bruker tesselatorenheten inne i grafikkprosessoren plasteret til prinsippet. Endelig, områdeskygging beregner posisjonene til alle nye hjørner kjøres. Disse dataene kan mates tilbake til toppunktbufferen etter behov, slik at belysningsberegninger kan gjøres igjen, men denne gangen med bedre resultater.

Så hvordan ser dette ut? La oss skyte wireframe-versjonen av mosaikscenen:

Sannheten er at vi setter nivået på tessellation til et ganske ekstremt nivå for å forklare prosessen. Like bra som moderne grafikkbrikker, det er ikke noe du vil gjøre i hvert spill - for eksempel ta lyktestolpen ved døren.

I de trådløse bildene blir du presset til å beskrive forskjellen i denne avstanden, og du kan se dette tesselleringsnivået samlet i for mange trekanter, hvorav noen er vanskelige å fortelle. Likevel brukes den riktig, og denne funksjonen av hjørnegjengivelse kan resultere i noen flotte visuelle effekter, spesielt når du prøver å simulere myke kroppskollisjoner.

I de trådløse bildene blir du presset til å beskrive forskjellen i denne avstanden, og du kan se dette tesselleringsnivået samlet i for mange trekanter, hvorav noen er vanskelige å fortelle. La oss ta en titt på hvordan dette kan se ut i form av Direct3D-kode; For å gjøre dette vil vi bruke et eksempel fra et annet flott nettsted. RasterTek.

Her er en enkelt grønn trekant flislagt i mange flere baby-trekanter.

Kroning gjøres med 3 separate skyggeleggere (se fig. kodeeksempel): en toppunktskygge for å sette trekanten klar for triangulering, en kroppsskygge for å lage lappen, og en domeneskygge for å gjengi nye hjørner. Resultatet er veldig enkelt, men Unigine-eksemplet fremhever de potensielle fordelene og farene ved å bruke mosaikker overalt. Likevel brukes den riktig, og denne funksjonen av hjørnegjengivelse kan resultere i noen flotte visuelle effekter, spesielt når du prøver å simulere myke kroppskollisjoner.

Du kan takle det, kaptein!

Husker du hjørneskygger og alltid blir kjørt i hvert hjørne av scenen? Det er ikke vanskelig å se hvordan tessellering kan gjøre dette til et reelt problem. Og det er mange visuelle effekter som du vil takle flere versjoner av den samme primitive, men ikke vil lage et stort antall først; hår, pels, gress og eksploderende partikler er gode eksempler på dette.

Heldigvis er det en annen skyggelegging tilgjengelig bare for denne typen ting - geometri skyggelegging. Det er en mer restriktiv versjon av toppunktskyggen, men kan brukes på et helt prinsipp og kombinert med tessellasjon gir programmerere større kontroll over store toppunktgrupper.

Direct3D, som alle moderne grafiske API-er, tillater et bredt utvalg av beregninger på hjørnepunkter. Endelige data kan sendes til neste trinn i opprettelsesprosessen (pixelasjon) eller mates tilbake i minnepoolen slik at den kan bearbeides eller leses av CPU for andre formål. Dette kan gjøres som en datastrøm som Microsoft fremhever Direct3D-dokumentasjon:

strømningseffekt scenen er spesielt nyttig for effekter med mange partikler overalt, da den kan mate tilbake alle prinsippene (ikke individuelle hjørner) i gjengivelsessløyfen. Det samme trikset kan endres eller dynamisk hjørne støtfanger, men det er bedre å holde inngangs støtfangerne stabile fordi det er en ytelse hit hvis de trenger å "åpne" for å endre.

Hjørngjengivelse er en kritisk del av gjengivelse da den bestemmer hvordan scenen er arrangert fra kameraets perspektiv. Moderne spill kan bruke millioner av trekanter for å skape sine verdener, og hvert av disse hjørnene vil bli forvandlet og brent på en eller annen måte.

Å håndtere all denne matematikken og dataene kan virke som et logistisk mareritt, men grafikkprosessorer (GPUer) og API-er er designet med alt dette i bakhodet - skildrer en sømløst driftende fabrikk, skyter en vare gjennom en serie produksjonsstadier og gjør det bra. du vil forstå.

Opplevde 3D-spilloppretting programmerere har et omfattende grunnlag innen avansert matematikk og fysikk; De bruker alle triks og verktøy i handelen for å optimalisere transaksjoner og redusere topp prosesseringsfasen til bare noen få millisekunder. Og det er bare begynnelsen på å lage en 3D-ramme - deretter rasteriseringstrinnet, og så er det ganske intrikate piksler og teksturgjengivelse før den når hvor som helst i nærheten av skjermen.

Nå som du har nådd slutten av denne artikkelen, håper vi at du har et dypere innblikk i ferden til et toppunkt når du bearbeider for en 3D-ramme. Vi dekket ikke alt (det er en muazzam artikkel!), og vi er sikre på at du vil ha mange spørsmål om vektorer, matriser, lys og primitive elementer. Slipp dem på vei i kommentarfeltet, så vil vi gjøre vårt beste for å svare på dem alle.

Les også