# [OpenGL] im shader feststellen, ob textur gesetzt ist



## DarkMo (1. Juli 2012)

hoi, ich will nen "allgemeingültigen" shader basteln. also ich geb dem ne set an texturen mit, und je nachdem, welche vorhanden sind, soll er die dann nutzen.

so, da das irgendwie konfus klingt, kleines bsp ^^
der shader soll zum bsp ne stink normale textur malen und darüber hinaus ne specular map auswerten sowie ne normal map (und wer weis, was vllt noch kommt ^^). nu hat aber vllt ned jedes objekt ne specular map (weils einfach matt is) oder eben keine normal map und das würde ich gerne im shader irgendwie feststellen.

vorüberlegungen:
ich könnte zusätzlich ne flag variable übergeben, die sagt, was vorhanden ist, und was nicht. aber wenn ich das ned muss, würd ich da auch gern drauf verzichten. wenn man quasi im shader sowas machen könnt wie "if(spec_map != NULL) berechne specula krempel;" bla, dann wäre das schon hilfreich. aber ich weis einfach ned, wie ich nach sowas googeln soll, daher is meine ausbeute zu dem thema mehr als mager :/ aktuell hab ich ne "default" textur, die halt jedesma mit übergeben wird, aber sinnvoll erscheint mir das ned. dann eher die flag variable.

naja, vllt weis ja einer von euch, was man da so machen kann


----------



## blubb3435 (1. Juli 2012)

hey, sowas zu schreiben hatte ich auch mal vor (:
und wenn du die normal map und specular map seperat übergibst?


----------



## DarkMo (1. Juli 2012)

das is mein jetziger ansatz, wollt das aber bissl "slot effizienter" bauen. laut meinem dollen buch hier, kann man nur maximal 16 variablen übergeben. un wenn man das ganze als "eine" textur übergibt, spart man da halt ne ganze menge. aber atm hab ich scho nen glas zuviel intus wies scheint, das denken fällt schwer ^^ daher werd ich da heute wohl nix mehr dran bauen ><


----------



## AMD (2. Juli 2012)

Es macht ja keinen Unterschied, ob du eine Default-Texture oder einen Flag übergibst, die Texture ist ohnehin im VRam der Grafikkarte und übergeben tust du ja nur die ID der Textureunit.
Wenn du einen sampler2D erstellst, da aber keine Textur drin ist, dann könntest du halt in den Inhalt checken. Da wäre der RGB Wert bestimmt 0, performant ist das aber nicht...

Die Lösung mit der Flag-Variable sollte schon ziemlich gut sein (würde ich zumindest auch so machen) und ich meine was ist schon eine Variable (bzw. n-Texture Variablen)? Im Vergleich zu den anderen Operation nix.


----------



## DarkMo (2. Juli 2012)

hmm oki.

primär kamen mir beim näheren überlegen nur folgende probleme in den sinn:
- ich hab in meinem schauen buch (super bible 5) nochmal das mit der 3d textur da durchgelesen. man erstellt eine textur mit GL_TEXTURE_2D_ARRAY (statt GL_TEXTURE_2D) und bindet da dann mittels glTexImage3D ein 3 dimensionales bild, was mit NULL als data pointer initialisiert wird. sprich, man baut sich nen großen leeren käfig, mit den dimensionen des bildes (x y). mittels glTexSubImage3D füllt man dann diesen leeren käfig stück für stück mit den einzelnen texturen (die texturkoords s und t werden um die koordinate r erweitert, welche dann quasi den array index repräsentiert).

da is für mich der erste knackpunkt: müssen diese subimages die selbe größe haben (x y) wie der "käfig" oder können sie auch kleiner sein? weil als default textur hätte ich dann einfach ne 1x1 pixel große grafik genommen um platz zu sparen un gut. aber wenn ich nen hochauflösendes bild mit 4x2k pixeln ca habe und diese "sinnfreien" default texturen dann auch so groß sein müssen, obwohl 1px langen würde.... gnaa ^^ aber gut, hier wären die flags sicher gut. wobei ich da auch noch überleg, wie ich das am dümmsten mach. textur, specular map, normal map, alpha map, luminezens(?) map - also die, die sagt wos auch im dunkeln leuchten soll ^^, höhen map? soll wohl für parallax mapping gut sein ^^. jedenfalls lässt sich mit bissl spucke da einiges zaubern und nen vec4 wird eng als flag-variable  müsste man dann vllt ne ganze matrix rüberballern  aber gut, das sollte ja ned der umstand sein.

gut, ich glaube die flags sind noch die beste option.

dann nochmal ne verfahrenstechnische frage. was organisatorisches. atm sieht mein object loader ca so aus (grob umrissen): er speichert alle punkte in ner punktliste, alle normalen in ner normalen liste and so on. dann gibts ne flächenliste, die je 3 vertizes (also ein konglomerat aus punkten, normalen, texcoords und farben) per index speichert (speicherplatz). beim zeichnen geht er dann die flächenliste durch, holt sich laut angegebenem index aus den entsprechenden listen die werte und baut sich das vbo/vao gedöhns zusammen, was er dann malen kann.

so, nun hab ich noch ne texturliste, die ich atm noch sträflich vernachlässigt habe. atm siehts so aus, das jede fläche noch ne texturangabe hat. is sorum aber schwachsinnig, da er dann ja im notfall laufend hin und her switchen muss (statusänderungen der pipeline = effiziensschwund = besser vermeiden). also wenn eine fläche aus der "textur" metall besteht, das nächste aus plastik und dann wieder metall... urgs ^^ daher war meine überlegung, das demnächst so umzubauen, dass die texturliste neben all den anderen listen läuft und ich zu jeder textur eine flächenliste anlege, die wiederrum den flächenindex speichert. dann kann man beim rendern/vao bla erstellen pro textur ein gesamtpaket der zu zeichnenden flächen erstellen und dann alle texturen nacheinander abarbeiten. so wechselt man dann alle texturen nur einmal durch un switcht ned laufen hin und her. natürl nur pro object. ein zweites gleiches objekt würde dann wieder alles durchgehen ^^ aber ich hoffe, das langt erstmal als optimierung.

so, nu müsst ich dann mit dieser 3d textur um dies mir eingans ging ja bei jedem texturwechsel diese textur neu "zusammenmischen". liege ich da a) vllt falsch und das lässt sich viel einfacher lösen oder is das richtig so? zumindest war meine überlegung dann dahin gehend, dass ich zu jeder "texturmischung" - die ich dann material nennen würde - eine eigene klasse baue. jede instanz dieser klasse baut dann quasi einmalig seine mischung zusammen, stellt gleich noch seine ambien/specular/diffuse eigenschaften zur verfügung usw und die textur-id würd ich dann einfach immer durchegeben, wenn was von dem material gezeichnet werden soll. irgendwie so ^^ details muss ich mir da noch überlegen.

