Du bist nicht angemeldet.

Werbeanzeige

1

22.07.2012, 15:17

Punkt rotieren

Hallo,

ich wollte mal eine Textur rotieren lassen. Mein Ansatz war es, alle 4 Punkte um den Mittelpunkt zu rotieren und dann die Punkte mit den neuen Koordinaten wieder als VBO zu übergeben. Es wird zwar rotiert, jedoch wird die Textur nach rechts verschoben und nur bei dem ersten Funktionsaufruf von setRotation wird es gedreht.

Bsp.:
1. Mainschleife
2. setRotation(10)
3. zeichnen und co.
4. Zurück zum Anfang der Schleife
3. setRotation(100) // Rotation bleibt trotzdem 10

Code:

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
26
27
28
29
void setRotation(float &angle) {
    A = rotatePointbyPoint(center.x, center.y, A.x, A.y, angle);
    B = rotatePointbyPoint(center.x, center.y, B.x, B.y, angle);
    C = rotatePointbyPoint(center.x, center.y, C.x, C.y, angle);
    D = rotatePointbyPoint(center.x, center.y, D.x, D.y, angle);


    // Vertex
    static const GLfloat vertex[] = {
        A.x, A.y, 0.f,
        B.x, B.y, 0.f,
        C.x, C.y, 0.f,
        D.x, D.y, 0.f,
    };

    // UVs
    static const GLfloat uvs[] = {
        0.0, 0.0,
        0.0, 1.0,
        1.0, 0.0,
        1.0, 1.0,
    };

    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);
}


und die rotatePointbyPoint Funktion:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Vector2f rotatePointbyPoint(float centerx, float centery, float pointx, float pointy, float angle) {
    float s = std::sin(angle);
    float c = std::cos(angle);

    // translate point back to origin
    pointx -= centerx;
    pointy -= centery;

    // rotate point
    float xnew = pointx * c - pointy * s;
    float ynew = pointx * s + pointy * c;

    // translate point back
    Vector2f newpoint;
    newpoint.x = xnew + centerx;
    newpoint.y = ynew + centery;

    return newpoint;
}


Hoffe ihr könnt mir helfen.

MfG
Delop

DeKugelschieber

Community-Fossil

Beiträge: 2 664

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

2

22.07.2012, 15:36

Zitat

Mein Ansatz war es, alle 4 Punkte um den Mittelpunkt zu rotieren und dann die Punkte mit den neuen Koordinaten wieder als VBO zu übergeben.


Oha, da hast du wohl etwas noch nicht verstanden. Du willst auch gar nicht die Textur drehen sondern das Mesh auf dem die Textur liegt. Das macht man normalerweise indem man die Modelview die an den Shader gesendet wird vorher errechnet. Diese enthält dann die Rotation.

In meinem Matrix struct sieht das dann so aus (hab vergessen wie das mit dem Matrix Stack von OpenGL geht):

C-/C++-Quelltext

1
2
3
4
5
mat4 m; // neue Modelview
m.setIdentity(); // Identitätsmatix setzen (sonst kann man damit nicht rechnen)
m.translate(-flaeche.breite/2, -flaeche.hoehe/2, 0); // auf den Mittelpunkt schieben...
m.rotate(winkel, 0, 0, 1); // drehen...
m.translate(flaeche.breite/2, ...); // zurückschieben!


Das was du machst geht in Richtung Software renderer, also alleine auf der CPU. Aber du willst ja über die Grafikkarte rendern.

Kannst du uns mal deine Render Funktion/Methode zeigen?

Zitat

jedoch wird die Textur nach rechts verschoben und nur bei dem ersten Funktionsaufruf von setRotation wird es gedreht.


