Reihenfolge bei Matrizenzeug 3d

[QUOTE=Unregistered]hm, is bin eigentlich gegen einen static buffer… Spätestens wenn man Multithreading in Betracht zieht und erst mal die ganzen Buffers berechnet und im GL-Thread dann nur mehr die Daten rüberschaufelt ist refactoring angesagt.

Ich würde einen Buffer pro Matrix erstellen, und den immer wiederverwenden…[/QUOTE]

Grundsätzlich stimme ich da zu. Man kann auch jeder einzelnen utility-Methode einen eigenen Buffer spendieren, das macht ja keinen Unterschied (es geht ja nur darum, nicht in jedem Frame tausende von neuen zu erstellen). Das multithreading-Problem ist damit aber auch nicht gelöst. Auch da könnte man sich was überlegen, ThreadLocal kommt mir in den Sinn, aber die besagten Operationen werden üblicherweise ja sowieso (nur) im Rendering-Thread aufgerufen, um z.B. Matrizen an den Shader rüberzureichen. Aber diese fancy neuen Erweiterungen (pun intended) erlauben es vielleicht diese Probleme auf andere Art abzuschwächen.

Ach und noch eine Frage: Ich hab jetzt n bissl über instancins gelesen. Okay, man zeichnet damit die selben Geometry Objects
mehrere male mit einem call. Also auch immer ein und das selbe vbo. Aber woher weiss mein Rechner jetzt an welcher Stelle jeder dieser Objects gemalt werden soll? :S
Blick da irgendwie nicht ganz durch :confused:

Ein Uniform Block sieht bei OpenGL 3.1 (das entspricht GLSL 140) z.B. so aus:

layout(std140) uniform MVP {
    mat4 p;
    mat4 mv;
    mat4 mvp;
};

Der Uniform Block wird über das UBO auf einen Buffer gemappt. Diesen Buffer füllst Du so, wie die Variablen im Block liegen, im Beispiel also (float[16] p, mv, mvp):

    uboBuffer.put(p);
    uboBuffer.put(mv);
    uboBuffer.put(mvp); 

