Egy rövid számítógépes grafikát írunk le egy egyszerűsített opengl saját kezünkkel, a 6. cikk 1. cikkében

A probléma megfogalmazása

Ennek a cikksorozatnak az a célja, hogy megmutassa, hogyan írja meg az OpenGL (nagymértékben leegyszerűsítve!) Clone yourself. Meglepő módon gyakran találkozom olyan emberekkel, akik nem tudják leküzdeni az OpenGL / DirectX kezdeti tanulási akadályát. Így elkészítettem egy rövid, hat előadást tartalmazó ciklust, amely után a diákok jó rendereket adnak ki.

Tehát a feladat az alábbiak szerint történik: anélkül, hogy külső könyvtárakat (különösen a grafikusokat) használnánk, hogy ilyen képeket kapjunk:

Egy rövid számítógépes grafikát írunk le egy egyszerűsített opengl saját kezünkkel, a 6. cikk 1. cikkében

Megpróbálok nem haladni 500 sorot a végső kódban. A diákjaimnak 10-20 órányi programozásra van szüksége ahhoz, hogy elindítsanak ilyen renderelőket. A bemeneten textúrát kapunk egy sokszög hálóval + textúrákkal ellátott képekkel, a kimenet egy renderelt modell. Nincs GUI, a program indításakor egyszerűen létrehoz egy képfájlt.

Mivel a cél a külső függőség minimalizálása, a diákjaimnak csak egy olyan osztálya van, amely lehetővé teszi a TGA fájlok használatát. Ez az egyik legegyszerűbb formátum, amely támogatja az RGB / RGBA / fekete-fehér képeket. Vagyis kiindulópontként egyszerűen dolgozhatunk képekkel. Felhívjuk a figyelmet arra, hogy az egyetlen funkció, amely a kezdetén elérhető (a kép betöltése és mentése mellett) képes beállítani egy pixel színét.

Nincsenek funkciók a háromszög szegmensek rajzolásához, ezt kézzel kell írni.

Adom a forráskódomat, amelyet a diákokkal párhuzamosan írok, de nem ajánlom használni, de nincs értelme.

Minden kód elérhető a githubban, itt van a kezdeti kód, amit adok a diákoknak.

output.tga így kell kinéznie:

A Brezenham-algoritmus

Az első előadás célja egy dróthálóval történő renderzés, ennek érdekében meg kell tanulnunk, hogyan húzzuk meg a vonalakat.
Brezenham-algoritmust csak el lehet olvasni. de a hallgatók többségét elakadtuk, ha azonnal átolvassuk az egész változatot, ezért írjuk fel magunknak a kódot.

Hogyan néz ki a legegyszerűbb kód, amely vonalat húz két pont között (x0, y0) és (x1, y1)?
Úgy tűnik, valami ilyesmi:

Pillanatkép kód elérhető a githubon.

A probléma ezzel a kóddal (a hatékonyság mellett) a konstans választása volt, amelyet a .01-es egyenlettel megegyeztek.
Ha hirtelen megegyezik a .1-vel, akkor a szegmensünk így fog kinézni:

Könnyű megtalálni a megfelelő lépést: csak a képpontok számát kell megrajzolni.
A legegyszerűbb (hibás!) Kód a következőképpen néz ki:

Figyelmeztetés: a diákjaim számára ez a kód első hibakezelési forrása a (x-x0) / (x1-x0) egész szám.

Továbbá, ha megpróbáljuk felhívni a kódot itt:

Kiderült, hogy egy sor jó, a második pedig lyukakkal, a harmadik pedig egyáltalán nem.

Lyukak az egyik szegmensben, mivel annak magassága nagyobb a szélességnél.
A hallgatók gyakran ajánlok nekem egy fixet: ha (dx> dy) más.
Nos, karácsonyfa!

Ez a kód jól működik. Ez az a fajta összetettség, amit a renderelés végső változatában szeretnék látni.
Természetesen nem hatékony (többszörös megosztások és hasonlók), de rövid és olvasható.
Ne feledje, hogy nincs benne állítás, nincs ellenőrzés a külföldre menni, rossz.
De megpróbálom nem felborítani ezt a kódot, mert nagyon sokszor olvastam, míg szisztematikusan emlékeztetek az ellenőrzésre.

Így az előző kód jól működik, de optimalizálható.

Az optimalizálás egy veszélyes dolog, ezért tisztában kell lennie azzal, hogy mely platformkód működik.
A grafikus kártya kódjának optimalizálása vagy csak egy processzor teljesen más dolog.
Minden optimalizálás előtt és alatt a kódot profilozni kell.
Próbálja kitalálni, melyik művelet itt a legerősebb erőforrás?

A teszteknél a 3 szegmensből 1000000-szer húztam, amit eddig húztak. Saját processzor: Intel® Core (TM) i5-3450 CPU @ 3,10 GHz.
Ez az egyes képpontok kódja hívja a TGAColor példányt.
És ez 1 000 000 * 3 db * darabonként 50 darab. Néhány kihívás.
Hol kezdjük el az optimalizálást?
A profilozó elmondja nekünk.

A kódot a g ++ -ggdb -g3 -pg -O0 gombbal állítottuk össze; majd gprof futott:

Ne feledje, hogy minden egyes részosztálynak ugyanaz a partíciója van, vegyük ki a hurokon kívül.
A változó hiba megadja az ideális vonal távolságát a jelenlegi pixelünkből (x, y).
Minden egyes alkalommal, amikor egy hiba meghalad egy pixelet, növeljük (csökkentjük) y-t egyenként, és csökkentjük a hibát egyenként.

És miért kell úszó pontokra? Az egyetlen ok az, hogy dx-vel egy osztás és egy összehasonlítás a .5-vel a hurok testében.
Megszüntethetjük a lebegőpontot, ha a hiba változót egy másik hibával helyettesítjük, hívjuk error2-nek, egyenlő a * dx * 2 hibával.
Itt van az egyenértékű kód:

Egy másik beszélgetés, most már elegendő a felesleges másolatok eltávolítása, amikor a funkciót hívják, az elülső szín referenciaként (vagy egyszerűen az -O3 összeállítási jelzővel), és minden készen áll. Nem egyetlen szaporítás, sem egyetlen rész a kódban.
A működési idő 2,95 másodpercről 0,64-re csökkent.

Huzal renderelés.

Most minden készen áll egy vezetékes renderelés létrehozására. A kód és a teszt modell pillanatképe itt van.

A hullámfront obj fájlformátumot használtam a modell tárolására. Minden, ami a renderhez szükséges, az űrlap csúcspontjairól olvasható le a fájlból

v 0,608654 -0,568839 -0,416318
[. ]
ezek a koordináták x, y, z, egy sor a soronként

és a foltok
f 1193/1240/1193 1180/1227/1180 1179/1226/1179
[. ]

Itt érdekli az első szám minden üres, ez a szám a csúcs a tömb, amit korábban olvastunk. Így ez a sor azt mondja, hogy az 1193, 1180 és 1179 csúcsok háromszöget alkotnak.

A model.cpp fájl tartalmazza a legegyszerűbb elemzőt.

Egy ilyen ciklust írunk a main.cpp és a voila felé, a vezetékes renderelő készen áll.

Egy rövid számítógépes grafikát írunk le egy egyszerűsített opengl saját kezünkkel, a 6. cikk 1. cikkében

Legközelebb 2D háromszögeket fogunk rajzolni és megjeleníteni.