Подтвердить что ты не робот

Является ли учебник Google OpenGL по обучению неправильной линейной алгебре?

После того, как другой пользователь задал вопрос о Отвечать на события Touch, я загрузил исходный код и был совершенно озадачен тем, что Я видел. Учебник, похоже, не может решить, хочет ли он использовать векторы строк или векторы столбцов, и это выглядит смешано со мной.

На странице Android Matrix они утверждают, что их соглашение - это столбец-вектор/столбца-майор, что является типичным для OpenGL.

Я прав, или что-то мне не хватает? Вот их соответствующие биты:

Начните с создания MVPMatrix путем умножения mProjMatrix * mVMatrix. Пока все хорошо.

    // Set the camera position (View matrix)
    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

    // Calculate the projection and view transformation
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0)

Затем они добавляют поворот в левую сторону MVPMatrix? Это кажется немного странным.

    // Create a rotation for the triangle
    Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);

    // Combine the rotation matrix with the projection and camera view
    Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0)

Загрузка в не транспонированном порядке.

    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

Наконец, в своем шейдере векторное * матричное умножение?

    // the matrix must be included as a modifier of gl_Position
    "  gl_Position = vPosition * uMVPMatrix;" 

Добавив все это вместе, получим:

gl_Position = vPosition * mRotation * mProjection * mView;

Это неверно по моему воображению. Есть ли объяснение, что я не вижу, что происходит здесь?

4b9b3361

Ответ 1

Как парень, который написал этот учебник OpenGL, я могу подтвердить, что код примера неверен. В частности, порядок факторов в коде шейдера должен быть отменен:

"  gl_Position = uMVPMatrix * vPosition;"

Что касается применения матрицы вращения, то порядок факторов также должен быть изменен на противоположные, так что поворот является последним фактором. Эмпирическое правило состоит в том, что матрицы применяются в порядке справа налево, и сначала применяется поворот (это часть "M" "MVP" ), поэтому он должен быть самым правым операндом. Кроме того, для этого расчета следует использовать скретч-матрицу, как рекомендовал Ян Ни-Льюис (см. Его более полный ответ ниже):

float[] scratch = new float[16];
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

Спасибо, что обратили внимание на эту проблему. Я получу учебный класс и пример кода, как только смогу.

Изменить: Эта проблема теперь исправлена ​​в загружаемом образце кода и учебном классе OpenGL ES, включая комментарии о правильном порядке факторов. Спасибо за отзывы, ребята!

Ответ 2

Учебник неверен, но многие из ошибок либо отменяют друг друга, либо не очевидны в этом очень ограниченном контексте (фиксированная камера с центром в (0,0), только вращение вокруг Z). Вращение назад, но в противном случае оно выглядит правильно. (Чтобы понять, почему это неправильно, попробуйте менее тривиальную камеру: установите глаз и посмотрите на y = 1, например.)

Одна из вещей, которые сделали это очень трудным для отладки, заключается в том, что методы Matrix не выполняют обнаружение псевдонимов на своих входах. Код учебника показывает, что вы можете вызвать Matrix.multiplyMM с той же матрицей, что и вход и результат. Это неверно. Но поскольку реализация умножает столбец за раз, гораздо менее очевидно, что что-то не так, если правая сторона повторно используется (как в текущем коде, где mMVPMatrix - это rhs и результат), чем если левая сторона повторно используется, Каждый столбец слева считывается перед тем, как соответствующий столбец в результате будет записан, поэтому вывод будет правильным, даже если LHS будет перезаписан. Но если правая часть совпадает с результатом, то ее первый столбец будет перезаписан до того, как закончится чтение.

Таким образом, код учебника имеет своего рода локальный максимум: кажется, что он работает, и если вы меняете какую-то одну вещь, она потрясает эффектно. Это заставляет думать, что это неправильно, это может быть справедливо.; -)

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

Код Java:

@Override
public void onDrawFrame(GL10 unused) {
    float[] scratch = new float[16];

    // Draw background color
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    // Set the camera position (View matrix)
    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

    // Calculate the projection and view transformation
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

    // Draw square
    mSquare.draw(mMVPMatrix);

    // Create a rotation for the triangle
    Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f);

    // Combine the rotation matrix with the projection and camera view
    Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

    // Draw triangle
    mTriangle.draw(scratch);
}