soweit so gut. aber wenn ich jetz ne bodentextur hab zum bsp... wasn dann? xD oder einen holzschrank mit plastikgriffen? jetz ma blöde gesagt. die textur bestimmt dann, wo welches material ist, und ned die flächen. seh ich da grad den wald vor lauter bäumen nich? als erster woraround würd mir zwar spontan ne "materialmap" einfallen (greyscale und der grauwert als materialindex oder so), aber wie ich da dann die materialzuornung hinbekommen sollte... am ende müsste man ja aufs geradewohl alle materialien textur-mischungen an den shader übergeben, was aufgrund der übergabe-limitierung da doch ned geht oder?

grad nochma hier nachgelesen: "... with GLSL you can have a maximum of 16 attributes per vertex program." <- wenn man da die punktdaten, texcoords und normalen allein schon hat, + uniforms wie matrizen und texturmischung sowie flag-matrix... da bleibt ned mehr allzuviel über >< mit glück vllt 8 materialien quasi. was aber, wenn man da mehr will ^^ lichtinfos wären auch noch ne geschichte, die ich vergaß. also ich find das limit irgendwie arg knapp ^^ oder versteh ich das grundsätzlich falsch?


----------



## Skysnake (4. Juli 2012)

DarkMo schrieb:


> hmm oki.
> 
> primär kamen mir beim näheren überlegen nur folgende probleme in den sinn:
> - ich hab in meinem schauen buch (super bible 5) nochmal das mit der 3d textur da durchgelesen. man erstellt eine textur mit GL_TEXTURE_2D_ARRAY (statt GL_TEXTURE_2D) und bindet da dann mittels glTexImage3D ein 3 dimensionales bild, was mit NULL als data pointer initialisiert wird. sprich, man baut sich nen großen leeren käfig, mit den dimensionen des bildes (x y). mittels glTexSubImage3D füllt man dann diesen leeren käfig stück für stück mit den einzelnen texturen (die texturkoords s und t werden um die koordinate r erweitert, welche dann quasi den array index repräsentiert).
> ...


