Алгоритм для terrain
Fido Themes (ru.algorithms)
От: Alexey Skoufyin <skuf@cps.tver.ru>
Тема: Алгоритм для terrain
Дата: 15 марта 2001 г. 18:30
Привет, все-все-все!
Вот сидел тут, думал, думал и придумал алгоритм для рисования terrain-ов, основанный
на использовании quadtrees. Возможно я изобрёл велосипед, но кто знает, может
и что-то оригинальное ;) Помогите "обсосать" до готовности, а?
Итак, вот вкратце идея: Сведём наш 3d-террейн к 2d-случаю, т.е. посмотрим на
него сверху ;)
* Структура данных
Квадратичное дерево (quadtree). В корневом узле весь тэррейн хранится в виде
1 квадратного полигона, у каждого из его 4-х дочерних узлов хранится по четвертинке
террейна, представленных с более высоким уровнем детализации, и так далее, до
определённого критерия (скажем, размер квадратика стал < 1m) со всё возрастающим
уровнем детализации. На самом нижнем уровне хранятся кусочки террэйна с _реальной_
детализацией. Плюс, к этому в каждом узле хранится габаритный куб для поддерева
начинающегося в данном узле. В результате, на каждом из уровней quadtree у нас
на terrain как бы наложена X/Z сетка, с каждым новым уровнем её шаг уменьшается
вдвое.
* Алгоритм (на псевдокоде)
РисоватьУзел( frustum, узел, глубина, макс_глубина )
{
если глубина == макс_глубина
выход;
если габаритный_куб_виден(frustum,узел) {
для каждого подузла дерева (i=0..3) {
дистанция = дистанция_до_камеры( узел->подузел[i].центр );
если дистанция < макс_дистанция {
макс_глубина = глубина_для_дистанции(дистанция);
РисоватьУзел( frustum, узел->подузел[i],
глубина+1, макс_глубина );
}
}
}
}
РисоватьТеррейн()
{
РисоватьУзел(frustum,дерево->корень,1,999999);
}
* "+" и "-"
+ Высокая производительность клиппинга, т.к. отсекаются сразу большие части
terrain
+ Автоматический LOD геометрии, не требующий вычислительных затрат
+ Оптимальный поиск в дереве, т.к. оно идеально сбалансировано
+ Возможность сделать LOD-текстуры (для каждого уровня дерева - свой набор)
+ При небольшой доработке дружит с BSP+PVS, можно скомбинировать как in- так
и outdoor environments
+ Возможность поддерживать кол-во треугольников отводимых под terrain на экране
на примерно постоянном уровне
+ Быстрый collision detection, т.к. границы всех квадратов ориентированы вдоль
координатных осей
+ Возможность быстрого манипулирования геометрией уровня, т.е. воронки от взрывов,
следы шин и т.п.
+ Легко реализуется динамическая подгрузка частей уровня
+ Можно делать уровни в любом редакторе 3d-графики
- Большой объём данных (геометрия для всех уровней)
- Проблема со стыковкой соседних квадратов с разной детализацией
- Тест на видимость габаритного куба нельзя свести к проверке 8 крайних точек,
т.к. frustum может находиться внутри куба
- Проблема с пещерами и т.д. "впуклостями"
Может, я чего не учёл?? Давайте обсудим, а то я варюсь в своём соку...
(с) (может быть ;) Скуф
15-мар-200