Bei OpenGL 3.1 wird die Uniform Block Location wird über den Uniform Block Index hergestellt, etwa so:


    final int uniformBlockIndexMVP = glGetUniformBlockIndex(program, "MVP");
    glUniformBlockBinding(program, uniformBlockIndexMVP, UNIFORM_BLOCK_LOCATION_MVP);```

Neue Daten werden über das UBO so übertragen:

```   glBindBuffer(GL_UNIFORM_BUFFER, uboBufferHandle);
   glBufferSubData(GL_UNIFORM_BUFFER, 0, uboBuffer);```

Und so wird das UBO an den Shader gebunden:

```glBindBufferRange(GL_UNIFORM_BUFFER, UNIFORM_BLOCK_LOCATION_MVP, uboBufferHandle, 0, uboBuffer.capacity() * 4);```

Vermutlich sind jetzt noch mehr fragen als vorher offen...


[QUOTE=mymaksimus;88370]Ist instancing zum beispiel bei einer Height map sinnvoll? Weil dort ist jedes Dreieck doch eigentlich das selbe objekt, nur mit einer anderen farbe, oder?[/QUOTE]

Nein, ein Dreieck ist zu wenig. Mögliche Beispiele wären Bäume, Player oder auch Würfel. Die Topologie des Meshs ist dabei immer gleich, unterscheiden kann sich z.B. Farbe, Textur, Position oder auch die Animationsphase.

Für eine Heightmap würde ich ein flaches Mesh als Triangle Strip anlegen und im Shader die jeweilige Höhe des Vertex anpassen.


[QUOTE=mymaksimus;88375]Ach und noch eine Frage: Ich hab jetzt n bissl über instancins gelesen. Okay, man zeichnet damit die selben Geometry Objects 
mehrere male mit einem call. Also auch immer ein und das selbe vbo. Aber woher weiss mein Rechner jetzt an welcher Stelle jeder dieser Objects gemalt werden soll? :S
Blick da irgendwie nicht ganz durch :/[/QUOTE]

Du hast im Shader eine gl_InstanceID, das ist eine ID, die für jede Instance eindeutig ist. Zusätzlich überträgst Du ein Array von (Transformations-)Matrizen z.B. via UBO:


layout(std140) uniform MVP {
mat4 p;
mat4 v;
mat4 vp;
};

layout(std140) uniform Instance {
mat4 m[256];
};

layout(location = 0) in vec4 v;

void main() {
vec4 current = m[gl_InstanceID] * v;
gl_Position = vp * current;
}



Wenn ein UBO zu klein wird (mindestens 16KB), kann man die Matrizen auch als Textur übertragen. (Oder ab OpenGL 4.3 als [SSBO](https://www.opengl.org/wiki/Shader_Storage_Buffer_Object), die sind richtig fancy ;)).

Viele Grüße
Fancy

Achso habs jetzt verstanden…, aaber: glsl 140 -.- Geht das nicht irgendwie auf mit 130 ?

geht eigentlich, habs grob verstanden ^^

Zu einer Heightmap aber: das heißt bei ner heightmap mit 5000 dreiecken dann doch 5000 * 3 vertices zeichnen oder wie? ._.

OpenGL 2.1 = GLSL 1.20
OpenGL 3.0 = GLSL 1.30
OpenGL 3.1 = GLSL 1.40
OpenGL 3.2 = GLSL 1.50
OpenGL 3.3 = GLSL 3.30

Ab 3.3 blieb es dann eindeutig. Wenn Dein Notebook also OpenGL 3.1 kann, ist die zugehörige GLSL-Version eigentlich die GLSL 1.40.

Bei GL_TRIANGLE_STRIP wären 5000 Dreiecke im Idealfall 5002 Vertices. Entscheidender ist aber das es ein glDraw-Aufruf ist.

Du bereitest das Mesh als flache Ebene in einem statischen IBO/VBO vor und zeichnest das dann. Die Höhe kommt erst im Shader, das Mesh (und damit IBO/VBO) muss also nie verändert werden.
(Es sei den das soll eh alles statisch sein, dann kann die Höhe natürlich auch direkt im VBO eingebaut sein.)

Viele Grüße
Fancy

Daas heisst also ich hab einfach eim vbo mit 5000 vertices, (ohne ibo) und zeichne es ganz normal mit glDrawArrays? Und was genau sind jetzt triangle strips?

*** Edit ***

ah und du benutzt ja immer „mvp“ was ja offensichtlich für model view projection steht. aber was war jetzt view? In meinem beispiel die camera oder wie?

*** Edit ***

Hast übrigens Recht, sry, mein lappi kann natürlich doch glsl 140. also nochmal zum mitdenken:

[ul]
[li]was genau bringt mir so ein uniform block?[/li][li]was genau ist ein layout in glsl?[/li][li]was bedeutet der parameter im layout() ?[/li][li]was ist in deiner erklärung eben der unterschied zwischen UNIFORM_BLOCK_LOCATION_MVP, uboBufferHandle und uniformBlockIndexMVP ? wieso ist letzteres einfach 0 und was ist vorletzteres?[/li][li][quote=Fancy]Und so wird das UBO an den Shader gebunden:[/quote] wann? einmal? nach dem daten verändert wurden?[/li][li][quote=Fancy;88383]uboBuffer[/quote] ist ein ganz normaler byte / float / xyz buffer? Im beispiel 3 * mat4 also ein FloatBuffer der size 48 ?[/li][li][quote=Fancy;88383][/li]```
layout(std140) uniform Instance {
mat4 m[256];
};

