Ich mache ein 3D-Spiel, in dem ich einen Ausrufezeichen über den Points of Interest platziere.
Um herauszufinden, wo auf dem 2D-Bildschirm ich meinen Marker platzieren soll, projiziere ich manuell den 3D-Punkt, an dem sich der Marker befinden soll.
Es sieht aus wie das:
Sieht sehr gut aus. Wenn sich die Markierung außerhalb des Bildschirms befindet, schneide ich die Koordinaten einfach so ab, dass sie in den Bildschirm passen. Es sieht aus wie das:
Bisher läuft die Idee ziemlich gut. Wenn sich die interessierenden Punkte jedoch hinter der Kamera befinden, werden die resultierenden X-, Y-Koordinaten invertiert (wie positiv / negativ), und ich erhalte die Markierung so, dass sie in der gegenüberliegenden Ecke des Bildschirms angezeigt wird:
(Der projizierte und dann geklemmte Punkt ist die Spitze des Markers. Beachten Sie die Drehung des Markers nicht.)
Es ist sinnvoll, da die Koordinaten hinter dem Kegelstumpf in X und Y invertiert sind. Ich invertiere also die Koordinaten, wenn sie sich hinter der Kamera befinden. Ich weiß jedoch immer noch nicht, unter welchen Bedingungen die Koordinaten invertiert werden sollen.
So sieht mein Projektionscode derzeit aus (in C # mit 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);
}
Wie Sie sehen können, besteht meine aktuelle Idee darin, die Koordinaten zu invertieren, wenn entweder die Z- oder die W-Koordinaten negativ sind. Es funktioniert meistens, aber es gibt immer noch einige sehr spezifische Kamerastandorte, an denen es nicht funktioniert. Insbesondere zeigt dieser Punkt, dass eine Koordinate arbeitet und die andere nicht (die richtige Position sollte unten rechts sein):
Ich habe versucht umzukehren, wenn:
Z
ist negativ (das macht für mich am meisten Sinn)W
ist negativ (ich verstehe die Bedeutung eines negativen W-Wertes nicht)- Entweder
Z
oderW
ist negativ (was derzeit die meiste Zeit funktioniert) Z
undW
haben ein anderes Vorzeichen, auch bekannt als:Z / W < 0
(macht für mich Sinn. funktioniert aber nicht)
Aber ich habe immer noch keinen konsistenten Weg gefunden, mit dem alle meine Punkte korrekt projiziert werden.
Irgendwelche Ideen?