2010-05-21 4 views
1

Bien que le contexte de cette question soit de faire un jeu 2d/3d, le problème que j'ai se résume à quelques maths. Bien que ce soit un monde 2.5D, faisons semblant que c'est juste 2d pour cette question.J'ai ma conversion 2D/3D fonctionnant parfaitement, comment faire de la perspective

// xa: x-accent, the x coordinate of the projection 
// mapP: a coordinate on a map which need to be projected 
// _Dist_ values are constants for the projection, choosing them correctly will result in i.e. an isometric projection 
xa = mapP.x * xDistX + mapP.y * xDistY; 
ya = mapP.x * yDistX + mapP.y * yDistY; 

xDistX et yDistX déterminer l'angle de l'axe x, et xDistY et yDistY déterminent l'angle de l'axe y sur la saillie (et aussi la taille de la grille, mais laisse supposer c'est la 1- pixel pour la simplicité).

x-axis-angle = atan(yDistX/xDistX) 
y-axis-angle = atan(yDistY/yDistY) 

un système "normal" de coordonnées de ce type

--------------- x 
| 
| 
| 
| 
| 
y 

has values like this: 
xDistX = 1; 
yDistX = 0; 
xDistY = 0; 
YDistY = 1; 

Ainsi, chaque étape dans la direction x résultera sur la projection de 1 pixel vers la droite fin 0 pixels vers le bas. Chaque pas dans la direction y de la projection se traduira par 0 pas vers la droite et 1 pixel vers le bas. Lorsque vous choisissez xDistX, yDistX, xDistY, yDistY corrects, vous pouvez projeter n'importe quel système trimétrique ou dimétrique (c'est pourquoi je l'ai choisi).

Jusqu'ici tout va bien, quand ceci est dessiné, tout se passe bien. Si «mon système» et mon état d'esprit sont clairs, passons à la perspective. Je voulais ajouter une perspective à cette grille donc j'ai ajouté quelques extras comme ceci:

camera = new MapPoint(60, 60); 
dx = mapP.x - camera.x; // delta x 
dy = mapP.y - camera.y; // delta y 
dist = Math.sqrt(dx * dx + dy * dy); // dist is the distance to the camera, Pythagoras etc.. all objects must be in front of the camera 

fac = 1 - dist/100; // this formula determines the amount of perspective 

xa = fac * (mapP.x * xDistX + mapP.y * xDistY) ; 
ya = fac * (mapP.x * yDistX + mapP.y * yDistY); 

Maintenant, le vrai dur ... si vous avez un (xa, ya) point sur la projection et que vous voulez calculer le point d'origine (x, y). Pour le premier cas (sans perspective) j'ai trouvé la fonction inverse, mais comment cela peut-il être fait pour la formule avec la perspective. Que les compétences en mathématiques ne soient pas tout à fait à la hauteur du défi pour résoudre ce problème.

(Je me souviens vaguement d'une longue période Mathematica pourrait y créer la fonction inverse pour certains cas particuliers ... pourrait-il résoudre ce problème? Quelqu'un pourrait-il essayer peut-être?)

+0

Qu'est-ce que 'fac'? Quelle est la "quantité de perspective"? Qu'est-ce que 100 a à voir avec quoi que ce soit? Est-ce que 'fac' est censé implémenter le rétrécissement apparent des objets dû à la perspective. Cela fait que l'objet à la distance 100 a la taille zéro, donc cela n'a pas de sens? – sigfpe

+0

fac signifie facteur, comme dans un facteur de perspective qui peut être ajusté pour la perspective lourde ou peu. Dessiner la grille en utilisant cette fonction a donné quelque chose qui semblait assez bien. Les objets doivent toujours être placés sur "la grille" devant la caméra. Cela signifie qu'un objet ne peut jamais avoir une distance supérieure à sqrt (60^2 + 60^2) 85. La raison pour laquelle je choisis 100 est parce que c'est plus grand que 85, et m'a donné le look dont j'avais besoin. Ma fonction n'est pas correcte, mais le résultat visuel a l'air bien, même si la "perspective" est un peu exagérée ... http://img188.imageshack.us/img188/5154/gridpu.png – jdv145

Répondre

1

La fonction que vous avez défini ne avoir un inverse. Juste à titre d'exemple, comme user207422 a déjà souligné que tout ce qui est à 100 unités de la caméra sera mappé à (xa, ya) = (0,0), donc l'inverse n'est pas défini de façon unique.

Plus important encore, ce n'est pas ainsi que vous calculez la perspective. Généralement, le facteur d'échelle de perspective est défini comme viewdist/zdistzdist est la distance perpendiculaire de la caméra à l'objet et viewdist est une constante qui est la distance entre la caméra et l'écran hypothétique sur lequel tout est projeté. (Voir le diagramme here, mais n'hésitez pas à ignorer tout le reste sur cette page.) Le facteur de mise à l'échelle que vous utilisez dans votre exemple n'a pas le même comportement.

Voici une tentative pour convertir votre code en un calcul de perspective correct (notez que je ne simplifie pas en 2D, la perspective consiste à projeter trois dimensions sur deux, essayer de simplifier le problème en 2D est inutile):

camera = new MapPoint(60, 60, 10); 
camera_z = camera.x*zDistX + camera.y*zDistY + camera.z*zDistz; 

// viewdist is the distance from the viewer's eye to the screen in 
// "world units". You'll have to fiddle with this, probably. 
viewdist = 10.0; 

xa = mapP.x*xDistX + mapP.y*xDistY + mapP.z*xDistZ; 
ya = mapP.x*yDistX + mapP.y*yDistY + mapP.z*yDistZ; 
za = mapP.x*zDistX + mapP.y*zDistY + mapP.z*zDistZ; 

zdist = camera_z - za; 
scaling_factor = viewdist/zdist; 
xa *= scaling_factor; 
ya *= scaling_factor; 

Vous allez seulement revenir xa et ya de cette fonction; za est juste pour le calcul de la perspective. Je suppose que le "za-direction" pointe hors de l'écran, donc si l'axe des x pré-projection pointe vers l'observateur alors zDistX devrait être positif et vice-versa, et de même pour zDistY.Pour une projection trimétrique, vous aurez probablement xDistZ==0, yDistZ<0 et zDistZ==0. Cela ferait en sorte que le point de l'axe z de la pré-projection soit rectiligne après la projection.

Maintenant les mauvaises nouvelles: cette fonction n'a pas non plus d'inverse. Tout point (xa, ya) est l'image d'un nombre infini de points (x, y, z). Mais! Si vous supposez que z = 0, vous pouvez résoudre x et y, ce qui est peut-être suffisant.

Pour ce faire, vous devrez faire de l'algèbre linéaire. Calculer camera_x et camera_y similaire à camera_z. C'est les coordonnées post-transformation de la caméra. Le point sur l'écran a les coordonnées post-transformation (xa,ya,camera_z-viewdist). Dessinez une ligne à travers ces deux points et calculez où intersecte le plan sur lequel s'étendent les vecteurs (xDistX, yDistX, zDistX) et (xDistY, yDistY, zDistY). En d'autres termes, vous devez résoudre les équations:

x*xDistX + y*xDistY == s*camera_x + (1-s)*xa 
x*yDistX + y*yDistY == s*camera_y + (1-s)*ya 
x*zDistX + y*zDistY == s*camera_z + (1-s)*(camera_z - viewdist) 

Ce n'est pas joli, mais cela fonctionnera.

1

Je pense qu'avec votre message je peux résoudre le problème. Pourtant, pour clarifier certaines questions:

Résoudre le problème dans 2d est en effet inutile, mais cela n'a été fait que pour rendre le problème plus facile à saisir (pour moi et pour les lecteurs ici). Mon programme donne en fait une projection 3d parfaite (je l'ai vérifié avec des images 3d rendues avec blender). J'ai cependant laissé quelque chose à propos de la fonction inverse. La fonction inverse est seulement pour les coordonnées entre 0..camera.x * 0.5 et 0 .. camera.y * 0.5. Donc dans mon exemple entre 0 et 30. Mais même alors j'ai des doutes sur ma fonction.

Dans ma projection, l'axe z est toujours droit, donc pour calculer la hauteur d'un objet, je n'ai utilisé que le vieuwingangle. Mais puisque vous ne pouvez pas réellement voler ou sauter dans le ciel, tout n'a que 2 points. Cela signifie aussi que lorsque vous essayez de résoudre les x et y, le z est vraiment 0.

Je sais que toutes les fonctions ont un inverse, et certaines fonctions le font, mais seulement pour un domaine particulier. Ma pensée de base dans tout cela était ... si je peux dessiner une grille en utilisant une fonction ... chaque point de cette grille correspond exactement à un point de la carte. Je peux lire les coordonnées x et y, donc si j'avais juste la fonction correcte, je serais en mesure de calculer l'inverse. Mais il n'y a pas de meilleur remplacement que de bonnes maths solides, et je suis très content que vous ayez pris le temps de donner une réponse très utile :).

+2

Juste pour l'avenir référence, cela aurait normalement été dans une édition à votre question, ou peut-être dans les commentaires. – Joren

Questions connexes