Članki » Grafične tehnologije » Novosti DirectXa 9 - 1. del
Novosti DirectXa 9 - 1. del
- Marko Dolenc ::
No pa smo vendarle dočakali novo, dolgo pričakovano, različico knjižnice DirectX, ki jo je zlobni Microsoft, bolj ali manj skrbno skrival pred radovednimi očmi javnosti. V približno enaki tajnosti sta nastala tudi ta dva članka, ki vam bosta, vsaj upam tako, uspela razložiti vse čare in novosti novega programskega vmesnika. V članku, ki ga trenutno berete, si bomo ogledali vse novosti, ki jih je Microsoft namenil novim programabilnim funkcijam sedanjih in prihajajočih grafičnih kartic. Sedaj že splošno znano dejstvo se namreč glasi, da je potrebno programerjem pustiti čim bolj proste roke pri tem, kakšne grafične učinke bodo uporabljali v svojih igrah in kako bodo do njih prišli. Dosti blebečenja, začnimo z vsebino!
Novi osenčevalniki točk
Pixel shaderji, osenčevalniki točk, so gotovo ena izmed stvari, ki se je spremenila naravnost dramatično glede na DirectX 8.1. Če je do sedaj za pixel shaderje še nekako veljalo, da so le podaljšek že obstoječih zmogljivosti, je nova verzija tega osenčevalnika nekaj povsem prenovljenega. Glavni novosti sta gotovo to, da so lahko novi shaderji bistveno daljši od obstoječih, ter to, da vedno operirajo s plavajočo vejico, kar zagotavlja večjo natančnost pri izvajanju teh operacij. Novost je tudi to, da sta sedaj nabora ukazov v osenčevalnikih trikotnikov ter točk (vertex shaderjih in pixel shaderjih) praktično enaka. Pa si poglejmo, kako visoko so se dvignile meje pixel shaderjev:
Lastnosti | DirectX 8.x | DirectX 9.0 | |||
---|---|---|---|---|---|
1.0-1.3 | 1.4 | 2.0 | 2.x | 3.0 | |
Število aritmetičnih ukazov | 8 | 8* | 64 | do 512 | do 32768 |
Število ukazov dostopanje do tekstur | 4 | 6* | 32 | brez omejitev | brez omejitev |
Skupno število ukazov | 12 | 14* | 96 | do 512 | do 32768 |
Število različnih tekstur | 4 | 6 | 16 | 16 | 16 |
Število konstantnih registrov | 8 | 8 | 32 | 32 | 224 |
Število začasnih registrov | 2 | 6 | 12 | do 32 | 32 |
Poljubne premene komponent registrov | ne | ne | ne | možnost | da |
Statičen nadzor izvajanja shaderjev | ne | ne | ne | možnost (do 4 nivoje) | da (4 nivoje) |
Dinamičen nadzor izvajanja shaderjev | ne | ne | ne | možnost (do 24 nivojev) | da (24 nivojev) |
Največje število izvedenih ukazov | 12 | 28 | 96 | brez omejitev | brez omejitev |
Največja globina odvisnih branj | 2 | 2 | 4 | brez omejitev | brez omejitev |
Gradient ukazi | ne | ne | ne | možnost | da |
* Pixel shaderji 1.4 so lahko sestavljeni iz dveh faz, od katerih je ena namenjena pripravi koordinat za teksture, druga pa operacijam z barvami. Tako je možno število ukazov podvojiti.
Pixel Shader 2.0
Ker proizvajalci grafičnih čipovij začnejo z razvojem nove strojne opreme, še preden se začnejo sploh pogajati o podpori s strani DirectXa, je na tem področju kar precejšnja zmeda. Ker pa Microsoft ni želel ponoviti napake iz DirectX 8.x, ko je bilo na razpolago kar pet različnih inačic osenčevalnikov točk, so se tokrat odločili vključiti samo dve verziji (2.0 in 3.0). Da pa bi programerjem iger in proizvajalcem strojne opreme vseeno pustili več maneverskega prostora, pa je tukaj še vmesna verzija 2.x, ki omogoča razširjanje zmogljivosti osenčevalnika skoraj do verzije 3.0 glede na sposobnosti posameznih kartic. Radeon 9700 tako pade v kategorijo 2.0, GeForceFX pa v 2.x kategorijo.
Z verzijo osenčevalnikov točk 1.4 je prišlo do poenostavitve programskega modela, saj so bili vsi kompleksni ukazi zamenjani z enim samim (ki je teksel iz podane koordinate naložil v želeni register) in možnostjo, da si pixel shader svobodno izračuna koordinate željenih tekslov. Ta trend se nadaljuje -- v verziji 2.0 je možno aritmetične ukaze poljubno mešati z ukazi za dostopanje do tektur. Sedaj je na teksture bolj prikladno gledati kot na 1D, 2D in 3D tabele, kot pa le enostavne 2D slike. Poglejmo si za primer, kako v različnih verzijah pixel shaderjev naredimo enivronment bump mapping, ki ga sicer znajo narediti tudi nekatere kartice brez pixel shaderjev:
PS 1.1
ps_1_1 tex t0 texbem t1, t0 mov r0, t1]
PS1.4
ps_1_4 texld r0, t0 texcrd r1.xyz, t1 bem r1.xy, r0, r1 phase texld r1, r1 mov r0, r1]
PS 2.0
ps_2_0 dcl t0 dcl t1 dcl_2d s0 dcl_2d s1 texld r0, t0, s0 dp2add r0.x, c0, t1, r0.x dp2add r0.y, c1, t1, r0.y texld r0, r0, s1 mov oC0, r0
Od vseh treh verzij se morda ravno verzija 2.0 zdi najbolj zapletena, vendar še najbolj naravna in enako hitra kot ostali dve (ukazi dcl se uporabljajo za navodila gonilniku ter ne štejejo za ukaze). Čas je, da preklopimo na višjenivojske jezike.
Pri delu s koordinatami tekstur pa pride v novem osenčevalniku točk še posebej do izraza povečana natančnost računanja. Koordinate tekstur so vedno podane z 32-bitno natančnostjo na komponento. Če smo hoteli to koordinato (ki ima sedaj dosti bolj splošen pomen) uporabiti v kakšnem izračunu, se je njena natančnost hitro skrčila na [-8,8] v verziji 1.4 in [-1,1] v ostalih, obenem pa nismo več mogli v coloti dostopati do tekstur, večjih od 256x256 točk. V pixel shaderjih 2.0 imamo garantirano vsaj 16-bitno natančnost s plavajočo vejico, kar zmanjša problem z obsegom, obenem pa omogoča tudi večjo natančnost, saj lahko še vedno dostopamo do tekstur velikosti 1024x1024.
Naslednja pomembna stvar v novih pixel shaderjih pa je kar bistveno povečanje števila vzorcev tekstur. To programerjem omogoča, da bodo lahko izdelovali povsem svoje filtre, ki bodo pač najbolj ustrezali funkciji, kateri bo tekstura namenjena. Tako linearno kot anizotropno filtriranje se pri teksturah, ki so zapisane v formatu s plavajočo vejico, ne obneseta najbolje. Drugi del problema pa je, če je v teksturi zapisanega kaj drugega kot slika. Če imamo tako v teksturi zapisane vektorje, jih praktično moramo popravljati v pixel shaderjih.
Več vzorcev tekstur pa odpre tudi možnost, da pridemo do efektov, kot so zabris gibanja (motion blur), globina polja (depth of field) in mnogo drugih v samem jedru osenčevalnika.
Naslednja zanimivost pa so nedvomno statične in dinamične vejitve ter zanke, ki omogočajo, da se različne pike na zaslonu lahko izrišejo po povsem ločenih poteh. Prvi problem, ki se pri tem pojavi je, da se lahko en piksel izvaja po bistveno daljši poti, kot drugi. Ker pa grafični čipi izrisujejo pike v pomnilnik v blokih, morajo vsi že končani piksli čakati, da se končajo še vse časovno zahtevne pike. Vejitve omogočajo veliko novosti v grafičnem svetu. Med drugim je možno doseči tudi precej slovito sledenje žarkom (ray tracing) ter izrisati celotno 3D teksturo v enem samem prehodu, kar eventuelno omogoča vokslasto grafiko.
Statična vejitev
if b3 // Izvedeni ukazi, če je b3 true else // Izvedeni ukazi, če je b3 false endif
Dinamična vejitev
if_lt r0.x, r1.y // Izvedeni ukazi, če je r0.x manjši od r1.y else // Izvedeni ukazi, če je r1.y večji ali enak r0.x endif
Primerjava med statičnimi in dinamičnimi vejitvami. Pri statičnih vejitvah vedno operiramo le z konstantnimi boolean registri, ki jih nastavi aplikacija. Pri dinamičnih vejitvah pa si lahko privoščimo odločanje glede na to, kakšen je nek parameter v primerjavi z drugim
Zelo blizu vejitvam so tudi t.i. gradientni ukazi (gradient instructions), ki nam povedo, koliko se bo razlikoval parameter, ki smo ga podali, čez serijo sosednjih pik (ločeno v x in y smeri). S pomočjo teh ukazov je možno izračunati normalo za vsako piko sproti, samo s pomočjo višinske mape. Pri izdelavi lastnih filtrov si s temi ukazi lahko pomagamo in dosežemo podoben učinek kot ga ima običajno anizotropno filtriranje. Z njimi pa si lahko pomagamo tudi pri renderiranju senc z mehkimi robovi (soft shadows).
Novi osenčevalnik trikotnikov (vertex shader)
Izboljšave vertex shaderja so druga pomembna sprememba glede na DirectX 8.1. Nova osenčevalna enota ima lahko vsaj dvakrat več ukazov kot dosedanja ter mora imeti vsaj statični nadzor izvajanja shaderjev. Obenem pa morajo vertex shaderji sedaj podpirati vsaj 256 konstantnih registrov.
Lastnosti | DirectX 8.x | DirectX 9.0 | ||
---|---|---|---|---|
1.1 | 2.0 | 2.x | 3.0 | |
Število ukazov | 128 | 256 | 256 | do 32768 |
Število konstant | vsaj 96 | vsaj 256 | vsaj 256 | vsaj 256 |
Število začasnih registrov | 12 | 12 | do 32 | 32 |
Statičen nadzor izvajanja shaderjev | ne | da (do 4 nivoje) | da (do 4 nivoje) | da (4 nivoje) |
Dinamičen nadzor izvajanaj shaderjev | ne | ne | možnost (do 24 nivojev) | da (24 nivojev) |
Poljubne premene komponent registrov | da | da | da | da |
Dostop do tekstur | ne | ne | ne | da (4 teksture) |
Največje število izvedenih ukazov | 128 | brez omejitev | brez omejitev | brez omejitev |
Vertex shaderji 2.0
Podobno kot se je zgodilo v pixel shaderjih se je tudi pri vertex shaderjih. Da bi ugodili čim večim željam, je Microsoft tudi tukaj uvedel dva osnovna modela in enega razširjenega. Tudi tukaj Radeon 9700 pade v 2.0 kategorijo, GeForceFX pa v 2.x kategorijo, saj poleg statičnih vejitev podpira tudi dinamične. Zanke in vejitve se lahko uporabijo za združevanje večih osenčevalnikov v enega, kar zmanjša število potrebnih le-teh in zmanjša število menjav shaderjev v igrah (te menjave namreč zahtevajo nekaj dragocenega procesorskega časa). Zanke skupaj s podprogrami pa omogočajo tudi izogibanje podvajanju delov kode, s čimer lahko z enakim številom ukazov naredimo bistveno več.
Dinamične vejitve skupaj z večjim številom konstant pa bodo omogočile precejšen skok naprej pri animiranju likov v igrah. Brez dinamičnih vejitev je običajno potrebno lik razbiti na več delov in vsak del animirati posebej.
Visokonivojski programski jezik za osenčevalnike
Že zaradi dejstva, da so novi osenčevalniki vedno daljši ter vedno bolj splošni, se vedno bolj pojavlja potreba po višjenivojskih jezikih za pisanje shaderjev. DirectX 9 prinaša svoj HLSL (High Level Shading Language), ki je zelo podoben klasičnemu Cju in omogoča vse, kar se je do sedaj dalo narediti z zbirnikom.
Primerjava treh osenčevalnikov
Ročno napisana koda
ps_2_0 dcl t0.xyz dcl t1.xyz dp3 r0.w, t0, t0 rsq r0.w, r0.w mul r0.xyz, t0, r0.w dp3 r1.w, t1, t1 rsq r1.w, r1.w mul r1.xyz, t1, r1.w dp3 r2.x, r0, c0 mul r3, r2.x, c2 dp3 r2.y, r0, r1 pow r2.y, r2.y, c1.x add r0, r3, r2.y mov oC0, r0
Koda v DX9 HLSL
truct Input { float3 Normala:TEXCOORD0; float3 Halfway:TEXCOORD1; }; struct Output { float4 Color:COLOR0; }; // Konstante float4 Barva={0.96f, 0.89f, 0.22f, 0.0f}; float3 Luc={-1.0f, 0.0f, -0.5f}; float Eksponent=32.0f; Output main(Input Data) { Output Out=(Output)0; // Normaliziramo normalo in halfway vektor float3 Normala=normalize(Data.Normala); float3 Halfway=normalize(Data.Halfway); // Izračunamo diffuse in specular senčenje Out.Color=dot(Normala,Luc)*Barva+ pow(dot(Normala,Halfway),Eksponent); return Out; }
Prevajalnikova koda
ps_2_0 dcl t0.xyz dcl t1.xyz dp3 r0.w, t1, t1 rsq r2.w, r0.w mul r9.xyz, r2.w, t1 dp3 r9.w, t0, t0 rsq r9.w, r9.w mul r4.xyz, r9.w, t0 dp3 r4.w, r4, r9 dp3 r11.w, r4, c1 log r6.w, r4.w mul r1.w, r6.w, c2.x exp r8.w, r1.w mad r10, r11.w, c0, r8.w mov oC0, r10
Summa summarum
Microsoftu je očitno uspelo izdelati zelo dober prevajalnik, ki zna kodo zelo dobro optimaliizirati. V večini primerov namreč izenači na roko napisano kodo, če pa že pride do odstopanj, pa ta niso velika (eden ali dva ukaza) in tudi HLSL občasno najde kakšno možnost za optimizacijo več kot pa programer. V prihajajočih igrah bodo višjenivojski jeziki odigrali odločilno vlogo in programerji bodo odločili, kateri od teh jezikov se bodo obdržali in kateri bodo zdrsnili v pozabo. Po vsej verjetnosti pa se bodo obdržali kar vsi trije, o katerih se danes največ govori: DirectX 9 HLSL, Cg in OpenGL 2.0 HLSL.
Pixel shaderji 3.0 in vertex shaderji 3.0 so trenutno največ, kar nam ponuja DirectX, vendar strojne podpore za te shaderje še nekaj časa ne bomo videli. Model 3.0 je že skoraj povsem poenoten, saj omogoča dostop do tekstur iz vertex shaderja in ima skoraj povsem enak nabor ukazov za vertex shaderje in pixel shaderje. Omejitve so zelo visoko, saj so lahko shaderji dolgi kar 32768 ukazov, kar je bistveno več od tega kar nam strojna oprema ponuja danes. Shaderji 3.0 so nedvomno napoved, kaj se nam obeta v DirectX 10, kjer bodo shaderji po vsej verjetnosti že povsem brez omejitev kar se tiče dolžine in brez omejejevanja globine gnezdenja zank, kar bo omogočalo tudi rekurzivne metode (torej funkcije, ki kličejo same sebe).
Grafične kartice v bližni prihodnosti tako ne bodo več imele enot za vertex shaderje in cevovodov za izrisovanje pik (pixel pipelineov), ampak bodo imele le še neko število vekorskih aritmetičnih enot, ki jim bodo gonilniki sposobni dinamično določati delo (torej ali bo določena enota izvajala vertex shaderje ali pixel shaderje). Zagotovo se nam obeta še zanimiva prihodnost.
Čudežno popotovanje skozi grafični cevovod II
- Marko Dolenc ::
V prvem delu smo si ogledali strukturo sodobne grafične kartice. Med drugim smo omenili shaderje, ki so zaradi svoje programabilnosti, nekakšno srce in duša sodobnih GPU-jev. Različni shaderji so odgovorni za različne strukture v grafiki (v Direct3D 10 so to oglišča, ...
Test 7800 GTX
GeForce napada v sedmo Po napetem tekmovanju za najhitrejši grafični procesor med NVIDIO in ATI-jem se razvoj novih grafičnih kartic kot kaže umirja. Od splavitve GeForce 6800 je minilo že dobro leto in kljub pritisku ATI-ja v obliki X850 sta oba proizvajalca nekako ostala v pat poziciji. NVIDIA je ...
DirectX 9.0 - Osvetljevanje
- Marko Dolenc ::
Tokrat si bomo ogledali eno izmed možnosti osvetljevanja v knjižnici DirectX 9.0. Ker tudi DirectX 9.0 še vedno pozna le osnovne osvetlitvene modele (flat in Gouraud), kljub temu da je strojna oprema že precej časa zmožna tudi česa več, si bomo pomagali s pixel ...
Nova bitka v vojni grafičnih kartic
- Marko Dolenc ::
Da je računalništvo hitro se razvijajoča panoga čivkajo že vrabčki na vejah. Da se grafične kartice razvijajo še hitreje od ostalih področij znotraj računalništva, je takisto lahko jasno vsakemu, ki vsaj vsake toliko preleti kakšen cenik ...
Register Combiners vs. Pixel Shaders
- Marko Dolenc ::
Tokrat bo članek nekoliko bolj tehnične narave. Namen tega članka pa je prikazati kontrast med register combinerji (na primeru NVidine implementacije) in pixel shaderji v DirectX 8.x oziroma klasičnim SetTextureStageState mehanizmom v DirectXu. Za vse, ki se bodo ustrašili, ...