2016-03-22 17 views
0

Ich versuche eine 4 * 4 Float-Matrix-Klasse zu schreiben, um einen 3D-Raum mit Modell-, Ansichts- und Projektionsmatrizen zu erstellen. In meinem aktuellen Zustand, wenn ich versuche, die Ansichtsmatrix zu drehen, scheint es auch eine Übersetzung anzuwenden, und der Raum wird verzerrt (als ob er gequetscht wäre). Die Multiplikation der Projektion, der Ansicht und der Modellmatrix erfolgt im Vertex-Shader.OpenGL Java Matrix4f Implementierung

Edit5: Die nicht funktionierenden Zustand der Transformationsfunktionen gefunden unten ist:

public class Mat4f { 
public float m00, m10, m20, m30, 
      m01, m11, m21, m31, 
      m02, m12, m22, m32, 
      m03, m13, m23, m33; 

public Mat4f() { 
    loadIdentity(); 
} 

public Mat4f loadIdentity() { 
    m00 = 1.0f; m10 = 0.0f; m20 = 0.0f; m30 = 0.0f; 
    m01 = 0.0f; m11 = 1.0f; m21 = 0.0f; m31 = 0.0f; 
    m02 = 0.0f; m12 = 0.0f; m22 = 1.0f; m32 = 0.0f; 
    m03 = 0.0f; m13 = 0.0f; m23 = 0.0f; m33 = 1.0f; 
    return this; 
} 

public Mat4f store(FloatBuffer buffer) { 
    buffer.put(m00); 
    buffer.put(m01); 
    buffer.put(m02); 
    buffer.put(m03); 

    buffer.put(m10); 
    buffer.put(m11); 
    buffer.put(m12); 
    buffer.put(m13); 

    buffer.put(m20); 
    buffer.put(m21); 
    buffer.put(m22); 
    buffer.put(m23); 

    buffer.put(m30); 
    buffer.put(m31); 
    buffer.put(m32); 
    buffer.put(m33); 

    buffer.flip(); 

    return this; 
} 

public Mat4f loadPerspective(float fov, float ratio, float near, float far) { 
    m11 = (float) (1.0f/(Math.tan(fov/2.0f))); 
    m00 = m11/ratio; 
    m22 = -(far + near)/(far - near); 
    m23 = -1.0f; 
    m32 = -2.0f * far * near/(far - near); 
    m33 = 0.0f; 
    return this; 
} 

public Mat4f translate(float x, float y, float z) { 
    m30 = x; 
    m31 = y; 
    m32 = z; 
    return this; 
} 

public Mat4f scale(float x, float y, float z) { 
    m00 = x; 
    m11 = y; 
    m22 = z; 
    return this; 
} 

public Mat4f rotateX(float x) { 
    m11 = (float) Math.cos(x); 
    m12 = (float) Math.sin(x); 
    m21 = (float) -(Math.sin(x)); 
    m22 = (float) Math.cos(x); 
    return this; 
} 

public Mat4f rotateY(float y) { 
    m00 = (float) Math.cos(y); 
    m02 = (float) -(Math.sin(y)); 
    m20 = (float) Math.sin(y); 
    m22 = (float) Math.cos(y); 
    return this; 
} 

public Mat4f rotateZ(float z) { 
    m00 = (float) Math.cos(z); 
    m01 = (float) Math.sin(z); 
    m10 = (float) -(Math.sin(z)); 
    m11 = (float) Math.cos(z); 
    return this; 
} 
} 

Und der richtige Weg, diejenigen zu tun, ist wie folgt:

public Mat4f translate(float x, float y, float z, Mat4f dest) { 
    dest.m00 = m00; 
    dest.m01 = m01; 
    dest.m02 = m02; 
    dest.m03 = m03; 
    dest.m10 = m10; 
    dest.m11 = m11; 
    dest.m12 = m12; 
    dest.m13 = m13; 
    dest.m20 = m20; 
    dest.m21 = m21; 
    dest.m22 = m22; 
    dest.m23 = m23; 
    dest.m30 = m00 * x + m10 * y + m20 * z + m30; 
    dest.m31 = m01 * x + m11 * y + m21 * z + m31; 
    dest.m32 = m02 * x + m12 * y + m22 * z + m32; 
    dest.m33 = m03 * x + m13 * y + m23 * z + m33; 
    return this; 
} 

public Mat4f translate(float x, float y, float z) { 
    return translate(x, y, z, this); 
} 

