Como faço para projetar corretamente um ponto atrás da câmera?

10

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:

Marcadores impressionantes

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:

Marcadores que parecem ainda mais impressionantes

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:

Marcadores não tão impressionantes

(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):

Marcadores meio impressionantes

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 Zou Wé negativo (o que está actualmente a trabalhar na maioria das vezes)
  • Ze Wsã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?

Panda Pajama
fonte

Respostas:

2

Que tal fazer um pouco diferente?

Comece como de costume e renderize todos os marcadores na viewport. Se um marcador estiver fora da janela de visualização - continue:

  1. Obter posições do marcador A
  2. Obter posição da câmera B(talvez um pouco na frente dela)
  3. Calcular o vetor 3D ABentre esses dois
  4. Converta e normalize esse vetor em limites de tela 2D ( Aestará no centro da vista e Batingirá uma borda da vista)

insira a descrição da imagem aqui

Linhas de cores são ABvetores. Linhas pretas finas são alturas de caracteres. À direita está a tela 2D

  1. Talvez adicione uma regra: tudo o que está por trás da câmera sempre fica na borda inferior
  2. Desenhe o marcador no espaço da tela em relação aos tamanhos e sobreposições dos marcadores

Pode ser necessário tentar isso para ver se o marcador que sai da janela de exibição salta entre as posições. Se isso acontecer, você pode tentar ler a posição quando o marcador se aproximar da borda da janela de visualização.

Kromster
fonte
Como exatamente essa etapa 4 funcionaria?
Panda Pyjama
@PandaPajama: eu adicionei um pouco à resposta. Está mais claro agora?
Kromster
Na verdade não. Como você está "convertendo e normalizando" o vetor? Se isso é através da aplicação de uma matriz de projeção, então você está fazendo a mesma coisa que eu estou fazendo
Panda Pajama
@PandaPajama: Até onde eu entendo, você está fazendo isso no espaço da câmera (daí o problema ZW negativo). Sugiro fazê-lo no espaço do mundo e só depois converter para o espaço da tela.
Kromster 14/05
Mas, calculando AB, não estou convertendo a posição de destino das coordenadas do mundo para as coordenadas da câmera?
Panda Pyjama
1

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:

if((PositionX - Camera.PositionX) * Camera.LookAtX
    + (PositionY - Camera.PositionY) * Camera.LookAtY
    + (PositionZ - Camera.PositionZ) * Camera.LookAtZ
    < 0)
aaaaaaaaaaaa
fonte
Yé PositionY + (y * Scaling). Vou tentar hoje à noite
Panda Pyjama
Com isso, posso realmente saber quando o ponto está por trás da câmera. No entanto, isso não impede que a projeção atinja uma singularidade em Z = 0.
Panda Pajama
@PandaPajama Essa é uma questão prática? A probabilidade de Zser exatamente 0deve 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.
Aaaaaaaaaaaa
0

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.

gaio
fonte
Eu tentei com apenas W <0, mas ainda existem lugares onde ele é projetado incorretamente.
Panda Pyjama
0
float screenX = (((projectedVector.X / abs(projectedVector.W)) + 1.0f) / 2.0f) * viewportWidth;
float screenY = ((1.0f - (projectedVector.Y / abs(projectedVector.W))) / 2.0f) * viewportHeight;

E remova esta parte

// Invert X and Y when behind the camera
if (projectedVector.Z < 0 || projectedVector.W < 0)
{
    screenX = -screenX;
    screenY = -screenY;
}
BiiG
fonte
-2

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

Michael Langford
fonte
3
Você leu a pergunta?
Kromster
Desculpe, deixe-me explicar - é possível pegar a saída de um outdoor, livrar-se da escala (por isso é 2D, mas segue a posição 3d) e, em seguida, mantê-lo nos limites da tela usando algumas matemáticas da matriz.
Michael Langford
A questão é especificamente sobre o "e, em seguida, mantê-lo nos limites da tela usando alguma matemática matriz "
Kromster