HTML

Az élet kódjai

Csináld maga. Senki nem csinálja meg helyetted.

Friss topikok

Textúra tömbök

2022.05.18. 10:02 Travis.CG

Elkezdtem írni a QBParty-ra a demót. Egy régi ötletet valósítok meg, aminek a lényege, hogy egy csomó fényképet felhasználva animációt készítek. Az ötlet nem új keletű, például a 2003-as Heart Shaped Box is ezen alapult. A különbség az lenne, hogy míg ott a képek időben egymás után jöttek fel, nálam egy időpillanatban több kép egyszerre lenne látható, majd ezeket különböző módon kombinálnám, ahogy egy grafikus programban a rétegeket szokták.

A képek keverésére programozástechnikai szempontból két módszert használhatunk. Az egyikben a CPU-n keverjük össze a pixel adatokat eggyé, majd ezt adjuk át a fragment shadernek textúraként, vagy mindegyik képet betöltjük a shaderbe, és ott összegezzük az információt. Az első módszer egyszerű, csak lassú. A másodikban viszont a GPU-n kombináljuk a képeket, ami gyorsabb, de a megvalósítása nem egyszerű. Természetesen a második megoldást fogom választani. Azonban, mint látni fogjuk, itt is több lehetőségünk van.

Eddig a demóimban a shaderekben a textúrákra egyszerű sampler2D típusú változókként hivatkoztam. Tehát a shader deklarációs részében ilyen sorok szerepeltek:

uniform sampler2D img;
uniform sampler2D mask;

Mivel eddig egy shaderen belül kevés textúrával játszottam, nem is volt szükségem másra. Most viszont 116 textúra egyidejű kezelésére lenne szükségem. Száztizenhatszor beírni, hogy uniform sampler2D még büntetésnek sem utolsó, de nincs is rá szükség, mert használhatunk tömböket.

uniform sampler2D img[116];

 Ez a része egyszerű. A bonyolult, hogy miként mondjuk meg a shadernek, hogy hol találja az adatokat? CPU oldalon a következő kódra van szükség.

GLint starids[116];
for(int i = 0; i  < 116; i++){
  glActiveTexture(GL_TEXTURE0 + i);
  glBindTexture(GL_TEXTURE_2D, texid[i]);
  starids[i] = i;
}
GLint loc = glGetUniformLocation(prgid, "img");
glUniform1iv(loc, 116, starids);

Mennyi textúrát vihetünk át? Ez a GPU függvénye, de lekérdezhetjük a számát a GL_MAX_COMBINED_TEXTURE_IMAGE_UNIT segítségével. Nálam ez 192, szóval a 116-al még benne vagyok a limitben. De ez nem jelenti azt, hogy ezt mind korlátlanul fel is használhatom. A fragment shader ennek a töredékét tudja csak használni. A GL_MAX_TEXTURE_IMAGE_UNIT mutatja meg ezt a számot, ami már csak 32 az én esetemben. (Bár meg kell jegyezni, az NVidia kártyák liberálisabbak, ott következmények nélkül használhattam mind a 116 textúrát, igaz elég lassú lett a végére. Mikor egy Intel UHD 620-as laptopon futtattam ugyan azt a kódot, a hátár átlépése után szabványos hibaüzenetet kaptam.) De még ezt a 32-t sem használhatom tetszőleges programkörnyezetben! Egyes (régebbi) videokártyák nem engedik, hogy cikluson belül használjuk őket. Szerencsére ez nálam nem gond.

De 32 textúra még mindig nem 116. Persze lehet több menetben is renderelni. Először 32 textúrát feldolgozok, majd az eredményt egy másik texturába mentem. Ezt megismétlem pár alkalommal, majd végső lépésben az előzőleg létrehozott textúrákat kombinálom. Ez nyakatekert, lassú, és értelmetlen, különösen, hogy az OpenGL biztosít egy szokatlan 3D textúrát, ahol az egyes rétegek nem keverednek. Ez a tömbtextúra (array texture).

Ez annyival más, mint az előzőleg használt tömb, ahol 32 volt a limit, hogy ott igazából csak az azonosítók voltak tömbbe rendezve. Itt viszont az adatok is, tehát nem váltunk a textúrák között, hanem egy textúrán belül lépkedünk. Akár 2048 textúrát is összefűzhetünk ily módon egy OpenGL 4.5 képes kártyán. Lássuk, hogyan is készül.

glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D_ARRAY, texid);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, width, height, maxindex);
for(i = 0; i < 116; i++){
  image = load_from_file(...);
  glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, width, height, 1, GL_RGB, GL_UNSIGNED_BYTE, image);
  free(image);
}

Mint látható, csak egy textúra készült, egyetlen azonosítóval. Az egyes rétegeket a glTexSubImage3D-vel töltöttem fel. A fragment shaderben a következő módon kell használni:

uniform sampler2DArray image;
...
FragColor = texture(image, vec3(texcoord, level));

Tehát a koordináta három dimenziós lett, kiegészült a tömbindex-el. A módszerrel gond nélkül használhattam az összes textúrát, ráadásul sokkal gyorsabban.

Szólj hozzá!

Címkék: programozás demoscene opengl

A bejegyzés trackback címe:

https://cybernetic.blog.hu/api/trackback/id/tr9617830327

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása