Projection en perspective 3D de base sur écran 2D avec caméra (sans opengl)

disons que j'ai une structure de données comme la suivante:

Camera {
   double x, y, z

   /** ideally the camera angle is positioned to aim at the 0,0,0 point */
   double angleX, angleY, angleZ;
}

SomePointIn3DSpace {
   double x, y, z
}

ScreenData {
   /** Convert from some point 3d space to 2d space, end up with x, y */
   int x_screenPositionOfPt, y_screenPositionOfPt

   double zFar = 100;

   int width=640, height=480
}

...

sans découpage d'écran ou autre chose, comment calculer la position de l'écran x, y d'un point donné un point 3d dans l'espace. Je veux projeter ce point 3d sur l'écran 2d.

Camera.x = 0
Camera.y = 10;
Camera.z = -10;


/** ideally, I want the camera to point at the ground at 3d space 0,0,0 */
Camera.angleX = ???;
Camera.angleY = ????
Camera.angleZ = ????;

SomePointIn3DSpace.x = 5;
SomePointIn3DSpace.y = 5;
SomePointIn3DSpace.z = 5;

ScreenData.x et y est la position X de l'écran du point 3d dans l'espace. Comment dois-je calculer ces valeurs?

je pourrais peut-être utiliser les équations trouvées ici, mais je ne comprends pas comment la largeur/hauteur de l'écran entre en jeu. Aussi, je ne comprends pas dans l'entrée wiki Quelle est la position du spectateur par rapport à la position de la caméra.

http://en.wikipedia.org/wiki/3D_projection

25
demandé sur Berlin Brown 2011-12-26 09:01:18

8 réponses

la' façon dont c'est fait ' est d'utiliser des transformations et des coordonnées homogènes. Vous prenez un point dans l'espace et:

  • Positionnez-le par rapport à la caméra en utilisant la matrice du modèle.
  • le projeter soit orthographiquement ou en perspective en utilisant la matrice de projection.
  • appliquer la trnasformation de viewport pour la placer sur l'écran.

cela devient assez vague, mais je vais essayer et recouvrez les parties importantes et laissez-en une partie. Je suppose que vous comprenez les bases des mathématiques matricielles :).

Vecteurs Homogènes, Points, Transformations

en 3D, un point homogène serait une matrice de colonne de la forme [x, y, z, 1]. Le dernier composant est 'w', un facteur d'échelle, qui pour les vecteurs est 0: cela a pour effet que vous ne pouvez pas traduire les vecteurs, ce qui est mathématiquement correct. Nous n'allons pas y aller, nous sommes points de discussion.

les transformations homogènes sont des matrices 4x4, utilisées parce qu'elles permettent de représenter la traduction comme une multiplication matricielle, plutôt qu'une addition, ce qui est agréable et rapide pour votre vidéo. Aussi pratique parce que nous pouvons représenter des transformations successives en les multipliant ensemble. Nous appliquons les transformations aux points en effectuant la transformation * point.

il y a 3 transformations primaires homogènes:

il y en a d'autres, notamment la transformation "look at", qui méritent d'être explorées. Cependant, je voulais juste donner une brève liste et quelques liens. L'application Successive du déplacement, de l'échelle et de la rotation appliquée aux points est collectivement la transformation du modèle de la matrice, et les place dans la scène, par rapport à la caméra. Il est important de comprendre que ce que nous faisons s'apparente à déplacer des objets autour de la caméra, et non l'inverse.

orthographe et Perspective

pour transformer les coordonnées du monde en coordonnées d'écran, vous devez d'abord utiliser une matrice de projection, qui généralement, viennent en deux saveurs:

  • orthographique, couramment utilisé pour 2D et CAD.
  • Perspective, bon pour les jeux et les environnements 3D.

une matrice de projection orthographique est construite comme suit:

An orthographic projection matrix, courtesy of Wikipedia.

où les paramètres comprennent:

  • Top : la coordonnée Y du bord supérieur de l'espace visible.
  • Bas : Le Y coordonnées du bord inférieur de l'espace visible.
  • Gauche : La coordonnée X du bord gauche de l'espace visible.
  • Right : coordonnée X du bord droit de l'espace visible.