[/quote] hier also ein UBO mit nem float buffer der größe 4096, reicht für 256 instances? bei 1000 instances 1000 mat4's (wär ja logisch) ?
[li]`uboBuffer.capacity() * 4)` hm...? why capacity, why * 4 ?[/li][li]`gl_InstanceID` ist bereits definiert?..[/li]```
layout(std140) uniform MVP {
    mat4 p;
    mat4 v;
    mat4 vp;
};

wieso? wieso p v und vp? das ist doch camera * projection - oder nicht? wieso nicht einfach direkt vp übertragen?
[/ul]

Sorry für die vielen Fragen, aber ich finde einfach nichts abstrakt erklärtes im internet :frowning: Die opengl wiki einträge sind auch ziemlich bescheiden gehalten für einen anfänger… ._.

Im VBO ein Vertex-Eintrag für jeden Gitterpunkt des Mesh. Im IBO eine Folge von Indices, mit denen die Topologie des Meshs beschrieben wird. Dabei nicht einfach von einer Seite zur anderen durchnummerieren, sondern den Cache beachten. Meistens im Zickzack als breite Streifen, so das die Vertices der vorhergehenden Reihe noch im Cache sind. Zeichnen mit glDrawElements. Zu Triangle Strip gibt es ne Menge im Netz, zB. hier (mitte/unten).

Ja.

Normale Uniforms „leben“ im Shader und Du must jeden Parameter jedes Mal einzeln neu übertragen. UBOs „leben“ außerhalb der Shader und mehrere Shader können auf dasselbe UBO zugreifen. UBOs werden gebunden und bleiben es so lange die location nicht anderweitig benötigt wird. Die Daten in UBOs können schneller aktualisiert werden als viele einzelne Uniforms. Außerdem kann über den Wechsel des UBO schnell zwischen verschiedenen Datensätzen gewechselt werden. Des Weiteren können UBOs deutlich größer sein als Uniforms, sodass mehr Daten im Shader zur Verfügung sehen. Und nicht zuletzt vermitteln sie einen Hauch von OOP.

[QUOTE=mymaksimus;88389][ul][li]was genau ist ein layout in glsl?
[/li][li]was bedeutet der parameter im layout() ?
[/li][li]was ist in deiner erklärung eben der unterschied zwischen UNIFORM_BLOCK_LOCATION_MVP, uboBufferHandle und uniformBlockIndexMVP ? wieso ist letzteres einfach 0 und was ist vorletzteres?
[/li][li] wann? einmal? nach dem daten verändert wurden?[/ul][/quote]
[/li]
Lies Dir mal diese Beschreibung durch, imho wird das dort verständlich erklärt.

[QUOTE=mymaksimus;88389][ul][li]ist ein ganz normaler byte / float / xyz buffer? Im beispiel 3 * mat4 also ein FloatBuffer der size 48 ?
[/li][li] hier also ein UBO mit nem float buffer der größe 4096, reicht für 256 instances? bei 1000 instances 1000 mat4’s (wär ja logisch) ?
[/li][li]gl_InstanceID ist bereits definiert?..[/ul][/quote]
[/li]
Ja, ja und ja.

[/li]
Dort wird die Größe des Buffers in Byte erwartet, wenn uboBuffer ein FloatBuffer ist, muss mal 4 gerechnet werden.

[QUOTE=mymaksimus;88389]

layout(std140) uniform MVP {
    mat4 p;
    mat4 v;
    mat4 vp;
};

wieso? wieso p v und vp? das ist doch camera * projection - oder nicht? wieso nicht einfach direkt vp übertragen?[/quote]

Wenn Du nur vp überträgst, kannst Du die Koordinaten immer nur vom World-Space in den Clip-Space transformieren. Einige Shader Programme arbeiten aber mit Algorithmen, die Eye-Space Koordinaten benötigen. Da ein UBO von vielen Shadern genutzt werden kann, ist es sinnvoll einfach alle Parameter im UBO zu setzen und der Shader nutzt dann die, die er jeweils braucht.

(Ein normales OpenGL Programm nutzt viele verschiedene Shader pro Pass und Frame)

Viele Grüße
Fancy

Wow, danke!
Wenn ich mal fragen darf… woher weißt du das alles? ^^ Bist du vielleicht still und heimlich der entwickler der unreal game engine? xD

Ich lese mir jetzt die Beschreibung bei arcsynthesis durch… mal sehen ob ich das implementieren kann. Leider finde ich
irgendwie kaum was zu glDrawElements, und glDrawElements- / Arrays- Instanced, kaum code beispiele die man verwerten kann…
und kaum beschreibungen… hat der herr arcsynthesis da vielleicht auch was zu? muss ich mal schauen. Es gibt wohl doch
nur wenige Leute die ihr wissen mit der Welt teilen wollen… ^^

Ich melde mich bald mal wieder, ich glaub nicht das ich die Todo Liste* ohne Probleme hinbekomme…

Todo Liste:

  • Ubo verwaltung implementieren
  • Instances implementieren
  • versuchen eine heightmap zu rendern…

Grüße, mymaksimus ^^

So, ich hab versucht die Pyramiden nun mit glDrawArraysInstanced zu zeichnen. Das ist leider sehr sehr langsam.
Ich erreiche bei 32 pyramiden, (16 unten 16 oben) auf meinem laptopt gerade mal 86 fps (ohne camera update 90). Ich hatte 30 fps bei 5000 glDrawArrays() calls pro frame…
Ist das etwa normal? Ich dachte instances müssten um einiges schneller sein… naja ich probiers mal mit elements statt instances, auch wenn ich keinen plan hab
was da der unterschied sein soll bzw was elements überhaupt sind. Falls ich doch nur irgendwas dummes gemacht hab, hier die änderungen:

  • Der draw call besteht jetzt aus GL31.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 18, gridSize * gridSize * 2); // * 2 wegen oben, unten

  • im vertex shader die variable uniform mat4 models[32];

  • diese wird so gefüllt (EINMAL, bei der initialisierung):

        FloatBuffer modelBuffer = BufferUtils.createFloatBuffer(16 * gridSize * gridSize * 2);
	for(int i = 0; i < gridSize; i++){
		    for(int j = 0; j < gridSize; j++){
		    	testTranslate(2 * i + i * 0.5f, -5.0f, 2 * j + j * 0.5f, 0).store(modelBuffer);
		    	testTranslate(2 * i + i * 0.5f, 5.0f, 2 * j + j * 0.5f, 180).store(modelBuffer);
		    }
		}
        modelBuffer.flip();
        GL20.glUniformMatrix4(GL20.glGetUniformLocation(shaderProgramID, "models"), false, modelBuffer);```

