Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Schwarzefee

Treue Seele

  • »Schwarzefee« ist der Autor dieses Themas

Beiträge: 155

Wohnort: Ost-Sachsen

Beruf: Programmierer

  • Private Nachricht senden

11

20.03.2014, 18:02

Hi,

ich möchte es erstmal ohne Instancing probieren.

C-/C++-Quelltext

1
2
3
4
5
6
glBindBuffer(GL_ARRAY_BUFFER, this->VertexBufferId);

GLvoid* Data = glMapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(Grafics::VERTEX)*this->VerticesInBuffer,GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(Data, this->VertexData, sizeof(Grafics::VERTEX) * this->VerticesInBuffer);

glUnmapBuffer(GL_ARRAY_BUFFER);


Damit hab ich jetzt nurnoch halb so viel FPS wie mit glBufferData().
Was mach ich falsch?


Gruß

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

12

20.03.2014, 19:23

Kopierst du jetzt immer noch jedes mal neue Vertices in den Buffer?

1. Buffer irgendwo außerhalb der Render Routine aufsetzen.
2. in der Render Routine Buffer binden
3. glDrawWhatever aufrufen
4. Buffer entbinden (debinden, unbinden?)

Wenn du natürlich jedes Frame den Buffer neu zur Grafikkarte schicken musst...
Für das durchwechseln der Textur musst du ja nur die Texturkoordinaten ändern, entweder mehrere Buffer oder im Shader manipulieren.

Schwarzefee

Treue Seele

  • »Schwarzefee« ist der Autor dieses Themas

Beiträge: 155

Wohnort: Ost-Sachsen

Beruf: Programmierer

  • Private Nachricht senden

13

20.03.2014, 20:40

Hi,

im Moment läuft es so ab:
Am Anfang erstelle ich einen Vertexbuffer mit Platz für 4000 Vertices.
Bei jeder Frame kopiere ich dann die Quads in den Buffer und, wenn voll oder Frame zuende, rendere ich ihn.

Eine andere Möglichkeit fällt mir (als Anfänger) erstmal nicht ein.
Kann man irgendwie die Quads im Buffer bearbeiten, anstatt die neu rein zu kopieren?
Es können sich ja zB auch die Positionen ändern (zB wenn ein NPC sich bewegt).


Gruß

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

14

20.03.2014, 21:28

Warum willst du die Quads überhaupt "bearbeiten"?
Du kannst die doch über die Modelmatrix transformieren wie du lustig bist. Das gleiche gilt für die Texturkoordinaten.

Und es gibt keinen (guten) Grund 4000 Vertices zu haben wenn du 1000 Quadrate rendern willst.
Ich z.B. setzte Sprites so auf:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
std::shared_ptr<Mesh> Mesh::createSprite(const std::string &index, const std::string &vertex, const std::string &texCoord){
    std::shared_ptr<Mesh> mesh = std::shared_ptr<Mesh>(new Mesh());

    unsigned int indices[] = {0, 1, 2, 0, 2, 3};
    bb::vec2 vertices[] = {bb::vec2(), bb::vec2(1, 0), bb::vec2(1, 1), bb::vec2(0, 1)};
    bb::vec2 texCoords[] = {bb::vec2(), bb::vec2(1, 0), bb::vec2(1, 1), bb::vec2(0, 1)};

    mesh->indexBuffer = std::unique_ptr<bb::VBO<unsigned int>>(new bb::VBO<unsigned int>(index, GL_ELEMENT_ARRAY_BUFFER));
    mesh->indexBuffer->fill(indices, 6, sizeof(unsigned int), GL_STATIC_DRAW);

    mesh->vertex2Buffer = std::unique_ptr<bb::VBO<bb::vec2>>(new bb::VBO<bb::vec2>(vertex, GL_ARRAY_BUFFER));
    mesh->vertex2Buffer->fill(vertices, 4, sizeof(bb::vec2), GL_STATIC_DRAW);

    mesh->texCoordBuffer = std::unique_ptr<bb::VBO<bb::vec2>>(new bb::VBO<bb::vec2>(texCoord, GL_ARRAY_BUFFER));
    mesh->texCoordBuffer->fill(texCoords, 4, sizeof(bb::vec2), GL_STATIC_DRAW);

    return mesh;
}


