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

Как применить матрицу преобразования?

Я пытаюсь получить двумерные экранные координаты точки в трехмерном пространстве, т.е. я знаю местоположение камеры в ее панорамировании, наклоне и рулоне, и у меня есть координаты 3D x, y, z точки, которую я хочу проект.

У меня возникают трудности с пониманием матриц преобразования/проекции, и я надеялся, что некоторые умные люди могут мне помочь;)

Вот мой тестовый код, с которым я сейчас собрался:

public class TransformTest {

public static void main(String[] args) {

    // set up a world point (Point to Project)
    double[] wp = {100, 100, 1};
    // set up the projection centre (Camera Location)
    double[] pc = {90, 90, 1};

    double roll = 0;
    double tilt = 0;
    double pan = 0;

    // translate the point
    vSub(wp, pc, wp);

    // create roll matrix
    double[][] rollMat = {
            {1, 0, 0},
            {0, Math.cos(roll), -Math.sin(roll)},
            {0, Math.sin(roll), Math.cos(roll)},
    };
    // create tilt matrix
    double[][] tiltMat = {
            {Math.cos(tilt), 0, Math.sin(tilt)},
            {0, 1, 0},
            {-Math.sin(tilt), 0, Math.cos(tilt)},
    };
    // create pan matrix
    double[][] panMat = {
            {Math.cos(pan), -Math.sin(pan), 0},
            {Math.sin(pan), Math.cos(pan), 0},
            {0, 0, 1},
    };

    // roll it
    mvMul(rollMat, wp, wp);
    // tilt it
    mvMul(tiltMat, wp, wp);
    // pan it
    mvMul(panMat, wp, wp);

}

public static void vAdd(double[] a, double[] b, double[] c) {
    for (int i=0; i<a.length; i++) {
        c[i] = a[i] + b[i];
    }
}

public static void vSub(double[] a, double[] b, double[] c) {
    for (int i=0; i<a.length; i++) {
        c[i] = a[i] - b[i];
    }      
}

public static void mvMul(double[][] m, double[] v, double[] w) {

    // How to multiply matrices?
} }

В принципе, мне нужно получить координаты 2D XY для данного экрана, где точка 3D пересекается. Я не уверен, как использовать матрицы roll, tilt и pan для преобразования точки мира (wp).

Любая помощь с этим очень ценится!

4b9b3361

Ответ 1

Это сложный материал. Пожалуйста, прочитайте книгу по этой теме, чтобы получить все подробные сведения о математике и nitty. Если вы планируете играть с этим материалом, вам нужно знать все это. Этот ответ только для того, чтобы вы могли мочить ноги и взламывать.

Умножающиеся матрицы

Прежде всего. Умножающиеся матрицы - это разумно простое дело.

Скажем, у вас есть матрицы A, B и C, где AB = C. Скажем, вы хотите выяснить значение матрицы C в строке 3, столбец 2.

  • Возьмите третью строку A и второй столбец B. Теперь вы должны иметь такое же количество значений от A и B. (Если вы не выполняете матричное умножение для этих двух матриц, вы не можете этого сделать.) Если оба являются 4 × 4 матрицами, вы должны иметь 4 значения из A (строка 3) и 4 значения из B ( столбец 2).
  • Умножьте каждое значение A с каждым значением B. В итоге вы получите 4 новых значения.
  • Добавьте эти значения.

Теперь у вас есть значение матрицы C в строке 3, столбец 2. Задача, конечно же, сделать это программно.

/* AB = C

Row-major ordering
a[0][0] a[0][2] a[0][3]...
a[1][0] a[1][4] ...
a[2][0] ...
...*/
public static mmMul(double[][] a, double[][] b, double[][] c) {
    c_height = b.length; // Height of b
    c_width = a[0].length; // Width of a
    common_side = a.length; // Height of a, width of b

    for (int i = 0; i < c_height; i++) {
        for (int j = 0; j < c_width; j++) {
            // Ready to calculate value of c[i][j]
            c[i][j] = 0;

            // Iterate through ith row of a, jth col of b in lockstep
            for (int k = 0; k < common_side; k++) {
                c[i][j] += a[i][k] * b[k][j];
            }
        }
    }
}

Гомогенные координаты

У вас есть 3D-координаты. Скажем, у вас есть (5, 2, 1). Это декартовы координаты. Пусть назовем их (x, y, z).

Гомогенные координаты означают, что вы пишете лишний 1 в конце ваших декартовых координат. (5, 2, 1) становится (5, 2, 1, 1). Пусть назовем их (x, y, z, w).

Всякий раз, когда вы выполняете преобразование, которое делает w ≠ 1, вы делите каждый компонент ваших координат на w. Это изменяет ваши x, y и z, и это снова делает w = 1. (Нет никакого вреда в этом, даже когда ваша трансформация не изменится w. Она просто делит все на 1, что ничего не делает.)

Есть несколько замечательных вещей, которые вы можете сделать с однородными координатами, даже если математика позади них не дает полного смысла. Именно в этот момент я прошу вас снова взглянуть на совет в верхней части этого ответа.


Преобразование точки

Я буду использовать терминологию и подходы OpenGL в этом и следующих разделах. Если что-то неясно или, похоже, противоречит вашим целям (потому что это кажется смутным домашним заданием - как мне: P), пожалуйста, оставьте комментарий.

Я также начну с предположения, что правильны ваши рулонные, наклонные и поворотные матрицы.

