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?
fonte
AB
, não estou convertendo a posição de destino das coordenadas do mundo para as coordenadas da câmera?Dados os três vetores
[camera position]
,[camera direction]
e[object position]
. Calcule o produto escalar de[camera direction]
e[object position]-[camera position]
. Se o resultado for negativo, o objeto estará atrás da câmera.Dado que entendi seu código corretamente, seria:
fonte
Y
éPositionY + (y * Scaling)
. Vou tentar hoje à noiteZ
ser exatamente0
deve ser muito pequena, a maioria dos jogadores nunca a experimentará, e o impacto na jogabilidade nos poucos casos esperados é uma falha visual insignificante. Eu diria que seu tempo é melhor gasto em outro lugar.Teste apenas (projetado. W <0), não (Z <0 || W <0).
Em algumas formulações de espaço de clipe ( tosse OpenGL tosse ), a faixa visível inclui valores Z positivos e negativos, enquanto W é sempre positivo na frente da câmera e negativo atrás.
fonte
E remova esta parte
fonte
Tente um outdoor, que fará com que a foto fique de frente para a câmera e desenhe a foto na tela no local apropriado
fonte