public Mat4f scale(float x, float y, float z, Mat4f dest) { 
    dest.m00 = m00 * x; 
    dest.m01 = m01 * x; 
    dest.m02 = m02 * x; 
    dest.m03 = m03 * x; 
    dest.m10 = m10 * y; 
    dest.m11 = m11 * y; 
    dest.m12 = m12 * y; 
    dest.m13 = m13 * y; 
    dest.m20 = m20 * z; 
    dest.m21 = m21 * z; 
    dest.m22 = m22 * z; 
    dest.m23 = m23 * z; 
    dest.m30 = m30; 
    dest.m31 = m31; 
    dest.m32 = m32; 
    dest.m33 = m33; 
    return this; 
} 

public Mat4f scale(float x, float y, float z) { 
    return scale(x, y, z, this); 
} 

public Mat4f rotateX(float x, Mat4f dest) { 
    float cos = (float) Math.cos(x); 
    float sin = (float) Math.sin(x); 
    float rm11 = cos; 
    float rm12 = sin; 
    float rm21 = -sin; 
    float rm22 = cos; 

    float nm10 = m10 * rm11 + m20 * rm12; 
    float nm11 = m11 * rm11 + m21 * rm12; 
    float nm12 = m12 * rm11 + m22 * rm12; 
    float nm13 = m13 * rm11 + m23 * rm12; 

    dest. m20 = m10 * rm21 + m20 * rm22; 
    dest.m21 = m11 * rm21 + m21 * rm22; 
    dest.m22 = m12 * rm21 + m22 * rm22; 
    dest. m23 = m13 * rm21 + m23 * rm22; 

    dest.m10 = nm10; 
    dest.m11 = nm11; 
    dest.m12 = nm12; 
    dest.m13 = nm13; 

    return this; 
} 

public Mat4f rotateX(float x) { 
    return rotateX(x, this); 
} 

public Mat4f rotateY(float y, Mat4f dest) { 
    float cos = (float) Math.cos(y); 
    float sin = (float) Math.sin(y); 
    float rm00 = cos; 
    float rm02 = -sin; 
    float rm20 = sin; 
    float rm22 = cos; 

    float nm00 = m00 * rm00 + m20 * rm02; 
    float nm01 = m01 * rm00 + m21 * rm02; 
    float nm02 = m02 * rm00 + m22 * rm02; 
    float nm03 = m03 * rm00 + m23 * rm02; 

    dest.m20 = m00 * rm20 + m20 * rm22; 
    dest.m21 = m01 * rm20 + m21 * rm22; 
    dest.m22 = m02 * rm20 + m22 * rm22; 
    dest.m23 = m03 * rm20 + m23 * rm22; 

    dest.m00 = nm00; 
    dest.m01 = nm01; 
    dest.m02 = nm02; 
    dest.m03 = nm03; 

    return this; 
} 

public Mat4f rotateY(float y) { 
    return rotateY(y, this); 
} 

public Mat4f rotateZ(float z, Mat4f dest) { 
    float cos = (float) Math.cos(z); 
    float sin = (float) Math.sin(z); 
    float rm00 = cos; 
    float rm01 = sin; 
    float rm10 = -sin; 
    float rm11 = cos; 

    float nm00 = m00 * rm00 + m10 * rm01; 
    float nm01 = m01 * rm00 + m11 * rm01; 
    float nm02 = m02 * rm00 + m12 * rm01; 
    float nm03 = m03 * rm00 + m13 * rm01; 

    dest.m10 = m00 * rm10 + m10 * rm11; 
    dest.m11 = m01 * rm10 + m11 * rm11; 
    dest.m12 = m02 * rm10 + m12 * rm11; 
    dest.m13 = m03 * rm10 + m13 * rm11; 

    dest.m00 = nm00; 
    dest.m01 = nm01; 
    dest.m02 = nm02; 
    dest.m03 = nm03; 

    return this; 
} 

public Mat4f rotateZ(float z) { 
    return rotateZ(z, this); 
} 

Um die Matrizen zu ändern I verwendet die folgende Reihenfolge der Transformationen:

public void transform() { 
    mMat.loadIdentity(); 
    mMat.translate(position.x, position.y, position.z); 
    mMat.rotateX((float) Math.toRadians(orientation.x)); 
    mMat.rotateY((float) Math.toRadians(orientation.y)); 
    mMat.rotateZ((float) Math.toRadians(orientation.z)); 
    mMat.scale(scale.x, scale.y, scale.z); 
} 

public void updateCamera() { 
    Vec3f position = World.camera.getPosition(); 
    vMat.loadIdentity(); 
    vMat.rotateX((float) Math.toRadians(World.camera.getPitch())); 
    vMat.rotateY((float) Math.toRadians(World.camera.getYaw())); 
    vMat.translate(-position.x, -position.y, position.z); 
} 

Edit: die perspektivische Projektion ist in Ordnung, und so ist die Übersetzung, aber wenn ich die Modellmatrix in einer Mat4f speichere, folgt die Drehung der Modelle der Kamera.