Das gibt mir ein "Mesh" Objekt zurück, ist jetzt aus meiner Engine daher etwas abstrakt alles.
Grundsätzlich funktioniert das aber genauso mit GL direkt.
Heißt ich setzte hier 3 Buffer auf, Index, Vertex und Textur Buffer. Für diese habe ich dann jeweils eine ID (hier in "VBO" gespeichert).

Beim rendern binde ich diese dann (hier nur einer, müssen natürlich alle sein):

C-/C++-Quelltext

1
2
mesh->vertex2Buffer->bind(); // binden
mesh->vertex2Buffer->vertexAttribPointer(shader->getAttribLocation("vertex0"), 2, GL_FLOAT, false, 0, 0); // an den Shader weitereichen


Danach kann ich für die gebunden Buffer beliebig oft glDrawElements aufrufen. Zwischen diesen Aufrufen kann ich dann auch noch Daten an den Shader senden (Stichwort Uniforms), z.B. eben eine Texturmatrix, mit der sich die Texturkoordinaten verändern ließen um das Sprite zu animieren.

Die ganze Rendermethode findest du hier (gerade nicht sehr schön weil die Buffer für jedes Sprite neu gebunden werden, an sich unnötig).

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »DeKugelschieber« (20.03.2014, 21:47)


Schwarzefee

Treue Seele

  • »Schwarzefee« ist der Autor dieses Themas

Beiträge: 155

Wohnort: Ost-Sachsen

Beruf: Programmierer

  • Private Nachricht senden

15

21.03.2014, 09:48

Hi,

ich hab mir deine Engine mal angesehn.

Du speicherts für jedes Mesh (also ein Quad) einen Index-, Vertex- und TextureCoords-Buffer.
Indexbuffer und TexBuffer is klar. Im Vertexbuffer speicherst du die Positions-Koordinaten (x + y)? Oder sind da einfach nur die Koordinaten 0,0 bis 1,1 drin, und die Position des Quads in der Welt wird per Matrix ermittelt?

Und für jedes Quad rufst du dann glDrawElements() auf.

Was ich nicht verstehe sind die Matrizen.

Zitat

z .B. eben eine Texturmatrix, mit der sich die Texturkoordinaten verändern ließen um das Sprite zu animieren.

Wie muss sowas aussehn?

Ich benutze bei meiner Engine garkeine Matrizen.
Bei mir berechnet der Shader anhand der Kamera-Position die Position auf dem Bildschirm



Gruß

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

16

21.03.2014, 11:53

Zitat

Indexbuffer und TexBuffer is klar. Im Vertexbuffer speicherst du die Positions-Koordinaten (x + y)?

Ja, die Koordinaten sind später in Pixeln, aber erstmal nur von 0 - 1 (also ein Quadrat mit der Kantenlänge 1). Mit der Modelmatrix skaliere ich dann später auf die gewünschte Größe.
Damit lässt sich dann später jedes (gewöhnliche) Sprite abbilden.

Zitat

Und für jedes Quad rufst du dann glDrawElements() auf.

Aktuell leider ja. Wie gesagt, das ist eigentlich nicht nötig. Man könnte auch den Shader binden, die Buffer binden und dann in einer for Schleife alle Objekte rendern.
In der for Schleife gibt man dann nur noch an den Shader was sich auch ändert und ruf glDrawElements auf.
Du kannst die selbe Geometrie für unterschiedliche Objekte mit den selben Buffern rendern. Eigentlich könnte ich global irgendwo einmal dieses Sprite Mesh generieren und für alle Sprites nutzen (werde ich auch, bin im moment nur woanders dran).