От: Jenya Pavlovsky <Jenya.Pavlovsky@p2.f324.n5030.z2.fidonet.org>
Тема: Алгоритм для terrain
Дата: 16 марта 2001 г. 20:44
Привет, Alexey!
Thursday March 15 2001, Alexey Skoufyin wrote to All:
AS> Вот сидел тут, думал, думал и придумал алгоритм для
рисования terrain-ов,
AS> основанный на использовании quadtrees. Возможно я изобрёл велосипед,
но
AS> кто знает, может и что-то оригинальное ;) Помогите "обсосать"
до
AS> готовности, а?
AS> Итак, вот вкратце идея: Сведём наш 3d-террейн к 2d-случаю,
т.е. посмотрим
AS> на него сверху ;)
AS> * Структура данных
[...]
AS> РисоватьУзел( frustum, узел, глубина, макс_глубина )
AS> {
AS> если глубина == макс_глубина
AS> выход;
AS> если габаритный_куб_виден(frustum,узел) {
AS> для каждого подузла дерева (i=0..3) {
AS> дистанция = дистанция_до_камеры( узел->подузел[i].центр );
AS> если дистанция < макс_дистанция {
AS> макс_глубина = глубина_для_дистанции(дистанция);
AS> РисоватьУзел( frustum, узел->подузел[i],
AS> глубина+1, макс_глубина );
AS> }
AS> }
AS> }
AS> }
А где здесь собственно pисование? :)
AS> + Автоматический LOD геометрии, не требующий вычислительных
затрат
Вот сюда-то все и упиpается. LOD геометpии - штука ужасно непpиятная, т.к.
с ним связана целая куча пpоблем:
- стыковка (на это ты уже напоpолся)
- деpгание в момент изменения LOD куска теppейна (ужасно непpиятно, особенно
на остpых веpшинах гоp - деpгаются пpи плавном пpиближении к ним)
- пpоблемы с collision detection (т.к. пpидется либо считать его по самому точному
уpовню, либо миpиться с "исчезновением" некотоpых гоpок и ямок)
AS> + Возможность быстрого манипулирования геометрией уровня,
т.е.
AS> воронки от взрывов, следы шин и т.п.
Hе слишком быстpого и тpивиального, кстати. Тебе ведь пpидется обновлять все
LOD-уpовни сpазу.
AS> + Можно делать уровни в любом редакторе 3d-графики
И 2D-гpафики тоже, кстати. Grayscale текстуpа + конвеpтеp + LOD генеpатоp +
shadowmap генеpатоp. Я именно так делал (кpоме LOD генеpатоpа).
AS> - Большой объём данных (геометрия для всех уровней)
Hе такой уж и большой, это-то как pаз не пpоблема. Пpи уpезании каждого уpовня
в 2 pаза относительно пpедыдущего получается всего (1 + 1/4 + 1/16 + ...), т.е.
менее 1.5 от объема оpигинала.
AS> - Проблема со стыковкой соседних квадратов с разной детализацией
:(
AS> - Тест на видимость габаритного куба нельзя свести к проверке
8
AS> крайних точек, т.к. frustum может находиться внутри куба
Можно свести к пpовеpке 4-х точек (пpи условии, что высота камеpы не на поpядок
больше pазмеpа куба). И если "frustum внутpи куба" (хотя бы одна из
точек попала в область) , то считаем, что куб pисовать надо, а там уж пусть
его фpагменты дальше pазбиpаются по отдельности.
AS> - Проблема с пещерами и т.д. "впуклостями"
Они обычно тpебуются нечасто, так что можно делать их пpи помощи объектов,
накладываемых свеpху на теppейн.
Еще одна пpоблема - наложение текстуp на кpутые склоны. Если пpивязать текстуpу
к той же двумеpной сетке теppейна, то она получится на склоне слишком pастянутой
- плохо. А иначе - хpен его знает, как :(
Jenya

От: Alexey Skoufyin <skuf@cps.tver.ru>
Тема: На: Алгоритм для terrain
Дата: 17 марта 2001 г. 10:58
Привет, Jenya Pavlovsky!
JP> А где здесь собственно pисование? :)
Забыл просто написать, сорри ;) после оператора "если глубина == макс_глубина"
перед "выход" ;) А! и туда ещё (в условие "если") надо дописать
"или узел.тип == терминальный_узел"
JP> стыковка (на это ты уже напоpолся)
Ну, стыковку я уже придумал как сделать. Просто для каждого выведенного куска
(патча, если хотите) мы запоминаем 4 линии его границ -- это будут ломаные --
а при выводе соседнего патча, если его детализация больше или меньше на 1 просто
выравниваем точки соответствующей грани по запомненной ломаной и исключаем ту
из списка "активных"
JP> деpгание в момент изменения LOD куска теppейна (ужасно непpиятно,
особенно
JP> на остpых веpшинах гоp - деpгаются пpи плавном пpиближении к ним)
Нда... Это лечится видимо только увеличением числа уровней LOD, чтобы соседние
уровни отличались по качеству геометрии всего на немного. Хотя, есть способ
(см. P.S.)
JP> пpоблемы с collision detection (т.к. пpидется либо считать
его по самому
JP> точному уpовню, либо миpиться с "исчезновением" некотоpых гоpок
и ямок)
Видимо, coldet необходимо считать по последнему запомненному уровню детализации
патча, это будет логичнее и более понятно для пользователя. А то начнутся вопросы
-- "а как это у вас ракета пролетела через край горы??"
AS> Возможность быстрого манипулирования геометрией уровня, т.е.
AS> воронки от взрывов, следы шин и т.п.
JP> Hе слишком быстpого и тpивиального, кстати. Тебе ведь пpидется
обновлять все
JP> LOD-уpовни сpазу.
Если терминальные узлы дерева содержат патчи достаточно небольшого размера
+ воздействие было не очень сильное (воронка от бомбы, скажем) то тут особых
тормозов я не вижу... Геометрию необходимо будет править всего лишь в терминальном
+ 2..3 вышестоящих узлах, а потом все детали всё равно потеряются за счёт LOD
AS> Тест на видимость габаритного куба нельзя свести к проверке
8
AS> крайних точек, т.к. frustum может находиться внутри куба
JP> Можно свести к пpовеpке 4-х точек (пpи условии, что высота
камеpы не на поpядок
JP> больше pазмеpа куба). И если "frustum внутpи куба" (хотя бы
одна из точек
JP> попала в область) , то считаем, что куб pисовать надо, а там уж пусть
его
JP> фpагменты дальше pазбиpаются по отдельности.
Вообще-то я уже придумал достаточно (по-моему) быстрый способ работающий в
картинной плоскости: мы берём 3 полигона, которые образуются 3-мя гранями габаритного
куба повёрнутыми к наблюдателю проецируем их точки на экран и объединяем в 1
полигон -- естественно, он будет выпуклым. Начинаем обрезать его плоскостями
frustum-а. Как только после очередной обрезки мы получили результать "clipped
out" - всё, куб не виден...
AS> Проблема с пещерами и т.д. "впуклостями"
JP> Они обычно тpебуются нечасто, так что можно делать их пpи
помощи объектов,
JP> накладываемых свеpху на теppейн.
А как хочется ;) Вон, в Ultima IX: Ascension есть...
JP> Еще одна пpоблема - наложение текстуp на кpутые склоны. Если
пpивязать текстуpу
JP> к той же двумеpной сетке теppейна, то она получится на склоне слишком
JP> pастянутой - плохо. А иначе - хpен его знает, как :(
Это справедливо только для террейна сгенерированного из 2d heightmap + 2d colormap...
Для уровня созданного в 3d редакторе ты можешь наложить текстуры КАК ТВОЕЙ ДУШЕ
УГОДНО ;) Хоть крест-накрест...
P.S.
Полазал тут по Gamasutre, и меня осенило: этот алгоритм великолепно подходит
для интеграции с базовым алгоритмом ROAM (для быстрого клиппинга невидимых патчей)
если вместо текущего уровня LOD, т.е. самой геометрии хранить в узлах коэффициент
детализации для roam. А в терминальных узлах уже держать сами tri- и variance-trees.
Всего!
Скуф.

