2016-03-22 2 views
0

Я пытаюсь написать класс матрицы с плавающей запятой 4 * 4 для создания 3D-пространства с матрицами моделей, представлений и проекций. В своем текущем состоянии, когда я пытаюсь повернуть матрицу вида, он, похоже, также применяет перевод, и пространство искажается (как будто оно было сжато). Вычисление проекции, вида и модели выполняется в вершинном шейдере.Реализация OpenGL Java Matrix4f

Edit5: нефункционирование состояние функций преобразования находится ниже:

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; 
} 
} 

И правильный способ сделать то, заключается в следующем:

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); 
} 

Для изменения матрицы я использовал следующий порядок преобразований:

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); 
} 

Редакция: перспективная проекция в порядке, и, следовательно, это перевод, но если я храню модельную матрицу в Mat4f, то поворот модели следует за камерой.

Edit2: Ориентация модели больше не следует за поворотом камеры, когда я использую Mat4f в качестве матрицы модели. Матрица, трансляция и масштабирование проекции хорошо работают.

Редактирование 3: Отредактированный код, применяемое вращение не является полным вращением круга, модель качается влево и вправо.

Edit4: Я пытался делать вращение с матричным умножением

ответ

0

Ваш класс матрица не реализует композицию преобразований. Каждый из ваших методов матрицы только перезаписывает определенные элементы.

Например, рассмотрим следующий sequenc операций:

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

Такая последовательность математически должна быть представлена ​​матрица умножения T * R * T (где Т и R являются основным перевести/вращения матрицы с соответствующими параметрами).

Однако, что делает ваш код, просто создается T (потому что изначально матрица является личным), перезаписывая некоторые части для вращения и, наконец, снова записывая часть перевода.

Чтобы исправить свой код, вам необходимо реализовать правильный метод многоуровневой матрицы и фактически использовать его всякий раз, когда вы хотите применить дальнейшее преобразование к матрице. Возможно, было бы полезно создать некоторые методы для базовых матриц преобразования.

Тем не менее, это по-прежнему выглядит как обычный порядок строк для основных матриц хранения столбцов.

Ваше расположение членов mXY предполагает, что вы используете математическое соглашение, где m12 будет второй строкой, третьим столбцом. Ваш метод store затем помещает матрицу в буфер с основным расположением столбцов. Такой буфер может быть непосредственно использован в матричных функциях GL (например, glLoadMatrix или glMultMatrix). Это также будет «стандартная» компоновка для матричной униформы, где параметр transpose установлен на GL_FALSE.

Однако, глядя на ваш метод translate:

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

Это позволит создать вектор перемещения к последнему строке, не последний колонки. (Все остальные функции также используют транспонированный макет).

Теперь унаследованное GL использует соглашение matrix * vector (в отличие от vector * matrix, которое предпочитает D3D). В этом случае ваши матрицы переносятся на то, что ожидает GL. Если вы используете шейдеры, то вам решать, какой порядок умножения вы используете, но он должен соответствовать соглашению, которое вы используете для своих матриц, и M * v == v^T * M^T.

+0

Спасибо, что нашли время ответить на мой вопрос! Метод loadPerspective, перевод и масштабирование работают нормально, поэтому это не может быть проблемой порядка. Если я изменю функцию хранилища, чтобы быть главной строкой (что, я думаю, совпадает с изменением моих индексов до m03, m13 и m23 в случае перевода), тогда все будет черным, даже если я изменю порядок умножений в вершинный шейдер. –

+0

Я обновил свой ответ. – derhass

+0

Поля, ориентация и масштаб полей объектов содержат свои абсолютные значения, а не дельта, я вызываю единицу матрицы каждый раз, прежде чем переводить матрицу. Единственное, что я не могу получить, это вращение. Я попытался перекрыть индексы только в ротации, и это тоже не сработало. Я также попытался добавить зависимые значения к функции (см. Редактирование), но он всегда вызывает такое же неправильное поведение. –

0

Вопрос в том, что я не принимал во внимание более ранние преобразования. Однако я не использовал матричное умножение. Я скопировал соответствующие детали из JOML, которые вы можете найти по адресу: https://github.com/JOML-CI/JOML. Я отредактирую вопрос, чтобы отразить, в чем проблема, и каково конечное состояние класса. Спасибо JOML contributors и Derhass за помощь!

Смежные вопросы