Проблема "Экран - мир" на iPhone e

У меня есть 3D-модель (CUBE), отображаемая в EAGLView, и я хочу иметь возможность определять, когда я касаюсь центра данной грани (под любым углом ориентации) куба. Звучит довольно просто, но это не так ...

Проблема:
Как мне точно связать координаты экрана (точку касания) с мировыми координатами (местоположение в 3D-пространстве OpenGL)? Конечно, преобразование заданной точки в «процент» от экрана / мировой оси может показаться логичным решением, но проблемы могут возникнуть, когда мне нужно увеличить или повернуть трехмерное пространство. Примечание: вращение и увеличение и уменьшение трехмерного пространства изменит отношения координат 2D-экрана с мировыми координатами 3D ... Кроме того, вам придется учитывать «расстояние» между точкой обзора и объектами в 3D-пространстве. . Поначалу это может показаться «легкой задачей», но все меняется, когда вы фактически исследуете требования. И я не нашел примеров, чтобы люди делали это на iPhone. Как это обычно делается?

«Легкая» задача?
Конечно, можно было бы взять на себя задачу написания API, который действовал бы как посредник между экраном и миром, но задача создания такой структуры потребовала бы серьезного проектирования и, вероятно, потребовало бы «времени», чтобы сделать - НЕ то, что могу быть обслуживаемым одним человеком за 4 часа ... А 4 часа - это мой крайний срок.

Вопрос:

  • Каковы самые простые способы узнать, коснулся ли я определенных мест в трехмерном пространстве в мире iPhone OpenGL ES?
5
RexOnRoids 2 Июл 2009 в 10:26

5 ответов

Лучший ответ

Представляются два решения. Оба они должны достичь конечной цели, хотя и разными способами: вместо того, чтобы ответить «какая мировая координата находится под мышью?», Они отвечают на вопрос «какой объект отображается под мышью?».

Один из них - нарисовать упрощенную версию вашей модели во внеэкранном буфере, визуализируя центр каждой грани с использованием отдельного цвета (и регулируя освещение так, чтобы цвет сохранялся одинаково). Затем вы можете обнаружить эти цвета в буфере (например, растровом изображении) и сопоставить с ними расположение мыши.

Другой - использовать выбор OpenGL. здесь. Основная идея состоит в том, чтобы перевести OpenGL в режим выбора, ограничить область просмотра небольшим окном (возможно, 3x3 или 5x5) вокруг интересующей точки, а затем визуализировать сцену (или ее упрощенную версию) с использованием «имен» OpenGL (целое число идентификаторы) для идентификации компонентов, составляющих каждое лицо. В конце этого процесса OpenGL может предоставить вам список имен, которые были отображены в области просмотра выбора. Сопоставление этих идентификаторов с исходными объектами позволит вам определить, какой объект находится под курсором мыши.

1
Michael Ekstrand 11 Июл 2009 в 16:11

Теперь вы можете найти gluUnProject в http://code.google.com / p / iphone-glu /. У меня нет ассоциации с проектом iphone-glu и сам еще не пробовал, просто хотел поделиться ссылкой.

Как бы вы использовали такую ​​функцию? В этом PDF-файле упоминается следующее:

Подпрограмма библиотеки утилит gluUnProject () выполняет эту обратную трансформацию. Учитывая трехмерные координаты окна для местоположения и все преобразования, которые повлияли на них, gluUnProject () возвращает мировые координаты от того места, где оно возникло.

int gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz, 
const GLdouble modelMatrix[16], const GLdouble projMatrix[16], 
const GLint viewport[4], GLdouble *objx, GLdouble *objy, GLdouble *objz);

Сопоставьте указанные координаты окна (winx, winy, winz) с координатами объекта, используя преобразования, определенные матрицей вида модели (modelMatrix), матрицей проекции (projMatrix) и окном просмотра (viewport). Полученные координаты объекта возвращаются в форматах objx, objy и objz. Функция возвращает GL_TRUE , указывающее на успех, или GL_FALSE , указывающее на сбой (например, необратимую матрицу). Эта операция не пытается обрезать координаты в области просмотра или исключать значения глубины, выходящие за пределы glDepthRange () .

