Comment appliquer une matrice de transformation?

j'essaie d'obtenir les coordonnées de l'écran 2D d'un point dans L'espace 3D, c'est-à-dire que je connais l'emplacement de la caméra son plateau, son inclinaison et son roulis et j'ai les coordonnées 3D x,y,z d'un point que je souhaite projeter.

je vais avoir de la difficulté à comprendre la transformation/matrices de projection et j'espérais que certains gens intelligents ici pourrait m'aider ;)

Voici mon code d'essai que j'ai rassemblé jusqu'à présent:

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

fondamentalement, ce dont j'ai besoin est d'obtenir les coordonnées 2D XY pour un écran donné où le point 3D se croise. Je ne sais pas comment utiliser les matrices roll, tilt et pan pour transformer le world point (wp).

toute aide est grandement appréciée!

12
demandé sur Wesley 2009-05-12 01:30:09

3 réponses

c'est compliqué. Merci de lire un livre sur ce sujet afin d'obtenir toutes les mathématiques et les détails concrets. Si vous prévoyez de jouer avec ce truc à la longueur, vous devez savoir ces choses. Cette réponse est juste pour que tu puisses te mouiller les pieds et te Hacker.

matrices multiplicatrices

tout d'abord. Les matrices multiplicatrices sont une affaire raisonnablement simple .

