iPhone OS - преобразование пространства в мир - PullRequest
0 голосов
/ 21 февраля 2010

В настоящее время я пытаюсь преобразовать касание из экранного пространства в то место, где оно находится в мировом пространстве для 2D-игры, над которой я работаю.

Вид, над которым я работаю, имеет почти ту же систему координат, что и экран, в основном, если кто-то коснется x = 345, y = 500 пикселей на экране, он будет таким же на виде, хотя y назад, потому что opengl использует левый нижний угол вместо 0 в левом верхнем углу.

«Камера» имеет 0,0,0, смотря на отрицательный Z. Я говорю «Камера», так как я еще не закодировал ее. Сейчас я просто перевожу все спрайты Z на -100.

Псевдокод, который я пробовал до сих пор (и дважды проверял в mathematica), таков:

// scale the screen point into a value from 0 to 1
point = { screenPoint.x / screenWidth, screenPoint.y / screenHeight, -100, 1 }
// doing a full inverse here, not just swapping rows and cols
out = Inverse(viewProjection) * point
inverseW = 1 / out.w
finalWorldCoord = out * inverseW

Проблема в том, что это дает мне значения, которые намного меньше, чем они должны быть, и я не уверен почему.

Это с OpenGL ES 2.0, на iPhone OS 3.2.

Кто-нибудь знает правильный способ сделать это?

Ответы [ 2 ]

0 голосов
/ 24 февраля 2010

Придумали решение, которое не использует инверсию матрицы проекции.
Несколько заметок для тех, кто находит это, прибегая к помощи Это предполагает матрицу представления с идентичностью, в 0,0,0. Поскольку у меня нет камеры, это означает, что я просто вычисляю точки в ближней и дальней плоскостях, а затем сразу перехожу к выполнению теста пересечения плоскости лучей. Если у вас есть матрица вида, вам нужно будет умножить точки в ближней и дальней плоскости на обратную матрицу вида.

-

(void) touchToWorld:(CGPoint*)screenLocation andZCoordForPlane: (GLfloat) zValue
{
    BGAssert([[GameManager sharedInstance] renderer] != nil, @"renderer is nil");
    BGAssert(screenLocation != NULL, @"location is NULL");
    GLint screenWidth = [[[GameManager sharedInstance] renderer] backingWidth];
    BGAssert(screenWidth > 0.0f, @"screen width is <= 0");
    GLint screenHeight = [[[GameManager sharedInstance] renderer] backingHeight];
    BGAssert(screenHeight > 0.0f, @"screen height <= 0");
    GLfloat aspect = [[[GameManager sharedInstance] renderer] aspect];
    BGAssert(aspect > 0.0f, @"aspect ratio is <= 0");
    GLfloat fov = [[[GameManager sharedInstance] renderer] fov];
    BGAssert(fov > 0.0f, @"fov is <= 0");

    GLfloat near = [[[GameManager sharedInstance] renderer] nearplane];
    GLfloat far = [[[GameManager sharedInstance] renderer] farplane];

    // convert to GL coordinates
    GLfloat newX = (screenLocation->x / (screenWidth / 2.0f) - 1) * aspect;
    GLfloat newY = 1.0f - (screenLocation->y / (screenHeight / 2.0f));

    GLfloat fovInRadians = fov * (PI / 180.0f);
    GLfloat ratioX = tanf(fovInRadians / 2.0f) * newX;
    GLfloat ratioY = tanf(fovInRadians / 2.0f) * newY;

    ESVector3 pointOnNearPlane;
    ESVector3 pointOnFarPlane;

    memset(&pointOnNearPlane, 0, sizeof(ESVector3));
    memset(&pointOnFarPlane, 0, sizeof(ESVector3));

    pointOnNearPlane.v[0] = ratioX * near;
    pointOnNearPlane.v[1] = ratioY * near;
    pointOnNearPlane.v[2] = near;
    pointOnNearPlane.v[3] = 1.0f;

    pointOnFarPlane.v[0] = ratioX * far;
    pointOnFarPlane.v[1] = ratioY * far;
    pointOnFarPlane.v[2] = far;
    pointOnFarPlane.v[3] = 1.0f;

    ESVector3 lineBetweenNearAndFarPlane;
    memset(&lineBetweenNearAndFarPlane, 0, sizeof(ESVector3));
    esVec3Sub(&lineBetweenNearAndFarPlane, &pointOnFarPlane, &pointOnNearPlane); 

    // we need to do ray to plane. Point on near plane is the rays origin
    // normalized direction is the rays direction
    ESVector3 normalizedDirection;
    memset(&normalizedDirection, 0, sizeof(ESVector3));
    esVec3Normalize(&normalizedDirection, &lineBetweenNearAndFarPlane);

    ESVector4 plane;
    memset(&plane, 0, sizeof(ESVector4));

    plane.v[0] = 0.0f;
    plane.v[1] = 0.0f;
    plane.v[2] = 1.0f;
    plane.v[3] = zValue;


    GLfloat vd = esVec3Dot((ESVector3*)&plane, &normalizedDirection);
    GLfloat v0 = -(esVec3Dot((ESVector3*)&plane, &pointOnNearPlane) + plane.v[3]);
    GLfloat t = v0 / vd;
    ESVector3 intersectPoint;
    memset(&intersectPoint, 0, sizeof(ESVector3));

    intersectPoint.v[0] = pointOnNearPlane.v[0] + normalizedDirection.v[0] * t;
    intersectPoint.v[1] = pointOnNearPlane.v[1] + normalizedDirection.v[1] * t;
    intersectPoint.v[2] = pointOnNearPlane.v[2] + normalizedDirection.v[2] * t;

    point.x = intersectPoint.v[0];
    point.y = intersectPoint.v[1];
}
0 голосов
/ 21 февраля 2010

Я думаю, что для начала вы хотите получить очки изначально, используя -[NSView convertPoint:toView:] и подавать в вашем самом верхнем представлении. Это даст вам абсолютные координаты экрана.

В противном случае я предлагаю установить контрольные точки / NSLogs, чтобы наблюдать за преобразованием значений, чтобы вы могли видеть, где числа теряют свой правильный масштаб.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...