Код шейдера:

gl_Position =  uMVPMatrix * vPosition;

NB: эти исправления делают проекцию правильной, но они также изменяют направление вращения. Это потому, что исходный код применял преобразования в неправильном порядке. Подумайте об этом так: вместо вращения объекта по часовой стрелке он поворачивал камеру против часовой стрелки. Когда вы фиксируете порядок операций, чтобы вращение было применено к объекту вместо камеры, объект начинает движение против часовой стрелки. Это не матрица, что неправильно; это угол, который использовался для создания матрицы.

Итак, чтобы получить "правильный" результат, вам также нужно перевернуть знак mAngle.

Ответ 3

Я решил эту проблему следующим образом:

@Override
public void onDrawFrame(GL10 unused) {      
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);        

    Matrix.setRotateM(mModelMatrix, 0, mAngle, 0, 0, 1.0f);
    Matrix.translateM(mModelMatrix, 0, 0.4f, 0.0f, 0);

    mSquare.draw(mProjMatrix,mViewMatrix,mModelMatrix);
}

@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
    ...  
    Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 99);

}

class Square {

    private final String vertexShaderCode =
        "uniform mat4 uPMatrix; \n" +
        "uniform mat4 uVMatrix; \n" +
        "uniform mat4 uMMatrix; \n" +

        "attribute vec4 vPosition; \n" +
        "void main() { \n" +
        "  gl_Position = uPMatrix * uVMatrix * uMMatrix * vPosition; \n" +
        "} \n";

    ...

    public void draw(float[] mpMatrix,float[] mvMatrix,float[]mmMatrix) {

        ...

        mPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uPMatrix");
        mVMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uVMatrix");
        mMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");

        GLES20.glUniformMatrix4fv(mPMatrixHandle, 1, false, mpMatrix, 0);
        GLES20.glUniformMatrix4fv(mVMatrixHandle, 1, false, mvMatrix, 0);
        GLES20.glUniformMatrix4fv(mMMatrixHandle, 1, false, mmMatrix, 0);

        ...
    }
}

Ответ 4

Im работает над той же проблемой, и вот что я нашел:

Я считаю, что образец Джоса ПРАВИЛЬНО,
в том числе порядок факторов в шейдерном коде:

gl_Position = vPosition * uMVPMatrix;

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

Реальная проблема, похоже, находится в функции setLookAtM.
В образцах Joes параметры:

Matrix.setLookAtM(mVMatrix, 0,
     0f, 0f,-3f,   0f, 0f, 0f,   0f, 1f, 0f );

что также вполне логично. Однако результирующая матрица просмотра выглядит странно для меня:

-1  0  0  0
 0  1  0  0
 0  0 -1  0
 0  0 -3  1

Как мы видим, эта матрица инвертирует координату X, поскольку первый член равен -1, который приведет к левому/правому флип на экране.
Он также изменит Z-порядок, но сосредоточьтесь здесь на координате X.

Я думаю, что функция setLookAtM также работает правильно.
Однако, поскольку класс Matrix НЕ является частью OpenGL, он может использовать некоторые другие системы координат,
например, регулярные координаты экрана с опусканием оси Y.
Это просто предположение, я действительно не проверял это.

Возможные решения:
Мы можем построить желаемую матрицу вида вручную,
код:

Matrix.setIdentityM(mVMatrix,0);
mVMatrix[14] = -3f;

OR
мы можем попытаться обмануть функцию setLookAtM, указав ее обратные координаты камеры: 0, 0, +3 (вместо -3).

Matrix.setLookAtM(mVMatrix, 0,
     0f, 0f, 3f,   0f, 0f, 0f,   0f, 1f, 0f );

Полученная матрица представлений будет:

1  0  0  0
0  1  0  0
0  0  1  0
0  0 -3  1

Вот именно то, что нам нужно.
Теперь камера ведет себя так, как ожидалось,
и образец работает правильно.

Ответ 5

Никакие другие предложения не работали для меня, используя текущий обновленный код примера Android, за исключением следующих при попытке перемещения треугольника.

Следующая ссылка содержит ответ. Взял за день, чтобы найти его. Проводятся здесь, чтобы помочь другим, когда я видел этот пост много раз. Матричные преобразования OpenGL ES Android