- dann noch die berechnung im vertex shader: `gl_Position = camera * models[gl_InstanceID] * vec4(vert, 1);`

Wie gesagt es gibt nur 2 sachen die in diesem kskb (ja, ich bastel immer noch darin rum ^^) leistung benötigen: rendern, (oben) und die camera updates. die machen aber wie erwähnt "nur" <5 fps aus.

*** Edit ***

Ach noch eine Sache: Ich hab irgendwo gelesen das es üblich ist in großen Projekten seehr sehr viele shader programme zu haben.
Ist ja eigentlich klar, da man den gleichen shader nicht auf alles andere anwenden kann... aber ist das dann wirklich so getrennt, dass man zB
in einem Spiel die Mobs mit dem einen Shaderprog zeichnet... die landscahft mit nem anderen.. die heightmap mit nem anderen... das wetter mit nem anderen...
die partikel mit nem anderen... und jeden frame zwischen all diesen shader programs switcht? ist das nicht n bissl rechenintensiv?

Ich mache das auch nur als Hobby, allerdings schon sehr lange.

Erfreulicherweise hat OpenGL in den letzten Jahren eine sehr hohe Innovationsgeschwindigkeit hingelegt. Zusammen mit der relativ hohen Komplexität der interessanten Themen macht es das aber schwierig Tutorials zu verfassen. Hinzukommt das das jeweilige Vorgehen von der verwendeten OpenGL Version abhängt. Eigentlich musste man also jedes Tutorial für jede Version verfassen…

Eigentlich ist der Sinn schon das es schneller wird.

Siehe Dir mal dieses Beispiel an. Das ist nur schnell runter geschrieben, nicht OOP like. Aber alles drin, was drin sein sollte, VAO, IBO, VBO, UBO und glDrawElementsInstanced. Nur statt Pyramiden hab ich Würfel genommen. Und model ist zur Abwechslung mal keine Matrix, sondern ein Vektor, da die Würfel nur verschoben werden. Es sollte OpenGL 3.1 kompatibel sein.