disons que vous avez matrices Un , B , et C , où AB = C . Disons que vous voulez calculer la valeur de la matrice C à la rangée 3, colonne 2.

  • prendre la troisième ligne de A et la deuxième colonne de B . Vous devriez avoir le même nombre de les valeurs de Un et B maintenant. (Si vous ne le faites pas, la multiplication matricielle n'est pas définie pour ces deux matrices. Vous ne pouvez pas le faire.) Si les deux sont des matrices 4×4, vous devez avoir 4 valeurs de A (ligne 3) et 4 valeurs de B (colonne 2).
  • Multiplier chaque valeur de Un à chaque valeur de B . Vous devriez vous retrouver avec 4 nouvelles valeurs.
  • ajouter ces valeurs.

vous avez maintenant la valeur de la matrice C à la ligne 3, colonne 2. Le défi est, bien sûr, de le faire par programmation.

/* 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];
            }
        }
    }
}

coordonnées homogènes

vous avez des coordonnées 3D. Disons que oui (5, 2, 1). Ce sont des coordonnées cartésiennes. Appelons - les ( x , y , z ).

les coordonnées homogènes signifient que vous écrivez un 1 supplémentaire à la fin de vos coordonnées cartésiennes. (5, 2, 1) devient (5, 2, 1, 1). Appelons - les ( x , y , z , w ).

chaque fois que vous faites une transformation qui fait w ≠ 1, vous divisez chaque composant de vos coordonnées par w . Cela change votre x , y , et z , et il fait w = 1 à nouveau. (Il n'y a aucun mal à faire cela même si votre transformation ne change pas w . Il divise tout par 1, ce qui ne fait rien.)

il y a des choses très cool que vous pouvez faire avec des coordonnées homogènes, même si le les maths derrière eux n'ont pas de sens. C'est à ce point que je vous invite à regarder de nouveau l'avis au sommet de cette réponse.


transformer un point

J'utiliserai la terminologie et les approches D'OpenGL dans cette section et les suivantes. Si quelque chose n'est pas clair ou semble entrer en conflit avec vos objectifs (parce que cela semble vaguement devoirs-comme pour moi :P), s'il vous plaît laisser un commentaire.

je vais aussi commencez par supposer que vos matrices roll, tilt et pan sont correctes.

quand vous voulez transformer un point en utilisant une matrice de transformation, vous droit-multiplier cette matrice avec un vecteur de colonne représentant votre point. Dites que vous voulez traduire (5, 2, 1) par une matrice de transformation Un . Vous définissez d'abord v = [5, 2, 1, 1] T . (J'écris [ x , y , z , w ] T avec le petit T pour signifier que vous devrait l'écrire comme un vecteur de colonne.)

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

dans ce cas, Av = v 1 , où v 1 est votre point transformé. Cela multiplication comme une multiplication matricielle, où A est 4×4 et v est 4×1. Vous finirez avec une matrice 4×1 (qui est un autre vecteur de colonne).

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

maintenant, si vous avez plusieurs matrices de transformation à appliquer, combinez-les d'abord en une matrice de transformation. Cela en multipliant les matrices ensemble dans l'ordre que vous souhaitez appliquer.

par programmation, vous devez commencez par la matrice d'identité et multipliez à droite chaque matrice de transformation. Laissez je 4 4×4 matrice d'identité, et de laisser Un 1 , Un 2 , Un 3 , ... soyez vos matrices de transformation. Que votre matrice de transformation finale soit A final

Un finale je 4

Un finale Un finale Un 1

Un finale Un finale Un 2

Un finale Un finale Un 3

notez que j'utilise cette flèche pour représenter l'affectation. Lorsque vous implémentez cette, assurez-vous de ne pas écraser un final pendant que vous l'utilisez encore dans le calcul de multiplication de matrice! Faire une copie.

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

enfin, faites la même multiplication que ci-dessus: A final v = v 1

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

du début à la fin

les transformations de caméra doivent être représentées comme une matrice de vue. Effectuer votre Un vue v = v 1 l'opération ici. ( v représente vos coordonnées mondiales comme un vecteur de colonne 4×1, A final est votre A vue .)

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

les transformations de Projection décrivent une transformation de perspective. C'est ce qui rend les objets plus proches plus grands et les objets plus éloignés plus petits. Ceci est effectué après la transformation de la caméra. Si vous ne voulez pas encore de perspective, utilisez simplement la matrice d'identité pour la matrice de projection. Quoi qu'il en soit, jouer A v 1 = v 2 ici.

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

ensuite, vous devez faire une Division de perspective. Ce approfondit coordonnées homogènes, que je n'ai pas encore décrite. De toute façon, divisez chaque composant de v 2 par le dernier composant de v 2 . Si v 2 = [ x , y , z , w ] T , puis diviser chaque composant par w (y compris w lui-même). Vous devriez finir avec w = 1. (Si votre matrice de projection est la matrice d'identité, comme je l'ai décrit précédemment, cette étape ne devrait rien faire.)

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

enfin, prenez votre v 2 . Le les deux premières coordonnées sont vos coordonnées x et y . Le troisième est z , que vous pouvez jeter. (Plus tard, une fois que vous êtes très avancé, vous pouvez utiliser cette valeur z pour comprendre quel point est devant ou derrière un autre point.) Et à ce stade, le dernier composant est w = 1, donc vous n'avez plus besoin de cela du tout.

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

si vous avez sauté les étapes de la perspective et de la division de la perspective, utilisez v_view au lieu de v_ndc ci-dessus.

très similaire à l'ensemble OpenGL coordinate systems . La différence est que vous commencez par les coordonnées du monde, alors Qu'OpenGL commence par les coordonnées des objets. La différence est la suivante:

  • vous commencez par les coordonnées du monde
    • OpenGL commence par les coordonnées de l'objet
  • vous utilisez la matrice de vue pour transformer les coordonnées du monde en coordonnées oculaires
    • OpenGL utilise la matrice ModelView pour transformer les coordonnées des objets en coordonnées oculaires

à partir de là, tout est pareil.

27
répondu Wesley 2009-05-11 23:21:18

la portée de cette question est beaucoup trop grande pour obtenir une bonne réponse ici: je recommande la lecture d'une bonne référence sur le sujet. J'ai toujours aimé le Foley et VanDam ...

3
répondu Paul Sonier 2009-05-11 21:37:40

j'ai posté un code ici qui fait beaucoup de ce dont vous avez besoin.

contient les implémentations Java des fonctions OpenGL gluPerspective() et 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);

pour utiliser la fonction project() , utilisez:

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

les valeurs retournées de x et y se situent dans l'intervalle -0,5 ... 0.5

1
répondu Alnitak 2009-05-12 16:42:52