je pense que c'est assez simple. Ce que vous établissez est une zone d'espace qui va apparaître sur l'écran, que vous pouvez clip contre. C'est simple ici, parce que la zone de l'espace visible est un rectangle. Couper en perspective est plus compliqué parce que la zone qui apparaît sur l'écran ou le volume de visualisation, est un frustrum .

si vous avez un moment difficile avec wikipedia sur la projection en perspective, voici le code pour construire une matrice appropriée, gracieuseté de geeks3D

void BuildPerspProjMat(float *m, float fov, float aspect,
float znear, float zfar)
{
  float xymax = znear * tan(fov * PI_OVER_360);
  float ymin = -xymax;
  float xmin = -xymax;

  float width = xymax - xmin;
  float height = xymax - ymin;

  float depth = zfar - znear;
  float q = -(zfar + znear) / depth;
  float qn = -2 * (zfar * znear) / depth;

  float w = 2 * znear / width;
  w = w / aspect;
  float h = 2 * znear / height;

  m[0]  = w;
  m[1]  = 0;
  m[2]  = 0;
  m[3]  = 0;

  m[4]  = 0;
  m[5]  = h;
  m[6]  = 0;
  m[7]  = 0;

  m[8]  = 0;
  m[9]  = 0;
  m[10] = q;
  m[11] = -1;

  m[12] = 0;
  m[13] = 0;
  m[14] = qn;
  m[15] = 0;
}

, les Variables sont:

  • fov : champ de vision, pi/4 radians est une bonne valeur.
  • aspect : rapport hauteur / largeur.
  • znear, zfar : utilisé pour couper, je vais ignorer ceux-ci.

et la matrice générée est la colonne principale, indexée comme suit dans le code ci-dessus:

0   4   8  12
1   5   9  13
2   6  10  14
3   7  11  15

Transformation De Viewport, Coordonnées D'Écran

ces deux transformations nécessitent une autre matrice pour mettre les choses dans les coordonnées d'écran, appelée la transformation de viewport. c'est décrit ici, Je ne vais pas le couvrir (c'est très simple) .

ainsi, pour un point p, nous:

  • Réaliser un modèle de matrice de transformation * p, résultant en pm.
  • Effectuer une matrice de projection * pm, résultant en pp.
  • L'écrêtage pp contre l'affichage d'un volume.
  • exécute la matrice de transformation de viewport * pp, résultant est ps: point sur l'écran.

résumé

j'espère que couvre la plus grande partie. Il ya des trous dans le ci-dessus et il est vague dans les lieux, postez toutes les questions ci-dessous. Ce sujet est généralement digne d'un chapitre entier dans un manuel, j'ai fait de mon mieux pour distiller le processus, espérons à votre avantage!

j'ai lié à cela ci-dessus, mais je vous suggère fortement de lire ceci, et télécharger le binaire. C'est un excellent outil pour approfondir votre compréhension de ces transformations et comment il obtient des points sur l'écran:

http://www.songho.ca/opengl/gl_transform.html

en ce qui concerne le travail réel, vous aurez besoin de mettre en œuvre une classe de matrice 4x4 pour les transformations homogènes ainsi que d'une classe de point homogène vous pouvez multiplier contre elle pour appliquer des transformations (rappelez-vous, [x, y, z, 1]). Vous devrez générer les transformations décrites ci-dessus et dans les liens. Il n'est pas difficile une fois que vous comprenez la procédure. Bonne chance :).

47
répondu Liam M 2012-01-15 05:01:17

@BerlinBrown tout comme un commentaire général, vous ne devez pas stocker votre rotation de la caméra comme des angles X,Y,Z, car cela peut conduire à une ambiguïté.

par exemple, x=60degrés est le même que -300 degrés. Lorsqu'on utilise x, y et z, le nombre de possibilités ambiguës est très élevé.