Liegt daran das du glBufferData() benutzt. Wenn du einen Buffer aktualisieren willst nimmst du glBufferSubData() (natürlich nachdem du ihm überhaupt erstmal Daten gegen hast mit glBufferData()). Und wenn du so häufig den Inhalt eines Buffers ändern willst solltest du nicht STATIC_DRAW benutzen, sondern DYNAMIC_DRAW. Aber für dieses Problem sind beide Wege Schwachsinn :)

3

22.07.2012, 15:53

Danke für deine Antwort DeKugelschieber :).

Meine Draw-Funktion:

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
26
27
28
29
30
31
32
glBindTexture(GL_TEXTURE_2D, myID);
glUniform1i(myTextureSampler, 0);


// Vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
        0,
        3,
        GL_FLOAT,
        GL_FALSE,
        0,
        (void*)0
);

// UVs
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glVertexAttribPointer(
        1,
        2,
        GL_FLOAT,
        GL_FALSE,
        0,
        (void*)0
);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);


Den Code den du mir geschickt hast ist "älteres" OpenGL. Aber ich will ja OpenGL +3 benutzten :/.

PS: Danke für den Tipp (GL_DYNAMIC_DRAW, ...) :)


Btw: Wenn es um 2D geht und ich die Koordinaten von einem Mesh ändern will, soll ich das über den Shader machen oder einfach nochmal als VBO machen?

MfG
Delop

DeKugelschieber

Community-Fossil

Beiträge: 2 664

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

4

22.07.2012, 16:27

Zitat

Den Code den du mir geschickt hast ist "älteres" OpenGL. Aber ich will ja OpenGL +3 benutzten :/.


Das ist gar kein OpenGL da ich es selbst geschrieben habe (also das mat4 struct). Aber auch in OpenGL ist es nicht veraltet mit Matrizen zu rechnen.

Zitat

Btw: Wenn es um 2D geht und ich die Koordinaten von einem Mesh ändern will, soll ich das über den Shader machen oder einfach nochmal als VBO machen?


Wie gesagt, "von Hand" jeden Vertex in deinem Buffer zu ändern ist extrem unperformant und unnötig Arbeit. Du machst das im Shader indem du jeden Vertex in deinem Buffer mit der Modelview Matrix mulitplizierst. Dann musst du nur einmal die Matrix errechnen. Der Rest wird dann vom Shader erledigt.

Ein (Vertex) Shader der dies macht sieht dann so aus (dieser ist allerdings für 3D, bei 2D nimmst du natürlich ein "in vec2 ...):

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
#version 150

uniform mat4 projection;
uniform mat4 modelview;
in vec3 vertex0; // Vertexbuffer
in vec2 texCoord0; // TexCoordbuffer

out vec2 texCoord; // Texturkoordiante an Fragmentshader weitergeben

void main(){
    gl_Position = projection*modelview*vec4(vertex0, 1.0); // Ausgabe Vertex
    texCoord = texCoord0;
}


Wie du an der Version (150) oben siehst ist das aktuelles OpenGL (3.irgendwas). Ich habe noch nie gesehen das irgendwer den Buffer auf der CPU ändern um ein Mesh zu verändern... (für Transformationen und Skalierungen, ... anders ist das natürlich wenn du "morphen" willst, aber das ist schon advanced).

dot

Supermoderator

Beiträge: 9 833

Wohnort: Graz

  • Private Nachricht senden

5

22.07.2012, 16:27

In der Mathematik arbeitet man normalerweise nicht in Grad, sondern in Radiant. Daher erwarten auch trigonometrische Funktionen wie sin, cos etc. den Winkel in Radiant, so auch die der Standardbibliothek... ;)

Zitat

jedoch wird die Textur nach rechts verschoben und nur bei dem ersten Funktionsaufruf von setRotation wird es gedreht.


Liegt daran das du glBufferData() benutzt. Wenn du einen Buffer aktualisieren willst nimmst du glBufferSubData() (natürlich nachdem du ihm überhaupt erstmal Daten gegen hast mit glBufferData()). Und wenn du so häufig den Inhalt eines Buffers ändern willst solltest du nicht STATIC_DRAW benutzen, sondern DYNAMIC_DRAW. Aber für dieses Problem sind beide Wege Schwachsinn :)

