Egy kicsit ugyan eltűntem, de élek még. Sajnos sok a dolog, vizsgaidőszak is van, de ha jön a nyár, remélem sikerül beindulni :)
2012.04.23. 08:30 Pretender
S lőn sötétség...
Jelenleg 2 directional és 4 point light van a "jelenetben", mindkettő directional árnyékot vet (egyik gyengébb). Később majd a point lightok is árnyékolnak majd. Valamikor... :)
A CSM technikáját vettem alapul, csak fixen 1 splitre, így viszont mindig a lehető legoptimálisabb shadowmapet kapom (ha mindent jól csináltam). Még egy kis blur kell majd az árnyékokhoz, de lesz egy ilyen változat is, kicsit gyengébb gépekhez
Szólj hozzá!
2012.04.11. 19:50 Pretender
Fallback
A valamivel újabb és normálisabb (azaz ATi vagy nVidia) videókártyákon jól megy a deferred shadinges megjelenítés, azonban régebbi, vagy kevésbé normális (Intel) kártyákon annyira nem szuperált, ezért elkezdtem azon dolgozni, hogy minél szélesebb körben futtatható legyen a program.
Bevezettem 3 renderelési módot: Deferred, Forward, Fixed function. Ezek között majd a menüben is lehet kézzel állítani, azonban vannak bizonyos kritériumok, amiknek meg kell lennie ahhoz, hogy az adott mód használható legyen.
1.: Deferred Shading:
Ahhoz, hogy lehessen Deferred Shadingünk, szinte az összes számomra szükséges extensionnek szerepelnie kell:
- VBO
- FBO
- GLSL Shader
- MRT
ha ezek bármelyikét nem támogatja az adott videókártya, akkor nem lehet deferred shadingünk.
2.: Forward Shading:
Ennek hasonlóak a feltételei, mint a Deferred Shading esetén, csupán az MRTre, azaz a Multi-RenderTargetre nincs szükség (OpenGLben annyit jelent, hogy a csatolható COLOR_ATTACHMENTek száma < 2)
3.: Fixed Function:
Ha se Shader, se FBO, se MRT nincs, akkor nincs más választásunk: Fixed Function kell renderelni. Ekkor nincs normal mapping sem, csupán maximum 8 db fény, sima diffuse shadinggel.
Itt már le van kezelve a VBOs és a VBO nélküli renderelés.
A fixed function animációról annyit, hogy nem áll rendelkezésre GPU skinning (tudtommal), hiszen nincs shaderünk, ezért a CPUn kell az animálást elvégezni. Ugyan azt csináljuk, mint a GPU esetén, annyiban más csak, hogy a VBO (már ha van) feltöltése után nem törölhetjük az adatokat (Vertexek listáját), hiszen azzal fogunk dolgozni. Sőt, még egy másolatot is kell készíteni róla, mert az eredeti vertexet akarjuk a megfelelő mátrixszal transzformálni.
Összegezve: eddig egész jól ment, de meg kell találni azért az arany középutat. Túlságosan nem akarok majd belemenni a fallbackelésbe, aki játszani akar majd, annak legyen is hozzá gépe. Vagy csak szimplán ne Intel kártyával akarjon bármit is csinálni :)
Szólj hozzá!
2012.04.07. 23:09 Pretender
UDP helyzet
Visszaraktam az UDP hálózatot közben, azonban voltak vele gondok (például 3-4 kliensnél hébe-hóba elszállt, meg ilyesmi - a free.c, heap memóriára hivatkozva), azokat orvosoltam.
Eddig úgy volt, hogy minden egyes packet érkezésekor és küldésekor nyomok egy new Packet(...)-et, ami ráadásul létrehoz egy új tömböt (data = new unsigned char[...]), majd ha fel lett dolgozva, akkor delete (és delete [] data). Valószínűleg ezért szállt el a progi :)
Először placement new-os megoldást nézegettem, azután sétálgatva az utcán majd' a fejemhez csaptam, hogy a legegyszerűbb miért nem jutott eszembe:
Mindegyik címhez tartozik 3 lista (vector), amikben Packet*-eket tárolok. Amikor hozzáadunk egy célállomást (azaz szerver esetében az x. kliens, kliens esetében csak 1x: a szerver), akkor ezekből lefoglalok előre memóriát: mindegyik vectort átméretezem (*.resize(INITIAL_SIZE), ahol az INITIAL_SIZE most 50), illetve mindegyik packetet maximális méretűre hozom létre (ami 250 + 7 byte). Ezzel számolgatva kicsit, egy kliens 257*3*50 = 38550 byte, ami ~ 37,6kb. Ami azért nem túl sok :)
Innentől se push_back, se erase, csak a counterek növelése, nullázása, és a megfelelő értékek állítása. És működik. Most már tényleg! :)
Egyetlen hátránya van még: kicsit sok a keresgélés. Amikor jön egy packet, akkor a cím alapján meg kell keresnem az adott "célállomást". Amikor reliable packet érkezik, akkor meg kell keresnem, hogy volt-e már fogadva. Ehhez kapcsolódóan amikor választ akarok generálni, meg kell néznem, hogy már generálva lett-e. Pár ilyen van még, ez most a leggyengébb része, amúgy egész jól megy (nyilván csak ilyen 10 kliensig tudtam kipróbálni, ráadásul még csak local network)
Apropó, válasz: amikor ugye reliable packet érkezik, akkor kell rá egy nyugtázó üzenet, hogy "oké tesó, megkaptam, ne küldjed többet!". Ezeket egyelőre külön packetekben küldöm el, viszont meg lehetne csinálni egy olyasfajta "bitfield"-el, hogy a packetek headerjéhez még hozzácsapom, így ha már 1db packet átmegy, akkor abban benne lennének azok, hogy melyik reliable packeteket kapta meg, így kevesebbszer küldené el "fölöslegesen" ugyan azt -> kevésbé terhelné a hálót. No de ez később. :)
Szólj hozzá!
2012.03.24. 09:18 Pretender
PostProcess
Végre valahára volt egy kis időm foglalkozni a cuccal. Talán hirtelen csak ennyi :(
A PostProcessinget tettem most vissza, egy elég egyszerű formában. Mint a photoshopban, itt is "layerek" vannak, amik egymásra épülnek, tehát fontos a hozzáadási sorrend, hiszen olyan sorrendben futnak le az effektek. Az első bemeneti textúrája a deferred shadingből kapott végleges textúra, a második effekt bemeneti textúrája az előző kimenete, stb. Természetesen a deferred shadingből elérhető az összes többi textúra is, ha szükséges (depth, normal, light)
Első körben csak egy tesztet csináltam, egy kezdetleges radial blur lévén:
Szólj hozzá!
2011.12.04. 11:48 Pretender
Háló 2
És igen, tényleg működik! :D Jöhet a 'játék' ráépítése. Megy a kliens csatlakozás, kirúgás, timeout, mindenféle :) Itt van két egyszerű képecske. Az IP azért ugyan az, mert ugye lokálisan teszteltem, de tegnap már megnéztük, mennek a csomagok rendesen :)) Szóval csudijó!
5 komment
2011.12.02. 07:44 Pretender
Háló
És igen! Működik az UDP-s hálózat!
Az alap csomagkezelés nyilván nem bonyolult, de ha csak annyit csinálunk, hogy ontjuk ki magunkból az adatokat, az nem lesz túl jó, hiszen számolni kell:
- adatcsomag vesztéssel (packet lost, ha jól rémlik valami 1-5%)
- nem biztosított az adatok átviteli sorrendje
Tehát könnyen előfordulhat az, hogy küldünk egy chat üzenetet, és az mégsem érkezik meg (mert pont az elveszett), vagy 2 inputot, csak amit hamarabb küldtünk az később érkezik meg, ami elég érdekes eredményeket is adhat.
Erre készült el az a megoldás, hogy:
Minden egyes csomagot ellátok egy headerrel.
- Az első egy protocol id-nek nevezett dolog, ami a csomagok érkezésekor megnézi, hogy egyezik-e az általunk megadott id-vel. Így, hiába kapunk csomagokat máshonnan, ha a protocol id nem egyezik, akkor eldobjuk.
- A második adat egy azonosító (seqence id). Ha reliable (azaz biztosítani szeretnénk, hogy átmegy) a csomag, akkor az id-jét egy tömbből kiválasztom, ha nem reliable, akkor az id = 0.
- Elraktározom továbbá az időt, hogy mikor lett kiküldve. Az időt én tickenként számolom, 1 másodperc alatt 60 megy le. Egy unsigned longban tárolom el, aminek az értékkészlete [0;4294967295], ami után ha számolgatunk egy kicsit, akkor jó sok napig elég, hogy ne forduljon át :)
Ezt az időt használom a nem reliable (azaz 0 seq. id-jű) csomagok azonosítására. Ha az érkezett csomag headerjében lévő idő < mint amit utoljára kaptunk attól a címtől, akkor eldobjuk, hiszen régebbi csomagokkal nem akarunk foglalkozni. Ez az idő szerinti eldobás nem vonatkozik a reliable csomagokra.
A packet header nagyjából ennyi, nyilván mellérakom még továbbá a szükséges adatokat egy unsigned char tömbben, és kiküldöm. Ha a packet reliable, akkor elrakom egy listába, hiszen addig újra kell küldenünk az adott cím(ek)nek, amíg nem kapunk róla értesítést, hogy megkapta(k). Az értesítések küldése pedig úgy működik, hogy ha kapunk egy csomagot valakitől, és az reliable (a seq. id-ből ugye meg lehet állapítani), akkor létrehozok egy reply üzenetet, aminek a data mezőjébe beleírom az érkezett packet seq. id-jét. Ez alapján fogom tudni, hogy melyiket nem kell tovább küldeni.
Ha érkezik egy ilyen reply típusú üzenet, akkor a data mezőjében tárolt azonosító alapján kiválasztom az újraküldendő reliable üzenetek listájából a megfelelőt, majd törlöm belőle. Tehát reply-t is mindaddig küldünk, amíg reliable packetet kaptunk.
Röviden, tömören ennyi, javítgatni még sokat fogok rajta természetesen, és még csak helyi hálón lett letesztelve, de reményeim szerint ugyan úgy működni fog interneten keresztül is :)
Szólj hozzá!
2011.11.27. 19:22 Pretender
Linear Depth
Gondolkoztam kicsit, és mivel úgy voltam vele, hogy úgyis kelleni fog a lineáris depth a CSM miatt, keresgéltem kicsit a neten. Sokkal kényelmesebb lineáris (view space) depthet eltárolni, még visszaszámolni is olcsóbb.
Kell egy sugár a kamera pozíciójából a vertex pozíciójába (ez worldspace), ezt normalizáljuk a pixel shaderben, szorozzuk a kivett depth értékkel és hozzáadjuk a kamera pozícióját. Tehát valami ilyesmi:
//vertex shader
void main(
in out float4 Position : POSITION0,
out float4 ScreenPos : TEXCOORD0,
out float3 EyeRay : TEXCOORD1)
{
float4 wpos = mul(Position, world);
Position = mul(wpos, viewProj);
ScreenPos = Position;
EyeRay = wpos.xyz - eyePosition;
}
//pixel shader
float depthText = tex2D(samplerDepth, Texcoord);
EyeRay = normalize(EyeRay);
float3 wposition = eyePosition + EyeRay * depthText;
Emellett a lineáris depth pontosabb távolabbi objektumokra is, így például egy diablo2-nézetes játéknál érthető, hogy sokkal pontosabb eredményt ad egy lineáris depth, mint egy exponenciális (ami a position.z / position.w-ből jön)
Emellett elkezdtem szórakozni a CSM-el is, max. 4 splitünk lehet, és egyetlen textúrába gyűjtöm bele a külön textúrákat. Tehát ha van egy 512x512-es split méretű shadow mapem, akkor lesz egy 1024x1024-es rendertargetem, amibe a 4 split textúráját belerakom, így csak ezt kell átadni az árnyékolást végző shadernek, ami a távolságból meghatározza, hogy melyik splitbe tartozik az adott pixel.
Képek akkor lesznek, ha már jó lesz, és működni is fog rendesen :D
Szólj hozzá!
2011.10.20. 21:05 Pretender
Winapi Winforms
Asylum kódja alapján kitaláltam, hogy most nekem editort kéne már csinálni, mert kényelmetlen konzolból "fordítgatni" a modelleket. Szóval elkezdtem WinApi segítségével különböző controlokat megvalósítani, egyelőre csak menü és gomb van, de ez most már könnyen bővíthető, sikerült pár osztályt csinálni, ami egész jól elfedi, és már majdnem annyira könnyű kód szinten használni, mint a c#-os .net-es WinForms-os cuccot.
Még van egy kis hibája a dolognak. Egy olyasfajta event handlert valósítottam meg függvénypointer segítségével. Ami a gond ezzel, az az, hogy az egyik osztályból akarom kvázi meghívni egy másik osztály metódusát. Ami megy, csak így a másik osztály adattagjait nem tudom használni. Két megoldás lehetséges, ezek (és maga a probléma is) ismertetve itt vannak: http://yscik.com/jf/forums.php?m=posts&p=167223#167223
11 komment
2011.10.17. 18:13 Pretender
Részecskerendszer 1. felvonás
Szintén egy Node3D lett a Particle Emitter osztály is, így - mint minden Node3D-t - ezt is lehet Node3D-hez kapcsolni (most egy csonthoz van éppen). Még fejleszteni kell, pl. legyen lehetőség nem additive blenddel kirajzolni részecskéket, illetve valami script-szerű irányítást szeretnék hozzá csinálni (jelenleg kódból kell vezérelni, de lehet, hogy ezt csak később csinálom meg, majd ha lesz grafikus, és szeretne részecskerendszereket csinálni :D)
Itt egy videó az egészről:
Szólj hozzá!
2011.10.15. 15:32 Pretender
Deferred shading újra
Visszaraktam a kódba a deferred shadinget, próbálok majd még optimalizálni rajta. :)
A mostani jeleneten van 3 irányfény (egy fő, és két derítés, csak mivel nem vagyok grafikus, így én nem tudom, hogy hogy kell beállítani ahhoz, hogy szép legyen :)), illetve egy pontfény is van, mégpedig (az előző bejegyzésben írtam a Hiearchiáról) a karakter "kézcsontjához" kapcsolva, így ahogy mozgatja a "kezét", úgy mozog vele együtt a fény.
Lesz még később árnyék, meg postprocess effektek, részecsekrendszer (mindez már meg van írva, csak kicsit szépíteni kell őket, átrakni az új környezetbe, stb.)
Szólj hozzá!
2011.10.13. 11:30 Pretender
Hiearchia
Néztem videót az Unity editoráról, és már régebben is gondolkoztam rajta, de így gyakorlati hasznát is láttam annak a dolognak, hogy mindenféle 3d-s objektumot lehet bármilyen, szintén 3d-s objektumhoz kapcsolni. Ezt csináltam most meg.
Van egy ősosztály, a Node3D, ami rendelkezik Position, Scale, Rotate 3d-s vektorokkal, és egy World mátrixszal. Emellett Update, UpdateChildren és UpdateAsChild metódusokkal. A memberek egyértelműek, a metódusok:
Node3D::Update(): itt döntöm el, hogy az adott Node-t transzformálni kell-e... 3 lehetőségünk van:
- van egy kötelező transzformálás, ha mondjuk éppen hozzá lett adva egy másik node-hoz, vagy elvéve, ilyesmi esetek (HaveToTransform)
- egy lehetséges eset az, hogy eltárolom az előző frameben lévő pozíciót, forgatást és skálázást, ezeket összehasonlítom, és ha valamelyik nem egyezik meg, akkor transzformálva lett az adott node
- a harmadik lehetőség az, hogy ha az előző kettő nem igaz, akkor megnézzük, hogy van-e szülője. Ha van, és az volt transzformálva, akkor az adott node is van, egyéb más esetben pedig nincs.
Ez azért jó, mert nem kell így mindent mindig újraszámolni. Ami azért lássuk be... kíméli a cpu-t :)
Miután eldöntöttük, hogy transzformálni kell-e, minden osztálynak jön a saját Update metódusa, ahol kiszámolja a world mátrixát (meg amit akar).
Először meghívom az összes Node Update-jét, így meglesznek a world mátrixok. Ezután azoknak a Node-oknak, amiknek nincs szülője, meghívom az UpdateChildren() metódusát, ami frissíti a gyerekeket (ami igazából meghívja az UpdateAsChild() metódust), ahol minden egyes gyerek a saját maga által definiált metódusban frissíti önmagát. Hülyén hangzik, de pl. Entity esetén ez: world = world * parent->world
Nagyjából erről lenne szó. Régen az volt, hogy csak Entity-ket tudtam egymáshoz kapcsolni, de most már, ami Node3D-ből öröklődik, és le vannak implementálva a megfelelő metódusai, azt lehet egymáshoz kapcsolni. Így pl. egy animált modellt egy animált modellhez, az animált modell egy csontjához pedig egy sima modellt. De akár részecskerendszert, fényt, bármit lehetne, csak az még nincs kész :)
Itt is egy kép az előbbiekről, a középsőhöz van hozzácsatolva a másik, mind a kettő forog, mozog, és a középső egyik csontjához hozzá van csatolva egy Entity (ami most éppen ugyan olyan "karakter"). A modell elég vicces, de programmer art-ként, place holdernek szerintem tökéletes :D
Szólj hozzá!
2011.09.30. 21:52 Pretender
Kiesés
Elkezdődött az egyetem, feljöttem Pestre, csináltam ezt-azt, járkáltam, ilyen-olyan tevékenységek voltak, stb.
Szóval volt itt mindenféle, ami miatt most volt egy nagyobb kiesés, de ha minden jól megy, akkor a csütörtököm vagy teljesen, vagy félig szabad lesz, így akkor tudok majd foglalkozni a kódolással is (remélhetőleg :)).
Szólj hozzá!
2011.08.26. 11:26 Pretender
Multithreading
Egy-két napja bekerült egy kis többszálúsítás a programba. Semmi bonyolult, egyelőre csak a betöltésnél használom: az új szál végzi magát a betöltést, a fő szál pedig halad tovább, és rajzol. Egyelőre mutex-szel (CRITICAL_SECTION-nel csináltam, Asylum segítségével, thanks) írok egy változót a betöltés elején és végén, ez alapján nézem meg a Draw-ban, hogy épp' betölt-e, vagy sem.
Bár ez így nem feltétlen optimális (minden egyes körben egy feltételvizsgálat: bár nem nagy szám, de akkor is...), így lehet, hogy az lesz, hogy amint a loading flag false lesz, hozzáadok egy új Screen-t, aminek átadom a betöltött Scene-t és társait, és az már magának a játéknak a rajzolását és frissítését végzi.
Szólj hozzá!
2011.08.23. 13:14 Pretender
Ennyi?
Jelezték számomra, hogy talán nem kellene ezzel az időmet tölteni (és talán igény sincs rá), így visszatérek az engine blogolására. :)
De ha valakinek miattam sikerül egy jó kis windowsos ablakot létrehoznia, akkor már megérte :D
Szólj hozzá!
2011.08.23. 11:55 Pretender
Tutorial sorozat 01 - második felvonás
Hozzunk létre egy új fájlt, a neve legyen ugyan az, csak ne header legyen, hanem C++ file (.cpp)
Szólj hozzá!
2011.08.21. 22:52 Pretender
Tutorial sorozat 01
Üdvözlöm a sorozat első leckéjének kedves olvasóját! :)
Ebben a néhány tutorialban szeretném bemutatni teljesen az alapoktól, egészen a 3D-s megjelenítésig a DirectX-et, c++ nyelven programozva.
Néhány apróság a kezdés előtt:
- fel kell telepítenünk először is valamilyen programozói környezetet (én visual studio 2008at használok, ebből is van ingyenes változat, az express)
- szintén telepíteni kell a DirectX SDK-t, amit szintén ingyenesen letölthetünk a netről (én konkrétan a 2008 novemberit használom)
- ha frissen telepítettük mindkét rendszert, akkor a Visual Studio-ban be kell állítanunk a DirectX SDK megfelelő mappáit (ha az nem tette volna meg): Tools -> Options -> Projects and Solutions -> VC++ Directories -> itt az Include és Library fájlok beállítása szükséges, ami nálam:
C:\Program Files\Microsoft DirectX SDK (November 2008)\Include
és
C:\Program Files\Microsoft DirectX SDK (November 2008)\Lib\x86
- egy alap c++ tudás azért szükséges, de bevallom őszintén, én sem igazán ismerem, viszont egyelőre stabilan használom az alap dolgokat, amelyek most elegek is lesznek nekünk :)
A rövidke bevezető után kezdjünk is bele az első leckébe.
Szólj hozzá!
2011.08.05. 10:47 Pretender
Features
Írtam egy rövidke kis feature listát, amit jó lenne teljesíteni. Nyilván vannak magasabb prioritást élvező dolgok, így van amit csak akkor fogok megcsinálni, ha már nagyon ráérek :)
Színkódok:
■: egyáltalán nincs még
■: már van valami, de még fejleszteni kell
■: egyelőre úgy néz ki, hogy "kész" (mert hát soha nincs kész semmi)
Tools:
- Map and Terrain editor ■
- Game object, Material, Physic, Animation editor ■
Game:
- Animation ■
- Pathfinding ■
- AI ■
- Network (UDP, Client-Server) ■
- Camera ■
- Scripting ■
- LOD ■
- Terrain ■
- Keyboard, Mouse and Joystick input ■
- Hang ■
Rendering:
- Diffuse, Diffuse + Specular, Normal, Normal + Specular mapping (Static and Animated) ■
- Special effects ■
- Particle systems ■
- Shadow (CSM) ■
Math:
- Rigidbodies ■
- Ragdoll ■
- Collision ■
- Culling (View Frustum Culling, Occlusion Culling) ■
- Portals ■
- Vehicles (Helicopters, Tanks, Cars, Trucks) ■
GUI:
- Ingame GUI ■
- Tools GUI ■
Mint látszik, van még dolog rendesen. Csak időm is legyen... :) Mostanában nem volt túl sok.
Szólj hozzá!
2011.07.04. 17:24 Pretender
Volt, Van, Lesz
Akkor kezdeném is egy kis helyzetjelentéssel.
Mivel kb. 4-5 hónapja teljesen elölről kezdtem az egészet, és most az utolsó év (és érettségi) miatt nem volt túl sok időm, ezért még nincs túl sok minden. De elég is a kifogásokból, ráadásul itt a nyár, aktivizálom magam :)
Szólj hozzá!
2011.07.04. 16:32 Pretender
Előszó
Először is üdvözlök mindenkit a blogomon!
Hogy miről is van szó?
Szabadidőmben tanulgatom a programozást és a játékfejlesztést, már néhány éve. Szeptemberben kezdem az egyetemet, tehát még idősnek sem vagyok mondható. :)
Először C#ban kezdtem (akkor még konzolos alkalmazásokkal; számkitaláló és társai), később - amikor gondoltam belevágok a játékfejlesztésbe, megjegyzem még programozni csak olyan szinten tudtam, hogy örültem, ha lefordult a kód :) - mellé beröffentettem az XNA frameworköt. Egy jó ideig azt használtam, míg nem úgy döntöttem, hogy megpróbálok megtanulni c++ul. Mivel az alapvető programozással már tisztában voltam (nagyjából :)), nem sok időt töltöttem a konzolos alkalmazásokkal (talán semmi konkrétat nem is írtam benne), rögtön a saját ablak, saját minden mellett döntöttem, és a "ha már lúd, legyen kövér" elvet követve nem is 2Dvel, hanem rögtön 3Dvel kezdtem.
Természetesen azóta már rengeteget tanultam (és ez természetesen továbbra is így lesz), rengeteg (felépítésben is különböző) verzió született már, de most értem el nagyjából azt, hogy az egyre bővülő funkciók ellenére teljes mértékben tudom követni a kódot.
Nyilván nem vagyok AAA fejlesztő, így kicsivel egyszerűbb dologra kell gondolni mindenből, ezért természetesen még csak összehasonlítani sem merem egy "rendes" engine-el (pl. Unreal, CryEngine, Doom3 engine, és társai), de azért talán egyszerűbb játékok készítésére alkalmas lesz. :)
Tehát ez a blog egy jelenleg is fejlesztés alatt álló, C++ nyelven írt, DirectX APIt használó engine fejlődését fogja bemutatni.
U.I.:
Sok segítséget kaptam a jf.hu-n a fórumokon, és msnen is néhány embertől, ezúton is köszönöm nekik! :)