Я делаю 3D-игру, в которой я ставлю восклицательный знак над достопримечательностями.
Чтобы узнать, где на 2D-экране я должен поместить свой маркер, я вручную проецирую 3D-точку, где должен быть маркер.
Это выглядит так:
Выглядит довольно хорошо Когда маркер находится за пределами экрана, я просто обрезаю координаты, чтобы они помещались на экране. Это выглядит так:
Пока идея идёт довольно хорошо. Однако, когда точки интереса находятся за камерой, результирующие координаты X, Y инвертируются (как в положительном / отрицательном), и я получаю маркер для отображения в противоположном углу экрана, например так:
(Спроецированная, затем зажатая точка - это кончик маркера. Не обращайте внимания на вращение маркера)
Это имеет смысл, так как координаты за усечкой инвертированы в X и Y. Так что я делаю, чтобы инвертировать координаты, когда они находятся за камерой. Тем не менее, я до сих пор не знаю, что именно является условием, когда координаты должны быть инвертированы.
Вот как выглядит мой код проекции (в C # с 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);
}
Как вы можете видеть, моя текущая идея - инвертировать координаты, когда координаты Z или W отрицательны. Это работает - большую часть времени, но все еще есть некоторые очень специфические местоположения камеры, где это не работает. В частности, эта точка показывает, что одна координата работает, а другая нет (правильное местоположение должно быть внизу справа):
Я пытался инвертировать, когда:
Z
отрицателен (это то, что имеет наибольшее значение для меня)W
отрицательно (я не понимаю значения отрицательного значения W)- Либо
Z
илиW
отрицательно (что в настоящее время работает большую часть времени) Z
иW
имеют разные признаки, иначе:Z / W < 0
(имеет смысл для меня. хотя не работает)
Но до сих пор не нашли последовательного способа, с помощью которого все мои очки правильно проецируются.
Любые идеи?