<= geht. Du lässt den Rest einfach frei und musst entsprechend deine Textur auswählen. Du kannst ja immer zwischen (0.0,0.0) und  (1.0,1.0) alle Bereiche frei auswählen. Du kannst also auch theoretisch alle Texturen nebeneinander packen...



> weil als default textur hätte ich dann einfach ne 1x1 pixel große grafik genommen um platz zu sparen un gut. aber wenn ich nen hochauflösendes bild mit 4x2k pixeln ca habe und diese "sinnfreien" default texturen dann auch so groß sein müssen, obwohl 1px langen würde.... gnaa ^^ aber gut, hier wären die flags sicher gut. wobei ich da auch noch überleg, wie ich das am dümmsten mach. textur, specular map, normal map, alpha map, luminezens(?) map - also die, die sagt wos auch im dunkeln leuchten soll ^^, höhen map? soll wohl für parallax mapping gut sein ^^. jedenfalls lässt sich mit bissl spucke da einiges zaubern und nen vec4 wird eng als flag-variable  müsste man dann vllt ne ganze matrix rüberballern  aber gut, das sollte ja ned der umstand sein.


Du nimmst halt am einfachsten immer ne eigene Ebene für jede Textur. Die werden ja wahrscheinlich alle gleich groß sein.



> gut, ich glaube die flags sind noch die beste option.


Denk dran, flags sind eigentlich bools, also mit nur einem! Bit! repräsentierbar. 

Wenn du nen int übergibst, kannste in der Zahl entsprechend halt viele Flags codieren, indem du die Bits jeweils nutzt. Bit 0 und Bit 1 gesetzt wäre z.B. 3 (0000...011) und Bit 0 auf False und Bit 1 auf True wäre dann halt 2 (000...010)



> so, nu müsst ich dann mit dieser 3d textur um dies mir eingans ging ja bei jedem texturwechsel diese textur neu "zusammenmischen". liege ich da a) vllt falsch und das lässt sich viel einfacher lösen oder is das richtig so? zumindest war meine überlegung dann dahin gehend, dass ich zu jeder "texturmischung" - die ich dann material nennen würde - eine eigene klasse baue. jede instanz dieser klasse baut dann quasi einmalig seine mischung zusammen, stellt gleich noch seine ambien/specular/diffuse eigenschaften zur verfügung usw und die textur-id würd ich dann einfach immer durchegeben, wenn was von dem material gezeichnet werden soll. irgendwie so ^^ details muss ich mir da noch überlegen.


Das ist eine Frage, die dir IMMER wieder unter kommen wird. Kann/soll ich für Rechenzeit Speicherplatz opfern?

So lange du genug Speicher hast, kannste einfach die erstellten Pakete behalten und neu nutzen, wenn du Sie erneut brauchst. Erst wenn dir der Platz ausgeht, dann verwirfst du Texturen und erstellst Sie im Zweifel halt mehrfach. Das wird btw. in allen modernen Spielen so gemacht meines Wissens nach. Daher steigt der Speicherverbrauch auch mit dem Fortlauf der Spielzeit immer weiter an, weil die alten "unnützen" Sachen einfach im Speicher liegen bleiben. Man könnte Sie ja eventuell nochmal brauchen 

Du kannst dir, sofern du in die Situation kommst, dass dir der Speicher ausgeht, auch ein Ageing für deine Texturen überlegen, also dass du dir nen Ringbuffer baust, in den du die Texturen rein schiebst, und wenn dann der Ringbuffer voll ist, dann weißte welche Textur die älteste ist, und überschreibst halt die. Man kann halt davon ausgehen, das was altes nicht mehr gebraucht wird. Du kannst das dann auch damit verknüpfen, dass du eine Last Recently used Liste baust. Also nen Flag noch drin hat, welches auf 1 gesetzt wird, sobald die Textur genutzt wird seit dem letzten ersetzen. Dann musste im Worstcase 2 mal über die ganze Liste drüber rödeln, weil alle genutzt wurden. Dann wirfste halt die älteste wieder raus, ansonsten die eben, die am ältesten und seit dem letzten Ersetzen nicht mehr genutzt wurde.

