Foto av sskennel.
Dagens Spørsmål & Svar-sesjon kommer til oss med høflighet av SuperUser-en underavdeling av Stack Exchange, en community-drive-gruppering av Q & A-nettsteder.
Spørsmålet
SuperUser leser Sathya stillte spørsmålet:
Her kan du se et skjermbilde av et lite C ++-program kalt Triangle.exe med en roterende trekant basert på OpenGL API.
Jeg var bare nysgjerrig og ønsket å vite hele prosessen fra å dobbeltklikke på Triangle.exe under Windows XP til jeg kan se trekanten roterende på skjermen. Hva skjer, hvordan CPU (som håndterer.exe) og GPU (som endelig utgir trekant på skjermen) samhandler?
Jeg antar at det er involvert i å vise denne roterende trekanten, hovedsakelig følgende maskinvare / programvare blant andre:
maskinvare
- HDD
- Systemminne (RAM)
- prosessor
- Videominnet
- GPU
- LCD-skjerm
programvare
- Operativsystem
- DirectX / OpenGL API
- Nvidia Driver
Kan noen forklare prosessen, kanskje med en slags flytskjema for illustrasjon?
Det bør ikke være en kompleks forklaring som dekker hvert eneste trinn (antar at det ville gå utover omfanget), men en forklaring som en mellomliggende IT-fyr kan følge.
Jeg er ganske sikker på at mange mennesker som selv ville kalle seg IT-fagfolk kunne ikke beskrive denne prosessen riktig.
Svaret
Bilde av JasonC, tilgjengelig som bakgrunn her.
Han skriver:
Jeg bestemte meg for å skrive litt om programmeringsaspektet og hvordan komponenter snakker med hverandre. Kanskje det vil kaste lys på enkelte områder.
Presentasjonen
Hva tar det for å til og med ha det enkle bildet, som du skrev inn i spørsmålet ditt, trukket på skjermen?
Det er mange måter å tegne en trekant på skjermen. For enkelhet, la oss anta at ingen toppunktsbuffere ble brukt. (EN vertex bufferer et område med minne hvor du lagrer koordinater.) La oss anta at programmet bare fortalte grafikkbehandlingsrørledningen om hvert enkelt toppunkt (et toppunkt er bare en koordinat i rommet) på rad.
Men, før vi kan tegne noe, må vi først kjøre noen stillas. Vi får se Hvorfor seinere:
// Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Reset The Current Modelview Matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Drawing Using Triangles glBegin(GL_TRIANGLES); // Red glColor3f(1.0f,0.0f,0.0f); // Top Of Triangle (Front) glVertex3f( 0.0f, 1.0f, 0.0f); // Green glColor3f(0.0f,1.0f,0.0f); // Left Of Triangle (Front) glVertex3f(-1.0f,-1.0f, 1.0f); // Blue glColor3f(0.0f,0.0f,1.0f); // Right Of Triangle (Front) glVertex3f( 1.0f,-1.0f, 1.0f); // Done Drawing glEnd();
Så hva gjorde det?
Når du skriver et program som vil bruke grafikkortet, vil du vanligvis velge en slags grensesnitt til sjåføren. Noen kjente grensesnitt til sjåføren er:
- OpenGL
- Direct3D
- CUDA
For dette eksempelet holder vi oss med OpenGL. Nå, din grensesnitt til sjåføren er det som gir deg alle verktøyene du trenger for å lage ditt program snakke til grafikkortet (eller driveren, som da samtaler til kortet).
Dette grensesnittet er forpliktet til å gi deg sikker verktøy. Disse verktøyene har formen på en API som du kan ringe fra programmet.
Det API er det vi ser blir brukt i eksemplet ovenfor. La oss ta en nærmere titt.
Stillasene
Før du virkelig kan gjøre noen konkrete tegninger, må du utføre en oppsett. Du må definere visningsporten din (det området som faktisk skal gjengis), ditt perspektiv (the kamera inn i din verden), hvilken anti-aliasing du skal bruke (å glatte ut kantet av trekanten din) …
Men vi vil ikke se på noe av det. Vi tar bare en titt på ting du må gjøre hver ramme. Som:
Fjerner skjermen
Grafikkrøret kommer ikke til å fjerne skjermen for hver ramme. Du må fortelle det. Hvorfor? Det er derfor:
Hvis du ikke tømmer skjermen, vil du ganske enkelt tegne over det hver ramme. Det er derfor vi kaller
glClear
med
GL_COLOR_BUFFER_BIT
sett. Den andre delen (
GL_DEPTH_BUFFER_BIT
) forteller OpenGL å slette dybdebuffer. Denne bufferen brukes til å bestemme hvilke piksler som er foran (eller bak) andre piksler.
Transformation
Transformasjon er den delen der vi tar alle inngangskoordinatene (hjørnene i vår trekant) og bruker vår ModelView-matrise. Dette er matrisen som forklarer hvordan vår modell (hjørnene) roteres, skaleres og oversettes (flyttes).
Deretter bruker vi vår projeksjonsmatrise. Dette beveger alle koordinater slik at de møter kameraet vårt riktig.
Nå forvandler vi en gang til, med vår Viewport-matrise. Vi gjør dette for å skalere vår modell til størrelsen på skjermen vår. Nå har vi et sett med vinkler som er klare til å bli gjengitt!
Vi kommer tilbake til transformasjon litt senere.
Tegning
For å tegne en trekant, kan vi bare fortelle OpenGL for å starte en ny liste over trekanter ved å ringe
glBegin
med
GL_TRIANGLES
konstant. Det finnes også andre former du kan tegne. Som en trekantstrimmel eller en trekantvifte.Dette er primært optimaliseringer, da de krever mindre kommunikasjon mellom CPU og GPU for å tegne samme antall trekanter.
Etter det kan vi gi en liste over sett med 3 hjørner som skal utgjør hver trekant. Hver triangel bruker 3 koordinater (som vi er i 3D-plass). I tillegg gir jeg også en farge for hvert toppunkt, ved å ringe
glColor3f
før ringer
glVertex3f
Skyggen mellom de 3 hjørnene (trekantens tre hjørner) beregnes av OpenGL automatisk. Det vil interpolere fargen over hele polygonets ansikt.
Interaksjon
Nå, når du klikker på vinduet. Søknaden må bare fange vinduet meldingen som signaliserer klikket. Deretter kan du kjøre noen handling i programmet du vil ha.
Dette får en mye vanskeligere når du vil begynne å samhandle med 3D-scenen din.
Du må først klart vite hvilken piksel brukeren klikket på vinduet. Deretter tar du perspektivmedregnet, kan du beregne retningen av en stråle, fra museklikkpunktet til scenen din. Du kan da beregne om noe objekt i din scene skjærer med den strålen. Nå vet du om brukeren klikket på et objekt.
Så, hvordan får du det til å rotere?
Transformation
Jeg er klar over to typer transformasjoner som vanligvis brukes:
- Matrise-basert transformasjon
- Bone-basert transformasjon
Forskjellen er det bein påvirker singelen toppunkter. Matriser påvirker alltid alle trukket hjørner på samme måte. La oss se på et eksempel.
Eksempel
Tidligere lastet vi vår identitetsmatrise før du trekker vår trekant. Identitetsmatrisen er en som bare gir ingen transformasjon i det hele tatt. Så, hva jeg tegner, påvirkes bare av mitt perspektiv. Så trekant vil ikke roteres i det hele tatt.
Hvis jeg vil rotere det nå, kan jeg enten gjøre matematikken meg selv (på CPU) og bare ringe
glVertex3f
medannen koordinater (som roteres). Eller jeg kunne la GPU gjøre alt arbeidet, ved å ringe
glRotatef
før tegning:
// Rotate The Triangle On The Y axis glRotatef(amount,0.0f,1.0f,0.0f);
amount
er selvsagt bare en fast verdi. Hvis du vil animere, du må holde styr på
amount
og øk det hver ramme.
Så, vent, hva skjedde med all matriksprat tidligere?
I dette enkle eksempelet trenger vi ikke å bry seg om matriser. Vi ringer bare
glRotatef
og det tar seg av alt det for oss.
glRotate
produserer en rotasjon av
angle
grader rundt vektoren x y z. Den nåværende matrisen (seeglMatrixMode) multipliseres med en rotasjonsmatrise med produktet som erstatter den nåværende matrisen, som ifglMultMatrix ble kalt med følgende matrise som argument:
x 2 1 - c + cx y 1 - c - zxzz 1 - c + y s 0 y x 1 - c + z 2 sy 1 - c + cyz 1 - c - xs 0 xzz 1 - c - yzz 1 - c + xzz 2 1 - c + c 0 0 0 0 1
Vel, takk for det!
Konklusjon
Det som blir tydelig er at det er mye snakk til OpenGL. Men det forteller ikke oss hva som helst. Hvor er kommunikasjonen?
Det eneste som OpenGL forteller oss i dette eksemplet er når det er gjort. Hver operasjon vil ta en viss tid. Enkelte operasjoner tar utrolig lang tid, andre er utrolig raske.
Sender et toppunkt til GPU vil være så fort, jeg ville ikke engang vite hvordan å uttrykke det. Å sende tusenvis av krysser fra CPU til GPU, hver enkelt ramme, er mest sannsynlig ingen problem i det hele tatt.
Fjerner skjermen kan ta en millisekund eller verre (husk at du vanligvis bare har ca 16 millisekunder tid for å tegne hver ramme), avhengig av hvor stor visningsporten din er. For å rydde det, må OpenGL tegne hver enkelt piksel i fargen du vil slette til, det kan være millioner av piksler.
Annet enn det, kan vi ganske enkelt bare spørre OpenGL om egenskapene til grafikkadapteren vår (maks oppløsning, maks anti-aliasing, maks fargedybde, …).
Men vi kan også fylle en tekstur med piksler som hver har en bestemt farge. Hver piksel har dermed en verdi, og tekstur er en gigantisk "fil" fylt med data. Vi kan laste det inn på grafikkortet (ved å lage en teksturbuffer), og last deretter inn en skygge, fortelle at shader å bruke tekstur som en inngang og kjøre noen ekstremt tunge beregninger på vår "fil".
Vi kan så "gjengi" resultatet av vår beregning (i form av nye farger) til en ny tekstur.
Slik kan du gjøre GPU-en for deg på andre måter. Jeg antar at CUDA utfører ligner det aspektet, men jeg har aldri hatt mulighet til å jobbe med det.
Vi har egentlig bare litt rørt hele emnet. 3D grafikk programmering er et helvete av et dyr.
Har du noe å legge til forklaringen? Lyde av i kommentarene. Vil du lese flere svar fra andre tech-savvy Stack Exchange-brukere? Sjekk ut hele diskusjonstråden her.