Edit2: Die Ausrichtung des Modells folgt nicht mehr der Kameradrehung, wenn ich Mat4f als Modellmatrix verwende. Die Projektionsmatrix, die Übersetzung und die Skalierung funktionieren gut.

Edit3: Bearbeitet den Code, die angewandte Rotation ist keine Vollkreisrotation, das Modell schwingt nach links und rechts.

Edit4: Ich habe dabei die Rotation mit Matrixmultiplikation versuchten

Antwort

0

Ihre Matrix-Klasse nicht implementiert, die Zusammensetzung von Transformationen. Jede Ihrer Matrix-Methode überschreibt nur bestimmte Elemente.

Zum Beispiel Blick auf den folgenden Sequenc von Operationen:

translate(1,1,1); 
rotateX(45); 
translate(1,1,1); 

solche Sequenz mathematisch durch die Matrixmultiplikation T * R * T (wobei T und R ist, dargestellt werden, sollte der Grund übersetzen/Matrizen drehen mit den entsprechenden Parametern).

Aber was Ihr Code tut, ist nur T (weil zunächst die Matrix Identität ist), einige Teile für die Rotation zu überschreiben, und schließlich den Übersetzungsteil wieder zu überschreiben.

Um Ihren Code zu reparieren, müssen Sie eine geeignete Matrix-Multiplizierungsmethode implementieren und diese verwenden, wenn Sie eine weitere Transformation auf die Matrix anwenden möchten. Es kann hilfreich sein, einige Methoden für die grundlegenden Transformationsmatrizen zu erstellen.

Allerdings sieht dies immer noch wie die übliche Zeile Major vs Spalte Hauptmatrix Speicherlayout Reihenfolge für mich.

Ihr Layout der mXY-Mitglieder schlägt vor, dass Sie mathematische Konvention verwenden, wobei m12 zweite Reihe, dritte Spalte wäre. Ihre store Methode stellt dann die Matrix in einen Puffer mit Hauptspaltenlayout. Ein solcher Puffer könnte direkt von Legacy-GL-Matrixfunktionen (wie glLoadMatrix oder glMultMatrix) verwendet werden. Es wäre auch das "Standard" -Layout für Matrix-Uniformen, wobei der transpose-Parameter auf GL_FALSE gesetzt ist.

Allerdings Blick auf Ihre translate Methode:

public Mat4f translate(float x, float y, float z) { 
    m30 = x; 
    m31 = y; 
    m32 = z; 
    return this; 
} 

Dies würde die Übersetzung Vektor zur letzten Zeile , nicht die letzte Spalte gesetzt. (Alle anderen Funktionen scheinen ebenfalls ein transponiertes Layout zu verwenden).

Jetzt verwendet Legacy GL die matrix * vector Konvention (im Gegensatz zu vector * matrix, die D3D bevorzugt). In diesem Fall werden Ihre Matrizen auf das übertragen, was der GL erwartet. Wenn Sie Shader verwenden, liegt es an Ihnen, welche Multiplikationsreihenfolge Sie verwenden, aber es muss mit der Konvention übereinstimmen, die Sie für Ihre Matrizen verwenden, und M * v == v^T * M^T.

+0

Vielen Dank, dass Sie sich die Zeit genommen haben, meine Frage zu beantworten! Die loadPerspective-Methode, die Übersetzung und die Skalierung funktionieren einwandfrei, daher kann es sich nicht um das Problem beim Sortieren handeln. Wenn ich die Speicherfunktion so ändere, daß sie die Zeile major ist (was meiner Meinung nach dasselbe ist wie die Änderung meiner Indizes in m03, m13 und m23 im Falle einer Übersetzung), dann ist alles stockdunkel, auch wenn ich die Reihenfolge der Multiplikationen in der Vertexshader –

+0

Ich aktualisierte meine Antwort. – derhass

+0

Die Positions-, Orientierungs- und Skalierungsfelder der Objekte behalten ihre absoluten Werte, nicht das Delta. Ich rufe die Identitätsmatrix jedes Mal auf, bevor ich eine Matrix übersetze. Das einzige, was ich nicht funktioniere, ist die Rotation. Ich habe versucht, die Indizes nur in der Rotation zu transponieren, und das hat auch nicht funktioniert. Ich habe auch versucht, der Funktion abhängige Werte hinzuzufügen (siehe die Änderung), aber es erzeugt immer die gleiche Art von Fehlverhalten. –

0

Das Problem war in der Tat, dass ich frühere Transformationen nicht berücksichtigt habe. Am Ende habe ich jedoch keine Matrixmultiplikation verwendet. Ich kopierte die relevanten Teile von JOML, die Sie erreichen können: https://github.com/JOML-CI/JOML. Ich werde die Frage bearbeiten, um zu reflektieren, was das Problem war und was der Endstatus der Klasse ist. Danke JOML-Mitwirkende und Derhass für die Hilfe!