Da musst halt schauen, was für dich performanter ist. Mehr logik, was ersetzt werden soll, falls was ersetzt werden soll, oder sich das sparen und einfach immer das älteste ersetzen.



> grad nochma hier nachgelesen: "... with GLSL you can have a maximum of 16 attributes per vertex program." <- wenn man da die punktdaten, texcoords und normalen allein schon hat, + uniforms wie matrizen und texturmischung sowie flag-matrix... da bleibt ned mehr allzuviel über >< mit glück vllt 8 materialien quasi. was aber, wenn man da mehr will ^^ lichtinfos wären auch noch ne geschichte, die ich vergaß. also ich find das limit irgendwie arg knapp ^^ oder versteh ich das grundsätzlich falsch?


 Listen/Listenpointer/Structpointer gelten sicherlich auch nur als eine Variable. Damit kannst du dann praktisch wieder beliebig viel Information rein packen. Genau kann ichs dir allerdings nicht sagen, da ich mit Shadern selbst noch nicht gearbeitet habe. Es ist aber davon aus zu gehen, dass das geht.


----------



## DarkMo (4. Juli 2012)

hmm, bin immernoch am grübeln, wie ich das am dümmsten lös. aktueller gedankenweg:

ich muss beim zeichnen im objekt eh die objekteigenen texturen laden. also atm binde ich die textur in der "globen" renderScene() funke, füttere den shader usw und dann lass ich über die objektinstanz die draw methode aufrufen. es zeigt mir zumindest, dass der objectloader ein mesh wunderbar laden kann und auch alle daten hüsch übergibt (texcoords, normalen bla). nu hatte ich damals mal ganz grob son perry rhodan kugel raumer gebastelt - ohne texturen, nur farben. so als ersten schritt. den lädt er fein und die specularmap und der specularpunkt werden hübsch gemalt. allerdings hab ich da noch ne erdentextur geladen ^^ sieht... witzig aus *g*



			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



der gelbe punkt da über der landestütze is die symbolische sonne ^^ dat schiffle is e weng groß geraten xD innendrin sind noch die erde und der mond - so wie die kirche sich das gewünscht hätte  aber bei den fps hab ich ja nen schwachen moment erwischt >< normalerweise is er bei 1000-1500 ^^

gut, also das is das problem. er nutzt ned die aus der datei gelesenen "textur" daten, sondern eben die extern eingestellten. also werd ich das wohl direkt mit in die texturliste mit aufnehmen. aber in welcher form? erste überlegung: die ganze texturladerei, die atm in der setup/init funktion abläuft in die klasse übernehmen. er rödelt dann alle texturen aller listenelemente (können ja mehrere sein, alpha map, textur, specmap...) durch und erstellt ein fertiges textur dingsbums ^^ vorteil: alles unter einem hut. nachteil: ich hab 2 so schiffe, dann lädt er alle texturen 2ma hoch? oO oder alleine in einem objekt zwei verschiedene textur elemente mit den selben textur-file vorkommen? stell ich mir nich grad performant her. also doch alles global extern laden. atm läuft das in etwa so ab bei mir (anhand meines ladebalkens mal veranschaulicht):


```
// Globals
GLuint    loadingScreens[2];
const char *szLoadingFiles[2] = { "data/ladebalken.tga", "data/ladebalken_rot.tga" };
...

void SetupRC(void) {
...
    // load and initialize loading screen textures
    glGenTextures(2, loadingScreens);
    for(i = 0; i < 2; i++) {
        glBindTexture(GL_TEXTURE_2D, loadingScreens[i]);
        pBytes = gltReadTGABits(szLoadingFiles[i], &iWidth, &iHeight, &iComponents, &eFormat);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
        glGenerateMipmap(GL_TEXTURE_2D);

        free(pBytes);
    }
...
}
```

da hab ich also ein array mit den filenames sowie eines mit den textur-id's und beide indizes sind zusammenhängend. wenn ich nun beides der objektinstanz übergebe... müsste ich doch fein raus sein. jede textur wird einmalig geladen und im objekt wird dann anhand des filenamens der index herausgesucht und über diesen index komme ich dann an die textur-id heran und kann so meine textur zusammenschrauben. vorteil is ja schon genannt, nachteil der mir einfallen würde: man muss wirklich immer ALLE texturen erstma laden >< könnte uU zu problemen führen ^^


----------

