Я роблю 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
(для мене це має сенс. не працює)
Але досі не знайшли послідовного способу, з яким усі мої моменти правильно проектуються.
Будь-які ідеї?