Estoy haciendo un juego en 3D en el que pongo un marcador de exclamación sobre los puntos de interés.
Para averiguar en qué lugar de la pantalla 2D debo colocar mi marcador, estoy proyectando manualmente el punto 3D donde debería estar el marcador.
Se parece a esto:
Se ve bastante bien. Cuando el marcador está fuera de la pantalla, simplemente recorto las coordenadas para que quepan en la pantalla. Se parece a esto:
Hasta ahora la idea está yendo bastante bien. Sin embargo, cuando los puntos de interés están detrás de la cámara, las coordenadas X, Y resultantes se invierten (como en positivo / negativo), y obtengo el marcador para que aparezca en la esquina opuesta de la pantalla, así:
(El punto proyectado y luego sujeto es la punta del marcador. No importa la rotación del marcador)
Tiene sentido, ya que las coordenadas detrás del tronco están invertidas en X e Y. Entonces, lo que estoy haciendo es invertir las coordenadas cuando están detrás de la cámara. Sin embargo, todavía no sé cuál es exactamente la condición cuando las coordenadas deben invertirse.
Así es actualmente mi código de proyección (en C # con 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);
}
Como puede ver, mi idea actual es invertir las coordenadas cuando las coordenadas Z o W son negativas. Funciona la mayor parte del tiempo, pero todavía hay algunas ubicaciones de cámara muy específicas donde no funciona. En particular, este punto muestra una coordenada funcionando y la otra no (la ubicación correcta debe ser la esquina inferior derecha):
He intentado invertir cuando:
Z
es negativo (esto es lo que tiene más sentido para mí)W
es negativo (no entiendo el significado de un valor W negativo)- Ya sea
Z
oW
es negativo (lo que está funcionando actualmente la mayor parte del tiempo) Z
yW
son de signo diferente, alias:Z / W < 0
(tiene sentido para mí, aunque no funciona)
Pero todavía no he encontrado una manera consistente con la cual todos mis puntos se proyecten correctamente.
¿Algunas ideas?