Tworzę grę 3D, w której umieszczam wykrzyknik nad interesującymi miejscami.
Aby dowiedzieć się, gdzie na ekranie 2D powinienem umieścić znacznik, ręcznie rzutuję punkt 3D, w którym powinien znajdować się znacznik.
To wygląda tak:
Wygląda całkiem nieźle. Kiedy znacznik znajduje się poza ekranem, po prostu przycinam współrzędne, aby pasowały do ekranu. To wygląda tak:
Jak dotąd pomysł idzie całkiem nieźle. Jednak gdy punkty zainteresowania znajdują się za kamerą, powstałe współrzędne X, Y są odwrócone (jak w dodatnich / ujemnych), a znacznik pojawia się w przeciwległym rogu ekranu, tak jak poniżej:
(Rzutowany następnie zaciśnięty punkt jest końcem markera. Nie przeszkadza to w obrocie markera)
Ma to sens, ponieważ współrzędne za ścięciem są odwrócone w X i Y. Więc robię to, aby odwrócić współrzędne, gdy znajdują się za kamerą. Jednak wciąż nie wiem, jaki jest dokładnie warunek, w którym należy odwrócić współrzędne.
Tak właśnie wygląda mój kod projekcji (w C # z SharpDX):
public override PointF ProjectPosition(float viewportWidth, float viewportHeight, float y)
{
var projectionMatrix = Matrix.PerspectiveFovRH(GetCalibratedFOV(Camera.FOV, viewportWidth, viewportHeight), viewportWidth / viewportHeight, Camera.Near, Camera.Far);
var viewMatrix = Matrix.LookAtRH(new Vector3(Camera.PositionX, Camera.PositionY, Camera.PositionZ), new Vector3(Camera.LookAtX, Camera.LookAtY, Camera.LookAtZ), Vector3.UnitY);
var worldMatrix = Matrix.RotationY(Rotation) * Matrix.Scaling(Scaling) * Matrix.Translation(PositionX, PositionY, PositionZ);
var worldViewProjectionMatrix = worldMatrix * viewMatrix * projectionMatrix;
Vector4 targetVector = new Vector4(0, y, 0, 1);
Vector4 projectedVector = Vector4.Transform(targetVector, worldViewProjectionMatrix);
float screenX = (((projectedVector.X / projectedVector.W) + 1.0f) / 2.0f) * viewportWidth;
float screenY = ((1.0f - (projectedVector.Y / projectedVector.W)) / 2.0f) * viewportHeight;
float screenZ = projectedVector.Z / projectedVector.W;
// Invert X and Y when behind the camera
if (projectedVector.Z < 0 ||
projectedVector.W < 0)
{
screenX = -screenX;
screenY = -screenY;
}
return new PointF(screenX, screenY);
}
Jak widać, moim obecnym pomysłem jest odwrócenie współrzędnych, gdy współrzędne Z lub W są ujemne. Działa prawie przez większość czasu, ale wciąż istnieją pewne bardzo specyficzne lokalizacje kamer, w których nie działa. W szczególności ten punkt pokazuje jedną współrzędną działającą, a drugą nie (poprawna lokalizacja powinna być w prawym dolnym rogu):
Próbowałem odwrócić, gdy:
Z
jest negatywny (to jest dla mnie najbardziej sensowne)W
jest ujemny (nie rozumiem znaczenia ujemnej wartości W)- Albo
Z
alboW
jest negatywne (co obecnie działa przez większość czasu) Z
iW
mają inny znak, alias:Z / W < 0
(ma dla mnie sens. choć nie działa)
Ale wciąż nie znalazłem spójnego sposobu, w jaki wszystkie moje punkty są poprawnie rzutowane.
Jakieś pomysły?