Zitat

Wie muss sowas aussehn?

Ich benutze bei meiner Engine garkeine Matrizen.
Bei mir berechnet der Shader anhand der Kamera-Position die Position auf dem Bildschirm

Bist du sicher dass du Shader nutzt? Ich sehe auch im Anfangsbeitrag keinen Hinweis darauf.
Wenn du dir den Vertexshader anguckst, siehst du dass an diesen eine "pmv" 4x4 Matrix übergeben wird.
Mit dieser werden dann die Punkte multipliziert und somit verschoben, skaliert, rotiert und in den Viewspace gebracht.

Der Mögliche Denkfehler den du hast ist es die Punkte von Hand neu "setzten" zu müssen. Also wenn du einen Punkt von A nach B bringen willst ihn auf der CPU verschiebst und dann wieder zur Grafikkarte sendest. Das ist viel zu langsam.
Dafür sind diese Buffer in OpenGL gut (alle, nicht nur die VBOs). Man setzt sie auf der CPU auf, sendet sie in den VRAM deiner Grafikkarte und behält nur das Handle (die ID). Mit dieser kann man dann arbeiten.
Ich speichere z.B. nur in den seltensten Fällen die Pixelinformationen meiner Texturen im Arbeitsspeicher. Diese werden auch an die Graka gesendet und die Daten im RAM brauche ich dann ja nicht mehr.

Mit der Texturmatrix kenne ich mich jetzt so auch nicht aus, aber das sollte genauso wie mit dem Vertices funktionieren.
Ich würde dir empfehlen dir das Buffer System noch einmal anzuschauen und was Matrizen in OpenGL eigentlich sind/machen.

Der ultimative Link. Credits gehen immer noch zu dot, damit hat er sich für immer bei mir verewigt :D

Schwarzefee

Treue Seele

  • »Schwarzefee« ist der Autor dieses Themas

Beiträge: 155

Wohnort: Ost-Sachsen

Beruf: Programmierer

  • Private Nachricht senden

17

21.03.2014, 12:21

Hi,

danke für die Ausführungen, da hab ich einiges gelernt ;)

Zitat

Bist du sicher dass du Shader nutzt? Ich sehe auch im Anfangsbeitrag keinen Hinweis darauf.

Ja, ich benutze Shader, ich wusste ehrlichgesagt garnicht, dass es auch ohne geht ;)

Quellcode

1
und dann in einer for Schleife alle Objekte rendern.

Das bedeutet aber immernoch, dass für jedes Quad ein glDrawElements() aufgerufen wird, oder?
Ich frage nur nach, weil mir gesagt wurde, dass man immer so wenig Draw-Calls wie möglich nutzen sollte. (Zumindest in DirectX war es so)



Das Wochenende steht vor der Tür, da werd ich mal meine Engine, auf Basis deiner Informationen, umschreiben und gucken, ob es was bringt.


Eine Frage hab ich allerdings noch: In meiner Engine hat jedes Vertex auchnoch eine Farbe, die im Fragment-Shader mit der Texture-Farbe multipliziert wird.
Was wäre hier Sinnvoller: Die Farbe mit im Vertex (also in dem Quad-Vertexbuffer) speichern, und nur wenn sich die Farbe ändert (was ja nicht soo häufig vorkommen sollte) den Vertexbuffer upzudaten, oder die Farbe (so wie du es glaub ich mit den Texture-Koordinaten machst) bei jeder Frame per Buffer an den Shader zu schicken?

Zitat

Du kannst die selbe Geometrie für unterschiedliche Objekte mit den selben Buffern rendern. Eigentlich könnte ich global irgendwo einmal dieses Sprite Mesh generieren und für alle Sprites nutzen (werde ich auch, bin im moment nur woanders dran).