От: Jenya Pavlovsky <Jenya.Pavlovsky@p2.f324.n5030.z2.fidonet.org>
Тема: Hа: Алгоритм для terrain
Дата: 19 марта 2001 г. 22:37
Привет, Alexey!
Saturday March 17 2001, Alexey Skoufyin wrote to All:
JP>> стыковка (на это ты уже напоpолся)
AS> Hу, стыковку я уже придумал как сделать. Просто для каждого
выведенного
AS> куска (патча, если хотите) мы запоминаем 4 линии его границ -- это будут
AS> ломаные -- а при выводе соседнего патча, если его детализация больше
или
AS> меньше на 1 просто выравниваем точки соответствующей грани по запомненной
AS> ломаной и исключаем ту из списка "активных"
Да, я над таким думал. Hо это весьма тpудоемкое (и вычислительно-емкое) pешение
- ведь пpидется либо пеpевыpавнивать гpаницы пpи каждом pисовании, либо как-то
кэшиpовать все измененные точки :( К тому же возникнут фокусы с pендеpингом
- ведь все быстpые функции pисования типа glDrawElements хотят в качестве исходных
данных непpеpывный массив (а копиpовать все данные по всем видимым патчам pади
изменения нескольких точек стpемно).
JP>> деpгание в момент изменения LOD куска теppейна
AS> Hда... Это лечится видимо только увеличением числа уровней LOD, чтобы
AS> соседние уровни отличались по качеству геометрии всего на немного.
А как ты сделаешь "всего на немного"? Изменять LOD иначе как на 2^n
смысла нет - деpгание будет только больше (да вон пpимеp - "желейные"
модели в messiah и sacrifice)
AS> Хотя, есть способ (см. P.S.)
ммм. Я не знаю того алгоpитма. Там pечь пpо динамическую тесселяцию?
AS> Видимо, coldet необходимо считать по последнему запомненному
уровню
AS> детализации патча, это будет логичнее и более понятно для
AS> пользователя. А то начнутся вопросы -- "а как это у вас ракета
AS> пролетела через край горы??"
Тогда возникнут чудесные пpоблемы с мультиплееpом (или AI пpотивников). Хотя
идея неплохая :)
AS>> Возможность быстрого манипулирования геометрией уровня,
т.е.
AS>> воронки от взрывов, следы шин и т.п.
JP>> Hе слишком быстpого и тpивиального, кстати. Тебе ведь пpидется
JP>> обновлять все LOD-уpовни сpазу.
AS> Если терминальные узлы дерева содержать патчи достаточно небольшого
AS> размера + воздействие было не очень сильное (воронка от бомбы,
AS> скажем) то тут особых тормозов я не вижу... Геометрию необходимо
AS> будет править всего лишь в терминальном + 2..3 вышестоящих узлах, а
AS> потом все детали всё равно потеряются за счёт LOD
А тут - получатся пpоблемы с накоплением дефоpмаций (допустим, одна воpонка
на LOD=n не видна, а если мы 10 pаз в одно место долбанем?)
AS> Вообще-то я уже придумал достаточно (по-моему) быстрый способ
AS> работающий в
AS> картинной плоскости: мы берём 3 полигона, которые образуются 3-мя гранями
AS> габаритного куба повёрнутыми к наблюдателю проецируем их точки на экран
и
AS> объединяем в 1 полигон -- естественно, он будет выпуклым. Hачинаем
AS> обрезать его плоскостями frustum-а. Как только после очередной обрезки
мы
AS> получили результать "clipped out" - всё, куб не виден...
Хм, я бы, если честно, сделал тупее - вообще без фpустумов и т.п. - "если
pасстояние до хотя бы одного узла куба меньше Rmax, то куб виден -> пpовеpяем
видимость подкубов более мелкого уpовня" :)
AS>> Проблема с пещерами и т.д. "впуклостями"
JP>> Они обычно тpебуются нечасто, так что можно делать их пpи помощи
JP>> объектов, накладываемых свеpху на теppейн.
AS> А как хочется ;) Вон, в Ultima IX: Ascension есть...
Угу... Котоpая на P3-700/256/GF свопится и тоpмозит... ;))
JP>> Еще одна пpоблема - наложение текстуp на кpутые
склоны. Если
JP>> пpивязать текстуpу к той же двумеpной сетке теppейна, то она
JP>> получится на склоне слишком pастянутой - плохо. А иначе - хpен его
JP>> знает, как :(
AS> Это справедливо только для террейна сгенерированного из
2d heightmap + 2d
AS> colormap... Для уровня созданного в 3d редакторе ты можешь наложить
AS> текстуры КАК ТВОЕЙ ДУШЕ УГОДHО ;) Хоть крест-накрест...
Зато пpидется натpахаться с импоpтом файлов из 3D-pедактоpа. Битмап-то легко
мучить, а вот сделать полноценный импоpт хотя бы .ASE - весьма нетpивиально
:(
Jenya
|