Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Программирование игр, графики и искусственного интеллекта > Как выбрать объект в 3d сцене


Автор: Ravend 22.7.2005, 11:03
Привет, All!

При помощи opengl формирую некоторую 3d сцену с набором объектов, с возможностью маштабирования и вращения.
Теперь требуется по щелчку мышью работать с выбранным объектом (в случае если под курсор попало несколько объектов предлагать выбор из списка).

Кто нибудь подскажет где можно почитать как в opengl произвести выбор объекта при помощи мыши?


Спасибо.

Автор: mr.DUDA 22.7.2005, 22:54
Честно говоря, не знаю как в OpenGL... Во многих движках есть функции типа RayQuery, возвращающие меш или даже треугольник по координатам в отрендеренной картинке для заданного viewport-а.

Автор: chipset 23.7.2005, 02:32
На гейм-дев есть статья http://www.gamedev.ru/articles/?id=20118 smile

Автор: gepard 24.7.2005, 16:09
chipset
Пора отвыкать от этого говноДева.Ру! Там то, как выбирать объекты, если ты их строишь на основе списков OpenGL'а...
Аналитическая геометрий - вот веь, которую должен знать человек + мат. ан. Тогда он может что-то "булькнуть" в разработке игр.
Чтобы выбрать объект, делаем так:
1)Определяем Bounding Box для всех объектов.
2)Все объекты имеют свой id номер, который не повторяется у двух любых объектов.
3)По пунктам в исходник:
Код

void AWorld::objSelection(CPoint point)//Передаются координаты мыши.
{
    //Определяем пару переменных
    double projection[16], modelview[16], wx, wy, wz, vz;
    unsigned int counter = 0, min, n, i,  j, objs_array[256];
    float a, b, c, d, p, ml;
    int viewport[4];
    bool yes = 0;
    //Выдёргиваем значения матриц
    glGetIntegerv(GL_VIEWPORT, viewport);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
         //Читаем глубину пикселя под курсором
    glReadPixels(point.x, point.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &vz);
         //Вычисляем точку курсора в пространстве
    gluUnProject(point.x, point.y - 1, vz, modelview, projection, viewport, &wx, &wy, &wz);//(wx, wy, wz) - координаты курсора в пространстве OpenGL.
    p1.x = (float)wx;//Присваеваем их переменной - структуре, в которой описано только три элемента: float x, y, z; Это глобальная переменная.
    p1.y = (float)wy;
    p1.z = (float)wz;

    p2.x = perspCamera.view.x;//Вторая такая же переменная, которая принемает значения точки, куда смотрит камера. Этот случай работает только в том случае, если курсор находится по середине игрового экрана. В ином случае надо будет вызывать две функции gluUnProject
    p2.y = perspCamera.view.y;
    p2.z = perspCamera.view.z;

    for (j = 0; j < objCount; j++)//Цикл по всем объектам
        for (i = 0; i < 6; i++)//Цикл по всем шести граням Bounding Box'а
        {
                           //Ниже идёт вычисление точки пересечения линии, построенной по двум точкам p1 и p2 и грани Bounding Box'а.
                           //Это всё из аналитической геометрии. Поясню, что objects - указатель на объекты. bbox - указатель на грань Bounding Box'а - структура, элементами которой являются
                           //вершины - vertex - 4 штуки у каждой грани. Каждая вершина - стуртура того же тпи, что и p1(или p2).
            a = ((objects[j].bbox[i].vertex[1].y - objects[j].bbox[i].vertex[0].y)*(objects[j].bbox[i].vertex[2].z - objects[j].bbox[i].vertex[0].z)-
                ( objects[j].bbox[i].vertex[2].y - objects[j].bbox[i].vertex[0].y)*(objects[j].bbox[i].vertex[1].z - objects[j].bbox[i].vertex[0].z));

            b = ((objects[j].bbox[i].vertex[1].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[2].z - objects[j].bbox[i].vertex[0].z)-( objects[j].bbox[i].vertex[2].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[1].z - objects[j].bbox[i].vertex[0].z));

            c = ((objects[j].bbox[i].vertex[1].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[2].y - objects[j].bbox[i].vertex[0].y)-( objects[j].bbox[i].vertex[2].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[1].y - objects[j].bbox[i].vertex[0].y));

            d = (-objects[j].bbox[i].vertex[0].x)*((objects[j].bbox[i].vertex[1].y - objects[j].bbox[i].vertex[0].y)*(objects[j].bbox[i].vertex[2].z - objects[j].bbox[i].vertex[0].z)-( objects[j].bbox[i].vertex[2].y - objects[j].bbox[i].vertex[0].y)*(objects[j].bbox[i].vertex[1].z - objects[j].bbox[i].vertex[0].z))+( objects[j].bbox[i].vertex[0].y)*((objects[j].bbox[i].vertex[1].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[2].z - objects[j].bbox[i].vertex[0].z)-( objects[j].bbox[i].vertex[2].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[1].z - objects[j].bbox[i].vertex[0].z))+(-objects[j]
.bbox[i].vertex[0].z)*    ((objects[j].bbox[i].vertex[1].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[2].y - objects[j].bbox[i].vertex[0].y)-( objects[j].bbox[i].vertex[2].x - objects[j].bbox[i].vertex[0].x)*(objects[j].bbox[i].vertex[1].y - objects[j].bbox[i].vertex[0].y));

            p = (-a*p1.x-b*p1.y - c*p1.z - d)/(a*(p2.x - p1.x) + b*(p2.y - p1.y) + c*(p2.z - p1.z));
 
            crosser.x = (p2.x - p1.x)*p + p1.x;//Итоговая точка пересечения плоскости грани Bounding Box'а и линии, которая построена по двум точкам - crosser
            crosser.y = (p2.y - p1.y)*p + p1.y;
            crosser.z = (p2.z - p1.z)*p + p1.z;

            if(здесь должно написать условие принадлежности точки к многоугольнику(в нашем случае - четырёхугольнику))
            {
                objs_array[counter++] = j;//номер выбранного объекта записываем.
                break;
            }
        }...
}

