Renderinimo principai
Vienas renderinimo būdas yra tikrinti kiekvieno trikampio atstumą nuo kameros. Pradėti renderinti nuo to trikampio, kurio atstumas iki kameros didžiausias (nerenderinami tie trikampiai, kurių normalės vektorius neigiamas palyginus su ViewVector). Kiekvienas trikampis, sudarytas iš trijų vertex'ų, yra atstumu nuo kameros pagal vidutinį trijų vertex'ų atstumą nuo kameros. Jeigu scenoje yra 100 trikampių tai reikia patikrinti (palyginti) kiekvieną trikampį su 100 kitų trikampių. Todėl, jei scenoje yra 100 trikampių, tai reikia 100*100=10000 palyginimų. Jei scenoje yra 1000 trikampių, tai reikia milijono palyginimų. Jei scenoje yra 10000 trikampių, tai reikia palyginimų. Trikampių skaičių scenoje reikia pakelti kvadratu, kad gauti patikrinimų (trikampių palyginimų) skaičių.
Kitas renderinimo būdas yra renderinti kiekvieną objektą (daiktą) atskirai. Reikia tik žinoti, kuris objektas toliausiai nuo kameros. Tada pradėti renderinti nuo toliausiai nutolusio nuo kameros objekto. Jei objektas turi iššokusių dalių kaip nosis ar rankos, tada reikia atskirti bet kokias truputi daugiau iškištas dalis (vieną objektą padalinti į daug mažesnių objektų). Objekto skaidymas į dalis labai palengvina šešėlių renderinimą. Renderinami visi trikampiai vieno atskiro objekto bet kokia tvarka išskyrus neigiamos normalės trikampiai, kurių normalė pagal kameros koordinačių sistemą yra priešingo ženklo nei ViewVector gylio koordinatė.
Trečias būdas yra kaip pirmas būdas ir turi quadratic overheat (trikampių atstumo [nuo kameros] palyginimų skaičius yra trikampių skaičius pakeltas kvadratu), bet naudoja kažką tokio kaip tessellation. Patikrinami (koks trikampis toliausiai nuo kameros) objektai (scena) su mažai trikampių, o renderinami objektai su ant kiekvieno didelio trikampio 100 mažų trikampių. Tada, jeigu yra ant kiekvieno trikampio 100 mažesnių trikampių, tai jei scenoje yra 1000 pagrindinių (neteseliuotų) trikampių, tai reikia iš viso renderinti 1000*1000*100=10^8 trikampių, kai scenoje yra 1000*100=10^5 trikampių.
Update 1. Gali būti, kad tai kas aukščiau parašyta yra nesamonė, nes taip trikampių į atmintį negalima sudėti... Paprasčiausias būdas yra renderinti visus trikampius išeilės ir kartu su pixelio spalva išsaugoti to pixelio gylio koordinatę (z koordinatę ant trikampio). Paskui, kai renderinamas kitas trikampis (arba kito trikampio pixelis), tai naujo trikampio pixelio z koordinatę palyginti su esamo pixelio z koordinate. Kurio [trikampio] pixelio z koordinatė yra arčiau kameros, to trikampio pixelio spalva ir išsaugoma. Taip renderinant, reikia renderinti visus scenoje esančius trikampius, bet kai trikampių scenoje padaugėja 1000 kartų, tai scenos renderinimo skaičiavimų reikia 1000 kartų daugiau, o ne milijoną kartų daugiau kaip parašyta aukščiau (trikampių skaičiui scenoje augant linijiniai skaičiavimų skaičius irgi auga linijiniai, o ne kvadratu). Gal dar galima nustatyti objektų konturus ir nerenderinti objektų, kurie aiškiai visiškai yra už arčiau kameros esančio objekto. Bet žolės, gėlių, medžių vieno už kito visiškai nepaslėpsi, todėl čia greičiausiai reikia lyginti visus [spraitų ar trikampių] pixelius. Gal dar galima optimizuoti ir atmesti trikampius kurių pixeliai yra toliau ir nebus matomi...
Rimtas renderinimo būdo aprašymas
[keisti]- Aprašysiu renderinimo budą 3D žaidimuose, kuris tikrai veiks ir kuris atrodo kaip vienintelis (bent jau kol kas) įmanomas būdas normaliai projektuoti 3D sceną ant ekrano su perspektyva.
- Iš pradžiu visi 3D objektai sudėti į 3D pasaulį su tam tikromis jų centro koordinatėmis tame 3D pasaulyje arba scenoje. Ir kiekvienas iš tu objektų pasuktas pagal dvi kryptis (į kairę ar į dešinę ir žemyn arba aukštyn) tam tikru kampu, kad atitikti jam suteiktą stovėseną 3D scenoje.
- Toliau imamas tame 3D pasaulyje taškas iš kurio bus žiūrima arba, kitaip tariant, iš kurio bus projektuojamas matomas vaizdas į virtualią kamerą - ekraną. Tada šitas taškas pažymimas kaip taškas O arba 0 ant xOy plokštumos. Šita xOy plokštuma yra monitoriaus rezoliucija. Spindulys einantis iš taško O statmenai plokštumai xOy yra spindulys arba vektorius einantis iš monitoriaus ekrano centro į visą 3D pasaulį ir į tą spindulį sueis visa projekcija ir visi tolimi daiktai-objektai.
- Tada atstumas nuo plokštumos xOy (arba atstumas nuo ekrano ar virualios kameros) iki kiekvieno 3D objekto pažymimas per to objekto koordinate z, o koordinatės x ir y visų objektų scenoje nusako ant kiek objektas pastumtas į kairę ar dešinę ir/arba žemyn aukštyn pagal plokštumą xOy, kuri yra ekranas.
- Kad tolimesni objektai atrodytų mažesni x ir y koordinatės visų 3D objektų scenoje dalinamos iš z koordinatės kiekvieno to objekto, kuri yra atstumas nuo plokštumos xOy iki 3D objekto vertekso (viršunės; 3 viršunės sudaro trikampį). Tiksliau tariant, kiekvieno objekto vertekso x ir y koordinatė (kurios atitinka plokštumos xOy koordinates) dalinama iš koordinatės z to vertekso (iš atstumo nuo plokštumos xOy iki objekto konkretaus vertekso). Tokiu budu tolimesni daiktai arba 3D objektai tampa mažesni ir arčiau ekrano centro (arčiau taško O).
- Pavyzdžiui, jei skaičiuoti tik su sveikaisiais skaičiais, tai x koordinatės tarkime yra nuo -960 iki +960 (960*2=1920), o y koordinatės yra nuo -540 iki +540 (540+540=1080). Bet 3D objektai gali turėti ir didesnes absoliučiu dydžiu (moduliu) koordinates nei minėtos (x ir y koordinatės). Tada jeigu kokio nors 3D objekto vertekso (iš verteksų sudaryti trikampiai) koordinatė z yra 6000, o x=30000, y=100000, tai ant ekrano to objekto to vertekso x ir y koordinatės bus x=30000/6000=5, y=100000/6000=16.666=17.
- Tada išeina mažiausias atstumas nuo plokštumos xOy iki 3D objekto vertekso yra 1.
- Kas yra Field of View? Iš tikro, manau, Field of View (kuris kai kuriuose 3D žaidimuose gali būti reguliuojamas, dažniausiai nuo maždaug 30 laipsnių iki 120 laipsnių) yra plokštumos xOy mastelis. Tarsi ant kiek matysis daugiau objektų iš šonų ir apačios/viršaus, jeigu nepadalinus jų x ir y koordinates iš z reikšmės. Kuo didesnis Field of View (FOV), tuo daugiau objektu vienu metu telpa scenoje. Teoriškai FOV gali buti nuo 0 iki 180 laipsnių. Jeigu FOV bus pora laipsniu tai bus tas pats kas žiūrėjimas į 3D pasaulį per mažą skylutę ir tą vaizdą išplėtus ant viso ekrano. O jeigu FOV bus apie 175 laipsniai, tai gali matytis beveik visas pasaulis, o FOV 180 laipsnių reikštų, kad matosi visas 3D pasaulis. Todėl greičiausiai, skirtinguose žaidimuose ta FOV reikšmė gali buti visai ne tiek kiek rašo... Ir pavyzdžiui 3D žaidime padarius FOV 170 laipsniu, ten gali būti tiesiog, labai pakeistas xOy plokštumos mastelis, kad matytųsi daug objektų vienu metu, bet visas tas Field of View iš tikro yra releatyvus. Bet galiu klysti.
- Dabar pereisiu prie svarbiausios dalies. 3D žaidimuose yra perspektyva ir matomi objektų šonai visų objektų kurie yra kokiu nors atstumu nuo ekrano centro.
- 3D renderinime yra svarbus vektorius, vadinamas žiūrėjimo vektorium arba View vector. View vectorius išeina iš taško O plokštumos xOy, kuri yra ekranas. Tada spindulys išėjęs iš taško O susikerta su 3D objekto trikampiu ir surandama to spindulio ir trikampio plokštumos susikirtimo koordinatės (x; y; z). Susikirtimo kordinates galima surasti kaip aprašyta čia:
- https://lt.wikibooks.org/wiki/Matematika/Tiesė_ir_plokštuma
- Nuorodoje aprašyta kaip surasti tiesės ir plokštumos susikirtimo koordinates (kaip surasti susikirtimo tašką tiesės ir plokštumos).
- Todėl visa 3D scena pradedama renderinti sukinėjant View vektorių žemyn ir aukštyn kampu nuo 0 laipsniu (apačia) iki 180 laipsnių (viršus) ir sukinėjant į kairę (nuo 0 laipsnių) ir į dešinę (iki 180 laipsnių). Pavyzdžiui, iš pradžiu imama, kad vertikalia kryptim View vektorius iš taško O (kuris yra plokštumos xOy centras - ekrano centras) pasukamas 1 laipsniu (beveik pati apačia) vertikalia kryptimi ir 1 laipsniu horizontalia kryptimi (beveik pati kairė). Toks View vektorius pataikys į 3D objekto trikampį esantį kairiame apatiniame kampe. Taip bus rastas šio View vektoriaus ir to 3D objekto trikampio susikirtimo taškas. Pagal tą objekto tašką bus rastas to objekto teksturos uždėjimo taškas (kuris bus atitinkamai suprojektuotas ant to 3D objekto ir taps galiausiai pikseliu su spalva ant xOy plokštumos - ant monitoriaus ekrano).
- Taip po truputi didinant pasisukimo kryptis View vektoriaus vertikalia ir horizontalia kryptimi bus nurenderinta visa scena 3D pasaulio, x ir y koordinatės visų 3D objektų verteksų jau padalintos iš jų z koordinatės. Taip bus gautas 3D nurenderintas pasaulis su perspektyva ir tai bus galutinis rezultatas - kaip 3D žaidimuose matomas vaizdas.
- Daug skaičiavimo resursu atimanti dalis yra patikrinti, kurie trikampiai bus pataikyti View vektoriumi tinkamu metu. Todėl jau jei pasukus View vektorių į šoną ir/ar į viršų tam tikrais kampais, kad paskui surasti to View vektoriaus susikirtimo tašką su kokiu nors 3D objektu, reikia patikrinti visus to 3D objekto trikampius kol bus surastas tinkamas trikampis su kuriuo bus susikirtimas View vektoriaus (tiesės ir plokštumos (trikampio)). Čia jau reikia daryti visokius optimizavimus, kaip pavyzdžiui, jeigu buvo rastas susikirtimo taškas View vektoriaus ir kokio nors trikamio (to 3D objekto), tai daug šansu, kad Po View vektoriaus naujo pasukimo jis vėl susikirs su tuo pačiu trikampiu. O jei nesusikirto su tuo trikampiu po naujo View vektoriaus pasukimo, tai daug šansu, kad susikirs su gretimais trikampiais to trikampio (į kuri pateikė prieš tai).
- Update 1. Texturos projektavimą į trikampį galima pagreitinti apskaičiavus kampą tarp tekstūros, kuri yra faktiškai plokštuma ir xOy plokštumos, kuri yra monitoriaus ekrano plokštuma. Ta tekstura turi gulėti ant trikampio. Tai reikia, žinant trikampio viršūnes (3 verteksus), sudaryti trikampio plokštumos lygtį. Kaip tai padaryti aprašyta čia:
- https://lt.wikibooks.org/wiki/Matematika/Plokštuma
- Kai trikampio plokštumos lygtis jau yra, reikia surasti kiek trikampio plokštuma pasukta nuo ekrano xOy plokštumos aplink Oy ašį ir kiek pasukta aplink Ox ašį. Tada apskaičiuoti tų kampų kosinus. Pagal šią schemą nesigaus tikslaus texturos uždejimo, kad 100 procentų atitiktų perspektyvą, bet taip bus daug greičiau. Tai neatitiks visiškos tikrovės, nes tolimiems daiktams tarkim 10 pikselių reiškia 100 metrų, o artimam daiktui 10 pikselių reiškia 1 metrą. Tai tas pagreitintas metodas yra apskaičiuoti trikampio viršunių vidutinis atstumą ir pilti pikselius iš tekstūros ant trikampio.
- Visiškai teisingas ir greitesnis metodas, kuris irgi paremtas šiuo budu būtų tolinti teksturos pikselius priklausomai nuo z koordinatės to trikampio ant kurio bus dedamas teksturos taškas.
- Bet visų pirmą reikia teksturos x ir y koordinates padauginti iš minėtų kampų kosinusų. Iš pradžių, žinoma, dar prieš tai tekstūros projektavimas į visą objektą vyksta taip, kad apskaičiuojami kosinusai kampų pasukimo kiekvieno trikampio aplink Ox ir Oy ašis, jei manyt, kad plokštuma xOy yra plokščia tekstura, kuri projektuojama ant nelygaus ar, pavyzdžiui, apvalaus objekto. Tada pagal tai kiek pasuktas objekto trikampis (palyginsu su xOy teksturos pokštuma) aplink tarkim Ox ašį, tai kiekvieno teksturos taško koordinated reikia dauginti iš trikampio pasukimo kampo kosinuso aplink Ox ir Oy ašis teksturos koordinačių sistemoj (jei manyt, kad tekstura guli ant xOy plokštumos, o z koordinatė yra gylis). Pavyzdžiui, jei objekto trikampis nuo teksturos pasuktas 30 laipsnių aplink Ox ašį, tai teksturos y koordinates reikia dauginti iš cos(30) = 0.8660254.
- Tada toliau reikėtų dauginti teksturos koordinates x ir y iš kitų kampų kosinusų - kampų, nusakančių pasukimą trikampio plokštumos aplink monitoriaus ekrano Ox ir Oy ašis. Bet kaip sakyta, reikia dar dalinti teksturos koordinates iš trikampio taško z koordinatės ant kurio bus teksturos taškas. Tai iš esmės vis tiek kiekvienam tekstūros taškui reikia žinoti trikamio z koordinatę (gylio koordinatę nuo kameros - monitoriaus ekrano).
- Beje, renderinti be teksturu daug lengviau. Tada duodamas objektui ar jo daliai koks nors atspalvis ir apšvietimas. Apšvietimas daromas labai paprastai. Dauginamas trikamio normalės vektorius (trikamio plokštumos normalė) iš šviesos šaltinio vektoriaus (šviesos krypties vektoriaus) skaliarine vektorių sandauga. Pavyzdžiui, jei šviesos vektorius (0.5, 0.70710678, 0.5) (jo ilgis lygus 1), o trikamio normalės vektorius yra (0, 0.8660254, 0.5) (jo ilgis lygus 1), tai apšvietimo lygis viso trikampio bus
- 0.5 * 0 + 0.70710678 * 0.8660254 + 0.5 * 0.5 = 0.862372431992212.
- Tada trikampio spalva (RGB spalvos vektorius) dauginama iš 0.862372431992212. T. y. trikampio spalva po sandaugos gali būti nuo 0 iki 1. Dar galima padauginti iš šviesos intensyvumo.
- Todėl Super Mario žaidimuose, kur mažai teksturų, viskas labai greit laksto ir su daug poligonų.
- Po gilesnio mąstymo, atrodo radau būdą kaip pagreitinti tekstūros projektavimą ant trikampių ir kuris būtų 100 procentu teisingas, su teisinga perspektyva teksturos uždėjimo atžvilgiu.
- Reikia apskaičiuoti teksturos pasuktos 3D pasaulyje taip kaip jinai turi būti projektuojama ant 3D objekto trijų jos galų koordinates. Toliau esantys teksturos galai-kraštai (kurių yra 4) nuo kameros bus toliau ant monitoriaus ekrano, nes ju x ir y koordinatės ant xOy plokštumos bus padalintos iš jų z (gylio - tolumo) koordinatės. Be kita ko reikia, kad pagal teksturos rezoliucija butu dar dvi papildomos pixelių koordinatės kokiame nors viename kampe. (x; y; z) koordinatės iš viso trijų gretimų teksturos pixelių, pavyzdžiui, dešiniame apatiniame teksturos kampe, kai tekstura pasukta prie objekto taip, lyg būtų pasiruošta projektuoti ją ant objekto (galima sakyti, kad artimiausias 3D objekto taškas nuo teksturos, liečia tą teksturą), kai žiurima iš kameros, iš xOy plokštumas, arba kitaip sakant, iš monitoriaus ekrano. Tai va, reikia apskaičiuoti atstumą nuo pačio dešiniausio apatinio teksturos pikselio (A) iki antro nuo dešinės apatinio teksturos pikselio (B). Ir paskui dar apskaičiuoti atstumą nuo pikselio B iki trečio nuo dešinės pačio apatinio pikselio (C). Atstumas AB bus didesnis nei BC dėl perspektyvos. Tada reikia atstumą AB padalinti iš atstumo BC. Tai bus žingsnis. Tiesa sakant, reikia atimti taško B x koordinate iš taško A x koordinatės bei taško C x koordinate atimti iš taško B x koordinatės. Tada tą trumpesnį atstumą padalinti iš ilgesnio atstumo. Šį santykį pavadinkime X. Analogiškai reikia padaryti su pikselių A, B, C y koordinatėmis ir pažymėti gautą santykį per Y.
- Toliau, kai teksturos, parengtos projektavimui ant 3D objekto (bet, kurios koordinatės x ir y nepadalintos iš pixelio z koordinatės (nes ji yra nežinoma)), ketvirto nuo dešinės apatinio taško (D) x koordinatė dauginama iš gauto santykio X^3 arba X kubu (skaičius X gaunamas, padalinus taško A ir taško B x koordinačių skirtumą iš taško B ir C x koordinačių skirtumo). Nes Taško A x koordinatė yra x; taško B x koordinatė yra x*X, taško C x koordinatė yra x*X*X, taško D x koordinatė yra x*X*X*X (X < 1). Analogiškai daroma su taškų, B, C, D y koordinatėmis. Tokiu budu, kiekvienas teksturos plokštumos taškas turės perspektyvą (tolimesni šios teksturos taškai bus arčiau centro O plokštumos xOy, nei plokštuma butų projektuojama ant ekrano be perspektyvos arba, kas tas pats, nepadalinus jos x ir y koordinačių iš kiekvieno teksturos taško z koordinatės). Be tekstūros perspektyvos, tekstura galėtų patrumpėti tik dėl pasukimo kampo aplink Ox ir/ar Oy ašis (plokštumos xOy, kuri yra monitoriaus ekranas).
- Tai va, pagal tai kokiais kampais teksturos plokštuma pasukta aplink Ox ir Oy ašis, reikia padauginti kiekvieną tos teksturos x ir y koordinatę 3D pasaulyje iš tų pasukimo kampų kosinusų. Pavyzdžiui, jei aplink Ox ašį tekstura pasukta 40 laipsnių, tai jos visas taškų y koordinates reikia padauginti iš cos(40) = 0.766044443118978. Ir pavyzdžiui, jeigu aplink Oy ašį tekstura (teksturos plokštuma) pasukta 50 laipsnių, tai tada visas jos x koordinates reikia padauginti iš cos(50) = 0.6427876.
- Tiksliau, tai ne teksturos taškų koordinatės 3D pasaulyje, o tiesiog texturos koordinatės, kai tekstura guli ant plokštumos arba ant stalo, vaizdžiai tariant. Tada, kad gauti pasuktos teksturos koordinates 3D pasaulyje, reikia prie jau gautų naujų teksturos x ir y koordinačių pridėti teksturos centro x ir y koordinates 3D pasaulyje. Vadinasi, tekstura ant stalo turi turėti neigiamas ir teigiamas Ox ir Oy koordinates. Pvz, nuo -512 iki +512 Oy ašimi ir nuo -1024 iki +1024 Ox ašimi.
- Taigi, kai tekstura jau po šitų veiksmų turi x ir y koordinates 3D pasaulyje, kaip aprašyta aukščiau, reikia surasti kuris teksturos kampas (kurių yra keturi) yra arčiausiai xOy plokštumos (arčiausiai kameros arba ekrano). Tada sakykime, jei tas kampas yra dešinysis apatinis teksturos kampas, kuris yra taškas A, o x koordinatė yra x*1, tai taško B koordinatė bus x*X, taško C kordinatė bus x*X*X, taško D kordinatė bus x*X*X*X.
- Taip pat turi būti su tašku , kuris turi buti, jei padėjus tekstura ant stalo, dešinys ir antras nuo apačios taškas (antras į viršų nuo taško A ir pats dešiniausias). Tada taškas turi buti pats dešiniausias teksturos taškas ir trečias nuo apačios. Taško y koordinatė atimama iš taško A y koordinatės ir gaunamas skaičius . Vėliau atimama Taško y koordinatė iš taško y koordinatės ir gaunamas skaičius (ar atstumas) . Skaičius dalinamas iš skaičiaus ir gaunamas santykis Toliau Taško y koordinatė dauginama iš Y ir tampa y*Y. Taško y koordinatė tampa y*Y*Y. Parinkus teksturos ketvirtą nuo apačios patį dešiniausią teksturos tašką ir pavadinus jį , jo y koordinatė turi būti padauginta iš Y*Y*Y. Po sandaugos teksturos taško koordinatė 3D pasaulyje bus y*Y*Y*Y (Y < 1).
- Jei paimti, tarkime, nuo dešinio apatinio teksturos kampo tašką, kurio x koordinatė yra 4, skaičiuojant nuo to kampo, o y koordinatė yra 5, tai šis taškas T turės koordinates 3D pasaulyje (x*X*X*X; y*Y*Y*Y*Y). Čia koordinatės (x; y) yra taško A koordinatės.