glBufferData() sollte genauso funktionieren, wobei glBufferSubData() und auf jeden Fall DYNAMIC_DRAW allerdings potentiell effizienter wären (wobei das im konkreten Fall vermutlich keine Rolle spielt).
Eine weitere Möglichkeit, um ein VBO upzudaten, wäre glMapBuffer(). Wenn du glMapBuffer() verwendest, solltest du immer den kompletten Buffer updaten und vor dem Update immer einmal glBufferData() mit der richtigen Größe und einem nullptr für die Daten aufrufen, um dem Grafiktreiber zu sagen, dass du die Daten, die momentan im Buffer sind, nichtmehr benötigst (sog. discard). Damit vermeidest du unnötige Synchronisation zwischen CPU und GPU (falls die GPU den Buffer gerade benutzt, müsste glMapBuffer() ansonsten evtl. warten, bis die GPU fertig is und dann die GPU blockieren bis die CPU fertig ist).

Btw: Wenn es um 2D geht und ich die Koordinaten von einem Mesh ändern will, soll ich das über den Shader machen oder einfach nochmal als VBO machen?

Das hängt davon ab, das sind zwei völlig unterschiedliche und keinesfalls gleichwertige Methoden. Aber im konkreten Fall wäre es vermutlich einfacher und effizienter, die Vertices im Shader zu rotieren...

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »dot« (22.07.2012, 16:33)


DeKugelschieber

Community-Fossil

Beiträge: 2 664

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

6

22.07.2012, 16:32

Zitat

glBufferData() sollte genauso funktionieren


Stellt sich nur die Frage warum es beide gibt?

dot

Supermoderator

Beiträge: 9 833

Wohnort: Graz

  • Private Nachricht senden

7

22.07.2012, 16:34

Weil beide verschiedene Dinge tun. glBufferSubData() ist möglicherweise effizienter, wobei ich vermuten würde, dass es in der Realität praktisch egal ist; gemessen hab ich's allerdings nie. Der Punkt ist, dass rein die Verwendung von glBufferData() den Fehler imo nicht erklären kann ;)

DeKugelschieber

Community-Fossil

Beiträge: 2 664

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

8

22.07.2012, 16:50

Zitat

glBufferSubData() ist möglicherweise effizienter


Gut, das dachte ich auch, deshalb war ich etwas irritiert ;)
Letztendlich ist es aber auch egal. Das was er machen will macht man nicht über den Buffer, außer man hat vor einen eigenen Software renderer zu schreiben.

9

22.07.2012, 16:59

Ok, es funktioniert immer noch nicht... Ich habe es mit glm probiert.

C-/C++-Quelltext

1
2
3
4
float angle = 180.f * 0.0174532925f;
glm::mat4 mat(1.f);
glm::rotate(mat, angle, glm::vec3(sprite.getPosition().x, sprite.getPosition().y, 0.f));
GLuint MatrixID = glGetUniformLocation(programID, "MVP");


Übergabe an den Shader:

C-/C++-Quelltext

1
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &mat[0][0]);

DeKugelschieber

Community-Fossil

Beiträge: 2 664

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

10

22.07.2012, 17:07

Ich geb dir dafür mal mein Matrix struct, da kannst du die Rotation in Grad angeben und wie man es verwendet hast du ja schon gesehen. Gesendet wird der dann per:

C-/C++-Quelltext

1
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, m.getArray());


Matrix.h
Matrix.cpp
Vector.h
Vector.cpp

In der Matrix.cpp sind hauptsächlich die Hilfsfunktionen implementiert. Die wirst du meistens nicht brauchen. Vector wird in Matrix verwendet. Wie das ganze funktioniert sollte selbsterklärend sein.

Werbeanzeige