Also einfach einen Vertexbuffer mit 4 Vertices erstellen, und dann so wie du das machst für jede Sprite per Matrizen Skalieren, verschieben, etc.?


Gruß

Volker_Neff

Treue Seele

Beiträge: 249

Wohnort: Hamburg

  • Private Nachricht senden

18

21.03.2014, 15:18

Wenn die Farbe für alle Verice gleich sind kannst du die Farbe auch über eine Uniform variable an den Shader übergeben

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

19

21.03.2014, 15:23

Zitat

Das bedeutet aber immernoch, dass für jedes Quad ein glDrawElements() aufgerufen wird, oder?
Ich frage nur nach, weil mir gesagt wurde, dass man immer so wenig Draw-Calls wie möglich nutzen sollte. (Zumindest in DirectX war es so)

Ja wird es, aber das ist besser als für jedes Quad die Buffer neu zu binden bzw. überhaupt einen so großen Buffer zu haben den du dann auf der CPU updatest.
Das mit den draw-calls stimmt, aber ist so eben auch nicht sinnvoll.

Zitat

Was wäre hier Sinnvoller: Die Farbe mit im Vertex (also in dem Quad-Vertexbuffer) speichern, und nur wenn sich die Farbe ändert (was ja nicht soo häufig vorkommen sollte) den Vertexbuffer upzudaten, oder die Farbe (so wie du es glaub ich mit den Texture-Koordinaten machst) bei jeder Frame per Buffer an den Shader zu schicken?

Die könntest du dann auch in einem neuem Buffer speichern und mitsenden. Es ist auch möglich die Farbe je Quadrat nur einmal im Buffer zu speichern.
Evt. würde ich hier die Farbe auch einfach als Uniform Variable an den Shader senden, aber ich bin mir nicht ganz sicher was du erreichen möchtest.

Zitat

Also einfach einen Vertexbuffer mit 4 Vertices erstellen, und dann so wie du das machst für jede Sprite per Matrizen Skalieren, verschieben, etc.?

Sollte zumindest das einfachste sein, ja.

Binden dann wie gesagt besser nur einmal:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// [...]
    spriteMesh->indexBuffer->bind();
    spriteMesh->vertex2Buffer->bind();
    spriteMesh->vertex2Buffer->vertexAttribPointer(shader->getAttribLocation("vertex0"), 2, GL_FLOAT, false, 0, 0);
    spriteMesh->texCoordBuffer->bind();
    spriteMesh->texCoordBuffer->vertexAttribPointer(shader->getAttribLocation("texCoord0"), 2, GL_FLOAT, false, 0, 0);

    camera->calculateOrthoMatrix();

    for(std::shared_ptr<bb::Entity> entity : entities){
// [...]
        if(texture && position && object && object->visible){
            object->calculateModelMatrix(position);

            texture->bind();
            shader->sendUniform("texture0", 0);
            shader->sendUniform4x4("pmv", (camera->orthoMatrix*object->modelMatrix).getArray());

            glDrawElements(GL_TRIANGLES, spriteMesh->indexBuffer->size(), GL_UNSIGNED_INT, 0);

            texture->unbind();
        }
    }
// [...]
}

Schwarzefee

Treue Seele

  • »Schwarzefee« ist der Autor dieses Themas

Beiträge: 155

Wohnort: Ost-Sachsen

Beruf: Programmierer

  • Private Nachricht senden

20

21.03.2014, 16:11

Hi,

da hab ich doch gleich noch eine Frage:

C-/C++-Quelltext

1
2
3
4
5
shader->bind();
[...]
texture->bind();
[...]
texture->unbind();

Wie schauts denn da mit der Performance aus?
Ist es OK wenn man das bei jedem Quad aufruft? D.H. man hat keinen Geschwindigkeitsverlust wenn man ein ShaderProgram, Texture, etc. bindet, obwohl es ja eigentlich schon gebunden ist?


Gruß

Werbeanzeige