Если вы хотите преобразовать точку с помощью матрицы преобразования, вы умножаете ее на эту матрицу с вектором столбца, представляющим вашу точку. Скажем, вы хотите перевести (5, 2, 1) на некоторую матрицу преобразования A. Сначала вы определяете v = [5, 2, 1, 1] T. (Я пишу [x, y, z, w] T с little T, чтобы вы могли написать его как вектор-столбец.)

// Your point in 3D
double v[4][5] = {{5}, {2}, {1}, {1}}

В этом случае Av = v 1, где v 1 - ваша преобразованная точка. Сделайте это умножение как матричное умножение, где A - 4 × 4, а v - 4 × 1. В итоге вы получите матрицу размером 4 × 1 (это еще один вектор столбца).

// Transforming a single point with a roll
double v_1[4][6];
mmMul(rollMat, v, v_1);

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

Программно, вы должны начать с матрицы идентичности и умножать каждую матрицу преобразования. Пусть я 4 - 4 × 4 единичная матрица, и пусть A 1, A 2, A 3... быть вашими матрицами преобразования. Пусть ваша конечная матрица преобразования будет A final

A final ← я 4
A final ← A final A 1
A final ← A final A 2
A final ← A final A 3

Обратите внимание, что я использую эту стрелку для представления присваивания. Когда вы реализуете это, убедитесь, что не перезаписываете A final, пока вы все еще используете его в вычислении матричного умножения! Сделайте копию.

// A composite transformation matrix (roll, then tilt)

double a_final[4][4] =
{
    {1, 0, 0, 0},
    {0, 1, 0, 0},
    {0, 0, 1, 0},
    {0, 0, 0, 1}
}; // the 4 x 4 identity matrix

double a_final_copy[4][4];
mCopy(a_final, a_final_copy); // make a copy of a_final
mmMul(rollMat, a_final_copy, a_final);
mCopy(a_final, a_final_copy); // update the copy
mmMul(tiltMat, a_final_copy, a_final);

Наконец, сделайте то же самое умножение, что и выше: A final v = v 1

// Use the above matrix to transform v
mmMul(a_final, v, v_1);

От начала и до конца

Преобразования камеры должны быть представлены как матрица вида. Выполните здесь операцию A view v = v 1. (v представляет ваши мировые координаты как вектор столбца 4 × 1, A final - это ваш A view.)

// World coordinates to eye coordinates
// A_view is a_final from above
mmMult(a_view, v_world, v_view);

Преобразования проекций описывают перспективное преобразование. Это то, что делает объекты ближе к более крупным и более мелким объектам. Это выполняется после преобразования камеры. Если вы еще не хотите перспективы, просто используйте матрицу идентификации матрицы проекции. В любом случае, выполните здесь 1= v 2.

// Eye coordinates to clip coordinates
// If you don't care about perspective, SKIP THIS STEP
mmMult(a_projection, v_view, v_eye);

Затем вам нужно сделать разделение перспективы. Это углубляется глубже в однородные координаты, о которых я еще не описал. В любом случае, разделите каждый компонент v 2 на последний компонент v 2. Если v 2= [x, y, z, w] T то разделим каждую компоненту на w (включая сам w). Вы должны в конечном итоге получить w = 1. (Если ваша матрица проецирования является идентификационной матрицей, как я описал ранее, этот шаг ничего не должен делать.)

// Clip coordinates to normalized device coordinates
// If you skipped the previous step, SKIP THIS STEP
for (int i = 0; i < 4; i++) {
    v_ndc[i] = v_eye[i] / v[3];
}

Наконец, возьмите v 2. Первые две координаты - ваши координаты x и y. Третий - это z, который вы можете выбросить. (Позже, как только вы получите очень продвинутый, вы можете использовать это значение z, чтобы выяснить, какая точка находится перед или за какой-то другой точкой.) И на этом этапе последний компонент равен w = 1, так что вам не нужно это уже не так.

x = v_ndc[0]
y = v_ndc[1]
z = v_ndc[2]  // unused; your screen is 2D

Если вы пропустили шаги разделения перспективы и перспективы, используйте v_view вместо v_ndc выше.

Это очень похоже на набор систем координат OpenGL. Разница в том, что вы начинаете с мировых координат, а OpenGL начинается с координат объекта. Разница заключается в следующем:

  • Вы начинаете с мировых координат
    • OpenGL начинается с координат объекта
  • Вы используете матрицу вида для преобразования координат мира в координаты глаз
    • OpenGL использует матрицу ModelView для преобразования координат объекта в координаты глаз

Оттуда все будет одинаково.

Ответ 2

Сфера этого слишком велика, чтобы получить хороший ответ здесь: я бы рекомендовал прочитать хорошую ссылку на эту тему. Мне всегда нравился Foley и VanDam...

Ответ 3

Я разместил код здесь, который делает многое из того, что вам нужно.

Он содержит реализации Java функций OpenGL gluPerspective() и gluLookAt():

Camera camera = new Camera();

Point3d eye = new Point3d(3, 4, 8);
Point3d center = new Point3d(0, 0, 0);
Vector3d up = new Vector3d(0, 1, 0);

camera.perspective(60.0, 1.6, 0.1, 20); // vertical fov, aspect ratio, znear, zfar
camera.lookAt(eye, center, up);

Чтобы использовать функцию project() в ней, используйте:

void plot(Camera camera, Point4d p) {
    Point4d q = Camera.project(p);
    float x = q.x / q.w;
    float y = q.y / q.w;
    ...
}

Возвращаемые значения x и y падают в диапазоне -0,5... 0,5