Estou fazendo um jogo em 3D no qual coloco um marcador de exclamação acima dos pontos de interesse.
Para descobrir onde devo colocar meu marcador na tela 2D, estou projetando manualmente o ponto 3D onde o marcador deve estar.
Se parece com isso:
Parece muito bom. Quando o marcador está fora da tela, eu simplesmente recorte as coordenadas para que elas se ajustem à tela. Se parece com isso:
Até agora, a ideia está indo muito bem. No entanto, quando os pontos de interesse estão atrás da câmera, as coordenadas X, Y resultantes são invertidas (como positivas / negativas) e faço com que o marcador apareça no canto oposto da tela, assim:
(O ponto projetado e então fixado é a ponta do marcador. Não se importe com a rotação do marcador)
Faz sentido, já que as coordenadas atrás do tronco são invertidas em X e Y. Então, o que estou fazendo é inverter as coordenadas quando elas estão atrás da câmera. No entanto, ainda não sei qual é exatamente a condição em que as coordenadas devem ser invertidas.
Atualmente, é assim que meu código de projeção se parece (em C # com 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 você pode ver, minha ideia atual é inverter as coordenadas quando as coordenadas Z ou W são negativas. Funciona na maioria das vezes, mas ainda existem alguns locais de câmera muito específicos nos quais não funciona. Em particular, este ponto mostra uma coordenada funcionando e a outra não (o local correto deve ser o canto inferior direito):
Eu tentei inverter quando:
Z
é negativo (é isso que faz mais sentido para mim)W
é negativo (não entendo o significado de um valor W negativo)- Quer
Z
ouW
é negativo (o que está actualmente a trabalhar na maioria das vezes) Z
eW
são de sinal diferente, também conhecido como:Z / W < 0
(faz sentido para mim. embora não funcione)
Mas ainda não encontrei uma maneira consistente com a qual todos os meus pontos sejam projetados corretamente.
Alguma ideia?