Bei mir läuft das Beispiel so mit 3362 Würfeln mit etwa 3000 fps. Bei 80802 Würfeln sind es immer noch 350 fps. Mehr passen bei mir nicht in ein UBO.

[QUOTE=mymaksimus;88451]Ist ja eigentlich klar, da man den gleichen shader nicht auf alles andere anwenden kann… aber ist das dann wirklich so getrennt, dass man zB
in einem Spiel die Mobs mit dem einen Shaderprog zeichnet… die landscahft mit nem anderen… die heightmap mit nem anderen… das wetter mit nem anderen…
die partikel mit nem anderen… und jeden frame zwischen all diesen shader programs switcht? ist das nicht n bissl rechenintensiv?[/QUOTE]

Ja, ein Wechsel zwischen den Shadern ist in der Tat sehr teuer. Eine aktuelle Grafikkarte könnte (wenn sie sonst nichts zu tun hätte) etwa 300.000 mal pro Sekunde den Shader wechseln. Zum Vergleich: sie könnte auch 1.500.000 mal pro Sekunde die Textur wechseln. Grundsätzlich gilt bei OpenGL immer das Zustandswechsel minimiert werden sollten.

Viele Grüße
Fancy

wie würde oben genanntest denn sonst gehen statt die shader zu wechseln?

Okay danke ich schau mir den code mal an.

Was macht man denn, wenn das ubo zu klein wird? :o
mehrere ubos? :smiley:

Die meisten Effekte kann man auf verschiedene weisen erzeugen. Welche davon schneller als andere sind, hängt vom konkreten Umfeld ab. Aber natürlich kann man nicht alles zusammenfassen. Aber man kann z.B. einen Shader schreiben der nur die Geometrie von z.B. Wasser berechnet und in ein Render-Target legt, einen Shader der die Geometrie der animierten Objekte berechnet und in ein Render-Target legt (ggf. derselbe), einen Shader der die Geometrien aller statischen Objekte berechnet und in ein Render-Target legt (ggf. derselbe) und dann noch einen Shader der alle diese Targets nimmt und auf allen die Beleuchtung für alle Lichtquellen berechnet.

[QUOTE=mymaksimus;88467]Was macht man denn, wenn das ubo zu klein wird? :o
mehrere ubos? :D[/QUOTE]

Wäre möglich, dann hat man aber auch mehr Draw-Calls. Eine andere Variante wären größere Buffer, z.B. Buffer Texture (im Shader nicht sonderlich schön) oder eben SSBO.

Viele Grüße
Fancy

Aaaalso so viel zum Status Bericht: Bei deinem Beipiel und einer GRID_ROOT_SIZE von 4 habe ich auf dem laptop 36 fps.
Bei 10 hab ich 0 - 1. Nehme ich alle Licht berechnugen raus, habe ich bei der G_R_S von 10 so ungefähr 5 fps.

Was kann ich daraus schließen? Ubo’s, Ibo’s, Instancing, auf schlechten Computern alles für die Katz ?

Hm, das ist natürlich ärgerlich. Wenn Du Zeit und Lust hast, könntest Du vielleicht folgende Varianten ausprobieren, ggf. last sich dann einkreisen, womit Dein Rechner ein Problem hat. Zum Vergleich mit meinen Werten:

Variante A, GRID_ROOT_SIZE von 7 und reduzierte Beleuchtung: 4200 fps
Variante B, wie A jedoch ohne Instancing, id per uniform: 1900 fps
Variante C, wie B jedoch model vector per uniform: 1900 fps
Variante D, wie C jedoch degenerierte Dreiecke statt Primitive Restart: 2000 fps
Variante E, wie D jedoch GL_TRIANGLES statt GL_TRIANGLE_STRIP: 2000 fps
Variante F, wie E jedoch ohne IBO: 2000 fps

Verallgemeinern sollte man die Ergebnisse aber nicht. IBO und Triangle Strip bringen z.B. am meisten bei glatten komplexen Oberflächen. Durch die harten Kanten beim Würfel lassen sich seine Vertices eben leider nicht wiederverwenden.

Viele Grüße
Fancy

tja ist eben nur ein schlechter laptop. mal sehn wie ich das in meiner mini engine einbaue…