Попытки обратить процесс трансформации вспять сопряжены с определенными трудностями. Двумерное местоположение на экране могло появиться из любого места на всей строке в трехмерном пространстве. Чтобы устранить неоднозначность результата, gluUnProject () требует, чтобы была указана координата глубины окна (winz) и чтобы winz был указан в терминах glDepthRange (). Для значений по умолчанию glDepthRange (), winz с 0,0 будет запрашивать мировые координаты преобразованной точки в ближайшей плоскости отсечения, а winz с 1,0 будет запрашивать точку в дальней плоскости отсечения.

Пример 3-8 (снова см. PDF) демонстрирует gluUnProject (), считывая положение мыши и определяя трехмерные точки в ближней и дальней плоскостях отсечения, из которых оно было преобразовано. Вычисленные мировые координаты выводятся на стандартный вывод, но само отображаемое окно просто черное.

Что касается производительности, я быстро нашел это через Google в качестве пример того, что вы, возможно, не захотите делать с помощью gluUnProject, со ссылкой на что может привести к лучшей альтернативе. Я понятия не имею, насколько это применимо к iPhone, так как я все еще новичок в OpenGL ES. Спроси меня снова через месяц. ;-)

7
Louis St-Amour 15 Дек 2009 в 07:54

У вас должны быть матрицы проекции opengl и просмотра модели. Умножьте их, чтобы получить матрицу проекции вида модели. Инвертируйте эту матрицу, чтобы получить матрицу, которая преобразует координаты пространства клипа в мировые координаты. Преобразуйте точку касания так, чтобы она соответствовала координатам клипа: центр экрана должен быть равен нулю, а края должны быть + 1 / -1 для X и Y соответственно.

Построить две точки, одну в (0,0,0) и одну в (touch_x, touch_y, -1), и преобразовать обе с помощью обратной матрицы проекции вида модели.

Сделайте обратное деление перспективы.

У вас должны получиться две точки, описывающие линию от центра камеры до «дальнего расстояния» (дальняя плоскость).

Делайте пикировку на основе упрощенных ограничивающих рамок ваших моделей. Вы сможете найти множество алгоритмов пересечения лучей и прямоугольников в сети.

Другое решение - раскрасить каждую из моделей немного другим цветом в закадровом буфере и считывать цвет в точке касания оттуда, сообщая вам, какой из них был затронут.

Вот источник курсора, который я написал для небольшого проекта, использующего физику пули:

float x=((float)mpos.x/screensize.x)*2.0f -1.0f;
    float y=((float)mpos.y/screensize.y)*-2.0f +1.0f;
    p2=renderer->camera.unProject(vec4(x,y,1.0f,1));
    p2/=p2.w;
    vec4 pos=activecam.GetView().col_t;
    p1=pos+(((vec3)p2 - (vec3)pos) / 2048.0f * 0.1f);
    p1.w=1.0f;

    btCollisionWorld::ClosestRayResultCallback rayCallback(btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z));
    game.dynamicsWorld->rayTest(btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z), rayCallback);
    if (rayCallback.hasHit())
    {
        btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject);
        if(body==game.worldBody)
        {
            renderer->setHighlight(0);
        }
        else if (body)
        {
            Entity* ent=(Entity*)body->getUserPointer();

            if(ent)
            {
                renderer->setHighlight(dynamic_cast<ModelEntity*>(ent));
                //cerr<<"hit ";
                //cerr<<ent->getName()<<endl;
            }
        }
    }
3
heeen 11 Июл 2009 в 16:51

Представьте себе линию, идущую от глаз зрителя
через точку касания экрана в пространство вашей 3D-модели.

Если эта линия пересекает любую из граней куба, значит, пользователь коснулся куба.

2
Rhythmic Fistman 2 Июл 2009 в 08:23

Google для opengl screen to world (например, есть ветка где кто-то хочет делать именно то, что вы ищете, на GameDev.net < / а>). Существует функция gluUnProject, которая делает именно это, но она недоступно на iPhone, поэтому вам необходимо перенести его (см. этот источник из проекта Mesa). Или, может быть, где-то уже есть какой-то общедоступный источник?

0
zoul 11 Июл 2009 в 05:42