Там есть ещё код, но это уже дороботки.
Хочу сказать, что самый простой и неэффективный способ выбора объекта, к тому же ещё и самый трудоёмкий для компьюетра, в плане вычислений.

Автор: mr.DUDA 24.7.2005, 19:21
gepard, этот код будет проверять попадание только в прямоугольные области экрана. То есть, если у меня есть mesh, имеющий сложную форму (к примеру - бублик), то данный код вернёт прямоугольный ящик, полностью окружающий мой бублик, но несколько бубликов, вложенных друг в друга под разными углами и смещениями, данный код не обработает.

Другое дело - если есть алгоритм, обрабатывающий полигоны. То есть, нужно пробежаться по bounding box-ам всех mesh-ей, попадающих в проверяемую точку, и далее проверить все полигоны каждого меша, спроецированные в viewport, т.е. "просматриваемые" камерой, в координатах которой мы работаем. Вот тогда есть реальная возможность определить меш (модель) и даже полигон, в который попадает курсор мыши. Пример приводить не стану, т.к всё слишком сильно зависит от используемой модели (D3D, OGL) и движка. К примеру, лично я использую порт Ogre под .NET - там свой собственный алгоритм определения, попала ли точка в заданный меш, и в какой именно полигон меша она попала.

Автор: gepard 25.7.2005, 07:49
Цитата(mr @ 24.7.2005, 19:21)
gepard, этот код будет проверять попадание только в прямоугольные области экрана. То есть, если у меня есть mesh, имеющий сложную форму (к примеру - бублик), то данный код вернёт прямоугольный ящик, полностью окружающий мой бублик, но несколько бубликов, вложенных друг в друга под разными углами и смещениями, данный код не обработает.

Не, друг, ты не прав. Добавь ниже кодом отбор по отдалённости объекта от камеры и будет тебе выбор объектов, наложенных друг на друга. При повороте объекта - персчитывается его Bounding Box, а следовательно угол наклона не будет иметь значения, по своей сути.
Цитата(mr @ 24.7.2005, 19:21)
Другое дело - если есть алгоритм, обрабатывающий полигоны.

Всё до безумия просто: нам просто надо, вместо вершин грани Bounding Box'а указать вершины нужного нам полигона(все по очереди) и получим точку пересечения плоскости полигона и(если он выпуклый) и "линии выбора".

Автор: DENNN 25.7.2005, 10:08
Цитата(mr @ 24.7.2005, 19:21)
то данный код вернёт прямоугольный ящик, полностью окружающий мой бублик, но несколько бубликов, вложенных друг в друга под разными углами и смещениями, данный код не обработает.