plutôt, essayez d'utiliser deux points dans L'espace 3D, x1,y1,z1 pour la localisation de la caméra et x2,y2, z2 pour la cible de la caméra. Les angles peuvent être calculés vers l'arrière à/de la emplacement/cible, mais à mon avis ce n'est pas recommandé. L'utilisation d'un emplacement de caméra/cible vous permet de construire un vecteur "LookAt" qui est un vecteur unitaire dans la direction de la caméra (v'). Vous pouvez également construire une matrice LookAt qui est une matrice 4x4 utilisée pour projeter des objets dans L'espace 3D vers des pixels dans l'espace 2D.

s'il vous Plaît voir cette question relative à la , où j'ai discuter de la façon de calculer le vecteur R, qui est dans le plan orthogonal à l' caméra.

donné un vecteur de votre caméra à la cible, v = xi, yj, zk

Normaliser le vecteur, v' = xi, yj, zk / sqrt (xi^2 + yj^2 + zk^2)
1519130920" U = vecteur mondial U= 0, 0, 1

On peut alors calculer R = vecteur Horizontal parallèle à la direction de vue de la caméra R= v '^ U,

où ^ est le produit transversal, donné par

a ^ b = (a2b3 - a3b2)i + (a3b1 - a1b3)j + (a1b2-a2b1)k

Cela vous donnera un vecteur qui ressemble à ceci.

Computing a vector orthogonal to the camera

cela pourrait être utile pour votre question, car une fois que vous avez le LookAt Vector v', le vecteur orthogonal R Vous pouvez commencer à projeter à partir du point dans L'espace 3D sur le plan de la caméra.

essentiellement tous ces 3D les problèmes de manipulation se résument à transformer un point de l'espace mondial en espace local, où les axes x,y,z locaux sont en orientation avec la caméra. Cela fait-il sens? Donc si vous avez un point, Q=x,y, z et que vous connaissez R et v' (axes de caméra), vous pouvez le projeter sur "l'écran" en utilisant des manipulations vectorielles simples. Les angles impliqués peuvent être trouvés en utilisant l'opérateur de produit dot sur les vecteurs.

Projecting Q onto the screen

10
répondu Dr. ABT 2017-05-23 11:55:04

D'après wikipedia, calculez d'abord "d":

http://upload.wikimedia.org/wikipedia/en/math/6/0/b/60b64ec331ba2493a2b93e8829e864b6.png

pour ce faire, compilez ces matrices dans votre code. Les correspondances entre vos exemples et leurs variables:

θ = Camera.angle*

a = SomePointIn3DSpace

c = Camera.x | y | z

ou, faites les équations séparément sans utiliser de matrices, votre choix:

http://upload.wikimedia.org/wikipedia/en/math/1/c/8/1c89722619b756d05adb4ea38ee6f62b.png

maintenant nous calculons "b", Un point 2D:

http://upload.wikimedia.org/wikipedia/en/math/2/5/6/256a0e12b8e6cc7cd71fa9495c0c3668.png

dans ce cas ex et ey sont les position du spectateur, je crois que dans la plupart des systèmes graphiques la moitié de la taille de l'écran (0.5) est utilisé pour faire (0, 0) le centre de l'écran par défaut, mais vous pouvez utiliser n'importe quelle valeur (play around). ez est l'endroit où le champ de vision entre en jeu. C'est la seule chose que vous avez été absent. Choisir un angle fov et calculer ez comme:

ez = 1 / tan (fov / 2)

enfin, pour obtenir bx et par aux pixels réels, vous devez mettre à l'échelle par un facteur lié à la taille de l'écran. Pour exemple, si les plans b de (0, 0) à (1, 1), vous pourriez juste l'échelle en 1920 x et y par 1080 1920 x 1080 d'affichage. De cette façon, n'importe quelle taille d'écran montrera la même chose. Il y a bien sûr beaucoup d'autres facteurs impliqués dans un système graphique 3D réel, mais c'est la version de base.

5
répondu 2012-01-02 16:36:42

convertir des points dans L'espace 3D en un point 2D sur un écran est simplement fait en utilisant une matrice . Utilisez une matrice pour calculer la position de l'écran de votre point, cela vous épargne beaucoup de travail.

lorsque vous travaillez avec des caméras, vous devriez envisager d'utiliser un "look-at-matrix et de multiplier le regard sur la matrice avec votre matrice de projection.

4
répondu Felix K. 2017-05-23 12:26:09

si la caméra est à (0, 0, 0) et pointée droit devant, les équations seraient:

ScreenData.x = SomePointIn3DSpace.x / SomePointIn3DSpace.z * constant;
ScreenData.y = SomePointIn3DSpace.y / SomePointIn3DSpace.z * constant;

où "constante" est une valeur positive. Réglage de la largeur de l'écran en pixels donne généralement de bons résultats. Si vous le positionnez plus haut, la scène aura l'air plus "zoomée", et vice-versa.

si vous voulez que la caméra soit à une position ou un angle différent, alors vous aurez besoin de se déplacer et tourner la scène de sorte que la caméra est à (0, 0, 0) et pointé droit devant, puis vous pouvez utiliser les équations ci-dessus.

vous calculez essentiellement le point d'intersection entre une ligne qui passe par la caméra et le point 3D, et un plan vertical qui flotte un peu devant la caméra.

3
répondu Thomas 2012-01-06 18:01:05

vous pourriez être intéressé de voir comment GLUT does it dans les coulisses. Toutes ces méthodes ont une documentation similaire qui montre les maths qu'elles contiennent.

les trois premières conférences de UCSD pourraient être très helful, et contiennent plusieurs illustrations sur ce sujet, qui autant que je peux voir est ce que vous êtes vraiment après.

2
répondu Christian Neverdal 2012-01-06 20:20:00

vous voulez transformer votre scène avec une matrice similaire à celle D'OpenGL gluLookAt et ensuite calculer la projection en utilisant une matrice de projection similaire à celle D'OpenGL gluPerspective .

vous pourriez essayer de simplement calculer les matrices et faire la multiplication dans le logiciel.

0
répondu Krumelur 2012-01-02 14:58:51

Exécuter par un lancer de rayons:

traceur de rayons en C# - certains des objets qu'il a vous sembleront familiers; -)

Et juste pour le plaisir d'un LINQ version .

Je ne suis pas sûr de ce que le but principal de votre application est (vous devriez nous dire, il pourrait susciter de meilleures idées), mais bien qu'il soit clair que la projection et le traçage des rayons sont des ensembles de problèmes différents, ils ont une tonne de chevauchement.

si votre application essaie juste de dessiner toute la scène, ce serait génial.

résoudre le problème #1 : les points obscurs ne seront pas projetés.

Solution : bien que je n'ai rien vu sur l'opacité ou la transparence sur la page de blog, vous pourriez probablement ajouter ces propriétés et le code pour traiter un rayon qui a rebondi (comme normal) et qui a continué sur (pour la "transparence").

résoudre le problème #2 : la projection D'un seul pixel nécessitera un tracé complet coûteux de tous les pixels .

Évidemment, si vous voulez juste dessiner les objets, utilisez le traceur de rayon pour ce qu'il est! Mais si vous voulez regarder des milliers de pixels de l'image, du hasard des objets aléatoires (pourquoi?), en faisant un ray-trace complet pour chaque request serait un énorme chien de performance.

heureusement, avec plus de retouches de son code, vous pourriez être en mesure de faire un Ray-tracing à l'avant (avec transparence), et de mettre en cache les résultats jusqu'à ce que les objets changent.

si vous n'êtes pas familier de ray tracing, lire l'entrée du blog - je pense qu'il explique comment les choses fonctionnent vraiment à l'envers de chaque pixel 2D, aux objets, puis les lumières, qui détermine la valeur du pixel.

vous pouvez ajouter du code de façon à ce que les intersections avec des objets soient faites, vous construisez des listes indexées par points intersectés des objets, l'élément étant le pixel 2d courant étant tracé.

ensuite, lorsque vous voulez projeter un point, allez dans la liste de cet objet, trouvez le point le plus proche de celui que vous voulez projeter, et cherchez le pixel 2d qui vous intéresse. Le calcul serait beaucoup plus minime que les équations de vos articles. malheureusement, en utilisant par exemple un dictionnaire votre mappage de structure objet+point en pixels 2d, Je ne suis pas sûr de trouver le point le plus proche sur un objet sans passer par toute la liste des points mappés. Même si ce ne serait pas la chose la plus lente au monde et que tu pourrais probablement le découvrir, je n'ai pas le temps d'y réfléchir. Quiconque?

bonne chance!

aussi, je ne comprends pas dans l'entrée du wiki ce qui est le position vers la position de la caméra "... Je suis sûr à 99% que c'est la même chose.

0
répondu FastAl 2012-01-03 21:22:23