Это способ хорошо использовать в качестве первого приближения. С отобранными объектами уже можно произвести аналитические операции.

P.S. найти пересекает ли прямая многоугольник, заданный вершинами не так уж и сложно.

Автор: Ravend 25.7.2005, 11:03
Народ спасибо за ответы, общая идея ясна, но есть ещё вопрос.

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

Вопрос как работать с визуальными координатами объекта?

Автор: DENNN 25.7.2005, 11:21
Вопрос не ясен. Если операция выбора малого объекта уже осуществленна, то в чем тогда проблема?

Автор: Ravend 25.7.2005, 12:30
Да в том то и дело что ещё не осуществлена. Выбор должен происходить даже если клик произошел в никуда, представьте себе при масштабировании объекты уменьшились до размера точек, требуется при клике привязывать (смещать) курсор (обрабатываемые координаты) к ближайшему объекту (для упрощения обрабатываем простые объекты, кубы, сферы)

Автор: gepard 25.7.2005, 12:38
Просто надо поставить ограничение на уменьшение размера Bounding Box'а и всё. Пусть он не будет скажем меньше чем 0,5 0,5 0,5. Вполне достаточно.

Автор: Ravend 25.7.2005, 13:06
Ну это косвенный выход из положения.
Хотелось именно имея массив координат объектов в 3D найти тот объект чьи координаты в 2D проекции наиболее близко расположены к курсору мыши.

Как я представляю нужно из имеющейся 3D матрицы объектов учитывая масштабирование и поворот сцены создать 2D проекцию, что собственно и делает OpenGL но не выводить на экран, а получить некий массив, обрабатывая который можно найти искомый объект.

Автор: gepard 25.7.2005, 13:30
Ну вообщем-то - да. Этот массив: буффер кадра, который храниться в памяти, как картинка - текстура и которую можно проанализировать, но это уже из области распознования образов.

Автор: Ravend 25.7.2005, 14:01
Это Вы говорите об уже сформированном изображении, но ведь у нас есть все составляющие, массив координат объектов, углы поворота, масштаб, из этих данных можно получить искомые данные. А распознавание образов нам тут и не нужно.

Автор: gepard 26.7.2005, 08:36
Я уже изложил свою точку зрения.

Автор: Ravend 26.7.2005, 09:11
Вы изложили но может у когонибудь есть другое мнение?

Автор: DENNN 26.7.2005, 15:34
Есть. И даже несколько smile
1) Сделать так как нужно. Т.е. открыть учебник математики и использовать формулы центральной проекции для перехода из СК мира в СК экрана и поиска точки пересечения луча с плоск. экрана. На крайний случай просто разобраться как OGL перемножает матрицы и сделать то же самое у себя в программе, благо тут ума много не надо.
2) "Схалявить".По принципу Z-буфера закрасить в своем буфере участки экрана учитывая удаленность объекта. Затем проанализировать координаты мыши в этом буфере.
3) "Метод грубой силы". ПРи щелчке отрендерить баундинг-боксы в бек-буфер каждый уникальным цветом с заливкой без освещения и пр.. Цвет пиксела в месте клика мышкой и есть уникальный идентификатор.

Думаю не надо объяснять почему способы расположенны в таком порядке? smile

Автор: mr.DUDA 26.7.2005, 23:29
Среднее - и есть самое оптимальное smile

Автор: DENNN 27.7.2005, 09:28
Цитата(mr @ 26.7.2005, 23:29)
Среднее - и есть самое оптимальное

А вот и нет. Умножение вектора на две видовые матрицы обойдется гораздо дешевле, чем операции по заполнению еще одного буфера с анализом "глубины" перекрываемых пикселов в этом буфере. Важен еще и тот факт, что когда объектов много, то аналитический анализ будет давать больший прирост производительности чем буфер, а строк кода будет заметно меньше smile

Автор: Vitaly333 23.4.2009, 15:12
Цитата

1) Сделать так как нужно. Т.е. открыть учебник математики и использовать формулы центральной проекции для перехода из СК мира в СК экрана и поиска точки пересечения луча с плоск. экрана. На крайний случай просто разобраться как OGL перемножает матрицы и сделать то же самое у себя в программе, благо тут ума много не надо.


А поподробнее можно как это сделать на OpenGL

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)