Russian Qt Forum

Qt => OpenGL => Тема начата: Igors от Ноябрь 12, 2016, 14:29



Название: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 12, 2016, 14:29
Добрый день

Подобную тему уже создавал, но, как говорится, "быльем поросло". А сейчас пришлось обновляться до новой версии одной из базовых либ - и опять пришлось многое переделывать.

- Есть исходные данные стандартные для OpenGL - вертексы и индексы, число индексов на фейс (могут быть не только тр-ки) + всякая опциональная всячина (нормали, цвет, UV(s)  др). Хранятся они в массивах/векторах, в общем у любого пользующего OpenGL практически то же самое. Данные могут шариться разными объектами, напр N объектов может использовать одни и те же вертексы и индексы, или только индексы. Ну и конечно все данные буферированы с использованием QOpenGLBuffer чтобы не гонять их CPU -> GPU при каждом рисовании

- И вот возникает "еще одно" представление/рисование объекта. Юзер включает бубочки и ожидает увидеть объект с высокой деталировкой (вместо исходного с низкой). Что однако, не означает "объект изменился" - нет, он хочет только "видеть", а все действия по-прежнему выполнять с исходным простым объектом, (а не таскать тонны геометрии)

Ясно что решение одно - хранить высоко деталированный объект на карте, но как это сделать ?

Спасибо


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 12, 2016, 15:35
В функциях gl имеется параметр stride. Может его задействование как-то поможет...


Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Ноябрь 12, 2016, 21:16
А разве при изменении "простого" объекта не должен будет меняться "сложный"?
Скорее всего, тут "хранить" надо некую изменяемую модель данных. А оба OpenGl-представления генерировать при изменении модели.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 13, 2016, 09:58
В функциях gl имеется параметр stride. Может его задействование как-то поможет...
stride - это просто размер единицы вертексных данных, который может быть больше требуемого. Напр если данные хранятся как QVector<QVector4D> то указав stride = 16 мы можем их скормить напрямую, без перевода в QVector<QVector3D>

А разве при изменении "простого" объекта не должен будет меняться "сложный"?
Конечно должен. Изменения бывают 2 типов

а) изменилась вся модель полностью
б) изменились вертексы и нормали к ним (только значения, их число осталось тем же)

Скорее всего, тут "хранить" надо некую изменяемую модель данных. А оба OpenGl-представления генерировать при изменении модели.
Так вот и хотелось бы поговорить/конкретизировать как генерировать разные OpenGL-представления.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 13, 2016, 10:53
Как должна быть создана картинка в стартовом посте

- есть исходный объект (слева). Он копируется 3 раза. Пока все 4 объекта используют одни и же QOpenGLBuffer для вертексов, индексов, нормалей и.т.д. Все шарится

- Теперь юзер открывает UI каждой из копий и назначает уровни деталировки 1, 2, 3 соответственно. Должны быть созданы/посчитаны новые данные, которые записываются в буфера. Теперь у каждого объекта все буфера свои. Ничего не шарится

Возиожно (хотя и не показано на картинке) юзер скопирует любой из объектов - копия шарит оригинал. Если же  копия или оригинал изменены (напр как-то деформированы), то 2 буфера (вертексы и нормали) у каждого свои, остальные буфера по-прежнему шарятся
 


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 14, 2016, 11:03
Я смотрю описание glVertexAttribPointer
Цитировать
stride
Specifies the byte offset between consecutive generic vertex attributes. If stride is 0, the generic vertex attributes are understood to be tightly packed in the array. The initial value is 0.
Вроде как им можно сказать конвейеру брать каждую вторую/третью вершину.


Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Ноябрь 14, 2016, 11:50
Так вот и хотелось бы поговорить/конкретизировать как генерировать разные OpenGL-представления.

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


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 14, 2016, 14:05
Вроде как им можно сказать конвейеру брать каждую вторую/третью вершину.
Можно и так, но обычно используется для согласования размеров единицы исходных/передаваемых данных. К этой теме отношения не имеет

Щас подумал - видно, я ранее чушь сказал... Хранить-модифицировать надо высокополигональную модель.
Потому что из хай-поли лоу-поли сделать можно, а наоборот - это неоднозначно.
Самокритика - это прекрасно, но в данном случае все наоборот: исходные данные LOW-resolution меш, часто его называют "cage". Заметим что частенько результат заметно лучше если исходные полигоны 4-угольники (а не тр-ки).   


Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Ноябрь 14, 2016, 14:58
Я имел в виду, что поскольку 1-й объект (исходный) - это некий треугольник в 3д, то как мы можем быть уверенными, что в хай-поли оно превращается в 4-й (сильно сглажены углы), а не должно оставаться таки треугольным?
Судя по картинкам - это не просто хай-поли, а объект плюс деформация.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 14, 2016, 15:32
Судя по картинкам - это не просто хай-поли, а объект плюс деформация.
Да, точнее "не просто триангуляция". Это называется SDS (surface sub-division) и известно уже не один десяток лет. Вертекс заменяется взвешенной суммой соседей. Если не вдаваться в тонкости с sharpness - можно самому написать за день. Конечно это не "magic", хороший рез-т получается для достаточно грамотных, аккуратных исходных cage(s).

Однако и это не имеет отношения к теме  :) 


Название: Re: Использование QOpenGLBuffer
Отправлено: ssoft от Ноябрь 15, 2016, 10:55
Разную детализацию на основе одних и тех же вертексных данных можно получить путем формирования различных массивов индексов. Например, загрубив полигональный объект путем пропуска вершин по какому-либо критерию, можно выполнить тесселяцию столько раз, сколько нужно. Тогда для одного набора вершин получим несколько наборов индексов для разной степени детальности.

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


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 15, 2016, 11:06
Насчёт упрощения представления, кстати, есть несколько способов: feature lines, bounding box, node cloud, element centroid


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 15, 2016, 13:55
...загрубив полигональный объект путем пропуска вершин по какому-либо критерию,
И что это будет за объект с выкинутыми вертексами?  ???

Для облегчения перетаскивания объектов несколько раз видел прием, когда все примитивы в момент перемещения отображаются в режиме отображения точек. Получается просто, легко и может даже эффектно.
Тут у меня очередные "чудеса на виражах" :) Если рисовать точки и wireframe с помощью gl(Multi)DrawElements (установив glPolygonMode) то скорость почти в 3 раза НИЖЕ чем для "залитых" полигонов даже с текстурами и материалом! Если же соскочить на glDrawArrays для точек, то конечно шустрее, но я попадаю в др беду - не работает Cull(Back)Fаce

Ну и вообще-то вопрос был как хранить разные представления


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 15, 2016, 14:08
Ладно, попробуем набросать структуры данных, может обсуждение станет более конкретным.
Вот "принципиальная схема"

Код
C++ (Qt)
typedef QVector<QVector3D> TVerArray;  
typedef QVector<int> TIndArray;
 
struct CRenderObject {
...
// данные рисования, могут шариться любым кол-вом CRenderObject
QSharedPointer<CGeometry> m_geometry;
 
// уровень деталировки, 0 = исходная геометрия
int m_detailLevel;      
 
// Если к объекту применены нелинейные преобразования
// то его "личные" вертексы и нормали должны использоваться
// (вместо тех же данных в m_geomretry)
TVerArray m_vertex, m_normal;
};
 
// класс данных рисования
struct CGeometry {
...
 TVerArray m_vertex, m_normal, m_color, m_uv0, m_uv1;
 TIndArray m_indices, m_ver_per_face;
};
Вот собсно и все - теперь надо куда-то поселить буфера (QOpenGLBuffer). Вот как и куда?


Название: Re: Использование QOpenGLBuffer
Отправлено: ssoft от Ноябрь 15, 2016, 14:46
(http://www.prog.org.ru/index.php?action=dlattach;topic=30760.0;attach=8705;image)

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

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

Или можно хранить копии одного объекта с разной детализацией? Тогда достаточно в момент рендеринга выбирать нужный вид.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 15, 2016, 15:43
Или можно хранить копии одного объекта с разной детализацией? Тогда достаточно в момент рендеринга выбирать нужный вид.
Почти так - только хранить "деталированные копии" как QOpenGLBuffer(s). Каждый уровень детализации увеличивает объем геометрии в 4 раза. Вот и надо сделать чтобы это было не накладно, пусть с этим видео разбирается


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 17, 2016, 11:09
Ну вот опять недоумение/молчание  :) Возможно товарищей смутило мое самопальное "сохранить модель на видео". Но это и сейчас делает всякий/каждый. Псевдокод
Код
C++ (Qt)
struct CObjectGL {
TVerArray  m_vertex, m_normal .... // контейнеры вертексов, нормалей еще др данных рисования
TIndArray m_index;     // контейнер индексов
 
QOpenGLBuffer m_buf_vertex, m_buf_normal ... // буфера вертексов и др
QOpenGLBuffer m_buf_index;  // буфер индексов
};
Пример заполнения одного буфера, остальные аналогично
Код
C++ (Qt)
m_buf_vertex.bind();
m_buf_vertex.allocate(&m_vertex[0], m_vertex.size() * sizeof(QVertor3D));
m_buf_vertex.release();
Все, данные полетели на видео, остается подать их рисованию. Если нет никаких др целей то можно даже избавиться от исходных данных, напр m_vertex.cl;ear() - рисовать все равно будет.

Теперь надо сделать то же самое но с учетом всяких shared как рассказано выше


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 17, 2016, 13:32
Не совсем понятно. Вы хотите на карте хранить все степени детализации? Тогда это делается использованием разных буферов. Самое простое сделать массив буферов.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 17, 2016, 15:30
Не совсем понятно. Вы хотите на карте хранить все степени детализации?
Нет, хранить только текущую. Но с учетом того что буфера могут шариться любым кол-вом объектов


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 18, 2016, 10:21
Имхо, объект должен предоставлять данные по используемым общим вершинам (индексы), которые будут зависить от степени детализации. При смене детализации объекта должна производиться реаллокация буфера.

В моём проекте объект обладает рисовальщиком, который отвечает за реаллокацию при изменении объекта и за отрисовку объекта.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 18, 2016, 12:01
Имхо, объект должен предоставлять данные по используемым общим вершинам (индексы), которые будут зависить от степени детализации. При смене детализации объекта должна производиться реаллокация буфера.
Есть 2 объекта шарящие буфер(а). Теперь деталировка первого объекта изменилась, второго нет. Ваши действия?


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 18, 2016, 12:17
При изменении деталировки первого объекта необходимо реаллоцировать его буфер индексами вершин, которые описывают модель более подробно.
Перерисовать объекты.

Или у вас нет возможности использовать вершины самой подробной модели для вывода грубой?


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 18, 2016, 13:15
При изменении деталировки первого объекта необходимо реаллоцировать его буфер индексами вершин, которые описывают модель более подробно.
Перерисовать объекты.

Или у вас нет возможности использовать вершины самой подробной модели для вывода грубой?
Откуда Вы вообще это взяли ??? Буфер(а) объектов с разной деталировкой не имеют между собой ничего общего. Конечно при выполнении детализации должны быть созданы все новые буфера (не только для индексов). Вопрос где и как их хранить учитывая возможный share.


Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Ноябрь 18, 2016, 13:22
Есть 2 объекта шарящие буфер(а). Теперь деталировка первого объекта изменилась, второго нет. Ваши действия?

Клонировать данные отрисовки. Ну или шейдерами, гада)


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 18, 2016, 13:28
Клонировать данные отрисовки.
Can you please be more specific?
Ну или шейдерами, гада)
Почему "или"? Только 1 буфер подается в glDrawElements, все остальные - в пресловутые шейдеры


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 18, 2016, 13:54
Почему "или"? Только 1 буфер подается в glDrawElements, все остальные - в пресловутые шейдеры
Что-то тут не то, кажется, написано.

Цитировать
Конечно при выполнении детализации должны быть созданы все новые буфера
Я представляю, что у вас есть облака точек для каждой детализации. Каждый ваш объект имеет набор индексов точек для формирования полигонов в той или иной детализации. Предполагаю, что у вас слабодетализированное облако пересекается высокодетализированным не полностью. Предполагаю, что большая часть точек из слабодетализированного облака совпадает с некоторыми точками из высокодеталлизированного. Предлагаю слить все облака в единый общий буфер при этом ликвидируя дубликаты точек. Полигоны объекта переиндексировать относительно общего облака. Таким образом, используя 1 вершинный буфер можно будет выводить объекты с разной степенью детализации.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 18, 2016, 16:14
Я представляю, что у вас есть облака точек для каждой детализации. Каждый ваш объект имеет набор индексов точек для формирования полигонов в той или иной детализации. Предполагаю, что у вас слабодетализированное облако пересекается высокодетализированным не полностью. Предполагаю, что большая часть точек из слабодетализированного облака совпадает с некоторыми точками из высокодеталлизированного.
Не совпадает, см картинку стартового поста  

Предлагаю слить все облака в единый общий буфер при этом ликвидируя дубликаты точек. Полигоны объекта переиндексировать относительно общего облака. Таким образом, используя 1 вершинный буфер можно будет выводить объекты с разной степенью детализации.
Утопия. Я спрашиваю о гораздо более простой/прозаической вещи. Попробуем идти по пути наименьшего сопротивления
Код
C++ (Qt)
struct CRenderObject {
...
QSharedPointer<QOpenGLBuffer> m_buf_ver;
..
};
Изначально уровень деталировки ноль. Хорошо, заполнили буфер из исходных данных. Копируемые объекты шарят, пока все норм. И вот юзверь поставил деталировку напр 2. Должны ли мы выполнить деталировку (дорогая операция) и перезаписать буфер? Ведь возможно кто-то из шарящих уже это сделал, и нужный буфер уже имеется


Название: Re: Использование QOpenGLBuffer
Отправлено: __Heaven__ от Ноябрь 18, 2016, 16:34
Рядом с буфером храните переменную, характеризующую его детализацию и не надо будет аллоцировать буфер лишний раз.

Я понял, что отмеченные во вложении вершины совпадают.
Можно ещё хранить буферы для используемых детализаций...


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 18, 2016, 17:11
Рядом с буфером храните переменную, характеризующую его детализацию и не надо будет аллоцировать буфер лишний раз.
Тот же код
Код
C++ (Qt)
struct CRenderObject {
...
QSharedPointer<QOpenGLBuffer> m_buf_ver;
..
};
Деталировка ноль, 2 CRenderObject шарят один буфер. Теперь первому поставили деталировку 1. Ладно, посчитал, сделал m_buf_ver.reset(new QOpenGLBuffer(..)), залил в буфер новые данные. Теперь для второго деталировка тоже 1. Что дает Ваша переменная? Ну был 0 стал 1, это можно узнать и без доп переменной, а толку? Деталировка 1 уже создана для др объекта, ее надо шарить, а не создавать еще раз 


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 22, 2016, 16:30
Мда, шо-то вообще мЭртво  :) Ну наверное это слишком просто и неинтересно. Ладно, чуть усложним

Ну во-первых это я так смело сказал "уровень деталировки", в реальности там с десяток опций, не только уровень. Во-вторых, как уже заметили, при изменении базового объекта деталированный(е) тоже должен меняться. Пересчитывать каждый раз с нуля весьма накладно. Возможность ускориться есть - нужно завести так называемую stencilTable и при повторных расчетах юзать ее вместо полного, заметно шустрее. Ну конечно таблица (и кое-какие др данные - тоже понадобятся) валидна пока не изменились опции деталировки и топология.

Проблема та же что и с буферами - куда эту таблицу приткнуть (где хранить) с учетом разнообразных возможных sharing?


Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Ноябрь 23, 2016, 02:41
А тут надо определиться, чье время дороже, программиста или юзера.
Имею в виду, есть ли смысл тратиться на мегаоптимизации, чтобы у юзера все летало...
Или же сделать обычным пересчетом данных каждый раз, заставив юзера несколько ожидать.
Обычно истина где то посередине...


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 23, 2016, 10:49
А тут надо определиться, чье время дороже, программиста или юзера.
Имею в виду, есть ли смысл тратиться на мегаоптимизации, чтобы у юзера все летало...
Или же сделать обычным пересчетом данных каждый раз, заставив юзера несколько ожидать.
Обычно истина где то посередине...
Во как,  "мегаоптимизации" :)  Вообще задача довольно стандартная, во всяком случае я столкнулся с подобным далеко не первый раз.

Всем известно как работает стандартная шара. Копии указывают на один объект пока не случился detach(). Вумные указатели - то же самое, произошел reset() - все, у этого клиента уже новые данные, остальные клиенты как себе хотят. На этом стандартные приемы заканчиваются.

Но по жизни нередко возникает ситуация когда велика вероятность что новые данные уже существуют, зачем же их еще раз создавать раcходуя ресурсы? Естественно выглядит хранить мапу(ы) созданных вариантов и шарить значения этой мапы. Понятно что надо иметь ключ, напр если detach произошел для массива/имеджа - ничего не выйдет, захлебнемся с ключом. Однако, если имеем ограниченный набор опций (в исходной постановке вообще лишь 1 int - "уровень деталировки"), то вот он и ключ.

Правда как организовать такую мапу - не так уж очевидно, об этом я и хотел поговорить. Увы, по каким-то необъяснимым причинам, чего-то типа "мап/хеш" не прозвучало вообще :'(  Возможно сбила с толку предметная часть, мол, нужно знать многочисленные подробности OpenGL, вот тогда.. Это совсем не так, да и глянуть тот же QOpenGLBuffer в букваре - дело нескольких минут. Опять заметим как мало помогает усердное изучение std/boost/паттернов и всякой всячины - вот банальная, заурядная задачка и... нiчого нема  :'(


Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Ноябрь 23, 2016, 11:29
А тут получается палка с двумя концами.
Хотим экономить ресурсы? Никаких шар, формируем только нужные данные в нужное время и фигачим на карту.
Хотим экономить время? Тогда создаем всевозможные кэши, половина из которых будет тупо ждать своего часа.
Конечно, тут можно гибрид сделать.
Но неясно, что в итоге хочется получить - "быстро" или "дешево".
Я бы не мучился и создавал бы нужные данные "on the fly". Конечно, если позволяет память, их можно положить в уголок, посчитав какое-нибудь crс. То есть достаточно будет единого глобального кэша, чтоб хранить посчитанные фрагменты. Но тогда придется гемороиться с инвалидацией, "устареванием" и прочими присущими вещами...


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 23, 2016, 12:09
А тут получается палка с двумя концами.
Хотим экономить ресурсы? Никаких шар, формируем только нужные данные в нужное время и фигачим на карту.
Хотим экономить время? Тогда создаем всевозможные кэши, половина из которых будет тупо ждать своего часа.
Конечно, тут можно гибрид сделать.
Но неясно, что в итоге хочется получить - "быстро" или "дешево".
Типичный ответ "на основании (богатого) опыта" :) Да, так часто бывает, но это не тот случай. О кеше здесь речь не идет, никакой альтернативы быстро/дешево нет, грамотно шарить данные и быстрее и дешевле.

Пример 1: юзверь назначил объекту деталировку 1 и затем скопировал его N раз. Простое использование Qt имплисит шары или вумных указателей - и мы имеем одну копию данных. Это совершенно нормально и ожидаемо

Пример 2: юзверь сначала скопировал объект N раз, а уж потом назначил объекту деталировку 1 для всех копий. Очевидно рез-т должен быть тот же что и в примере 1, но почему-то мы создаем N копий данных. Глупо, не так ли? Почему это мы не можем "зашарить взад"?



Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Ноябрь 24, 2016, 10:20
Как минимум надо делать различие между копией и инстанциями.
А если юзер склонировал объект а в б и потом поменял детальность б, должен ли измениться а?
А если а был отредактирован на уровне модели? Должны обновиться копии?
Если а убить, что с клонами будет?


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 25, 2016, 17:32
Но по жизни нередко возникает ситуация когда велика вероятность что новые данные уже существуют, зачем же их еще раз создавать раcходуя ресурсы? Естественно выглядит хранить мапу(ы) созданных вариантов и шарить значения этой мапы. Понятно что надо иметь ключ, напр если detach произошел для массива/имеджа - ничего не выйдет, захлебнемся с ключом. Однако, если имеем ограниченный набор опций (в исходной постановке вообще лишь 1 int - "уровень деталировки"), то вот он и ключ.

Правда как организовать такую мапу - не так уж очевидно, об этом я и хотел поговорить. Увы, по каким-то необъяснимым причинам, чего-то типа "мап/хеш" не прозвучало вообще :'(  Возможно сбила с толку предметная часть, мол, нужно знать многочисленные подробности OpenGL, вот тогда.. Это совсем не так, да и глянуть тот же QOpenGLBuffer в букваре - дело нескольких минут. Опять заметим как мало помогает усердное изучение std/boost/паттернов и всякой всячины - вот банальная, заурядная задачка и... нiчого нема  :'(

Оффтоп
Тему не читал, но мы использовали QPixmapCache в делегате таблицы с ключом от QStyleOption + data ячейки. Давало бешеное ускорение отрисовки.
Оффтоп


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 25, 2016, 17:36
Пример 2: юзверь сначала скопировал объект N раз, а уж потом назначил объекту деталировку 1 для всех копий. Очевидно рез-т должен быть тот же что и в примере 1, но почему-то мы создаем N копий данных. Глупо, не так ли? Почему это мы не можем "зашарить взад"?



QExplicitlySharedDataPointer?:)
Типа - какие-то операции над объектом детачат, а какие-то - нет. Но тут придется попрощаться с многопоточностью.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 26, 2016, 13:57
Как минимум надо делать различие между копией и инстанциями.
Для юзера существует одно понятие "скопировать", копия имеет те же права что и оригинал
А если юзер склонировал объект а в б и потом поменял детальность б, должен ли измениться а?
Нет, с какой стати?
Если а убить, что с клонами будет?
Ничего, живут себе как жили
А если а был отредактирован на уровне модели? Должны обновиться копии?
Тоже нет, с точки зрения юзера все независимо, что там шарится или нет под капотом его не волнует. Однако вполне реально что он нахрюкает сотню-другую копий, и тогда производительность будет сильно зависеть от числа используемых буферов.

Ладно, допустим пока не знаем что делать, не пойти ли "от использования"? Есть объект (CRenderObject), может он уникален, а может одна копия из многих - хз. Но по-любому мы его собрались рисовать, а значит надо получить буфер(а). Как бум это делать? Предлагаю такой набросок
Код
C++ (Qt)
typedef QSharedPoiinter<QOpenGLBuffer> TBufVBO;
 
struct CVerArray : public QVector<QVector3D> {
CBufControl  m_control;
};
 
// код рисования
void CRenderObject::Draw( ... )
{
// получаем буфер позиций вертексов
 CVerArray * ver = GetVerArray();
 TBufVBO vbo = ver->m_control.GetBuf(this);
 
// отдаем его шейдеру
 vbo->bind();
 glVertAttribPointer(...);
 vbo->release();
 
// то же с др буферами  
 ...
}
 
Ну то есть всю нагрузку переложили на (пока мифический) CBufControl::GetBuf, вот пусть он разбирается, если надо запускает расчет деталировки и в конце-концов вернет нам нужный буфер. Да, так надо делать по каждому буферу - это вызывает сомнения.

Критикуем/предлагаем активнее, товарищи  :) 


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 26, 2016, 19:06

Ладно, допустим пока не знаем что делать, не пойти ли "от использования"? Есть объект (CRenderObject), может он уникален, а может одна копия из многих - хз. Но по-любому мы его собрались рисовать, а значит надо получить буфер(а). Как бум это делать? Предлагаю такой набросок

Мне не нравится, что объект, который суть копируемые данные, умеет себя рисовать.


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 27, 2016, 02:14
В общем, прокурил всю тему

Верно я понимаю, что нужно что-то типа:
Код:
class CRenderObject 
{
    int level; // уровень деталировки
    std::shared_pointer<QOpenGLBuffer> buffer; // текущий буффер
    std::shared_pointer<InnerData> data;
};

struct InnerData
{
    std::vector<QVector3D> indexes; // сырые данные, не шарю в ОГЛ
    std::map<int /*level*/, std::weak_pointer<QOpenGLBuffer> buffers; // либо тут тоже shared_pointer
};

То есть имеем что CRenderObject держит какой-то один буффер, который быстро рисует. При изменении уровня детализации лезет в общий для всех *таких* (построенных на одних данных) объектов, лезем в общую дату и достаем новый буфер из кэща (если его там нет, создаем). Дальше вариант - если 2 копии имели деталировку 2, а потом обе стали не 2 (например, 1 и 3 или 1 и 1), то можно дропать буффер для 2 (тогда в InnerData надо что-то типа weak_ptr), либо не дропать (тогда нужен shared).
У этого решения есть минус, что он не учитывает, что 2 *таких* объекта могут быть созданы отдельно:
Код:
std::string s1("hello");
std::string s2("hello"); // данные не шарятся так как нет возможности узнать что они одинаковые
Тогда нужно еще общее хранилище InnerData которое будет доставать даты по сложному ключу от массивов. Но это плохое решение и надеюсь, это не ваш случай. Иначе надо подумать ещё.
Также я не очень понял про различные буфера. CRenderObject должен хранить несколько буферов? Детализация для буферов одинаковая при этом? Или разные CRenderObject имеют разное "предназначение" (вообще в огл не шарю, ничего кроме массива вершин придумать не могу) и строятся на разных данных?

Upd: ну это решение плохое тем, что не держит многопоточность, но думаю она и не нужна. Иначе придется накрутить детачей где-то.
Upd2: Если API объекта позволяет менять сырые данные то вот тут придется детачить InnerData и пересоздавать буффер.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 27, 2016, 10:18
Повторим матчасть. Вот общие прынцыпы
- Есть исходные данные стандартные для OpenGL - вертексы и индексы, число индексов на фейс (могут быть не только тр-ки) + всякая опциональная всячина (нормали, цвет, UV(s)  др). Хранятся они в массивах/векторах, в общем у любого пользующего OpenGL практически то же самое. Данные могут шариться разными объектами, напр N объектов может использовать одни и те же вертексы и индексы, или только индексы. Ну и конечно все данные буферированы с использованием QOpenGLBuffer чтобы не гонять их CPU -> GPU при каждом рисовании
Вот структуры данных (пока без буферов, нам и надо их организовать)
Код
C++ (Qt)
typedef QVector<QVector3D> TVerArray;  
typedef QVector<int> TIndArray;
 
struct CRenderObject {
...
// данные рисования, могут шариться любым кол-вом CRenderObject
QSharedPointer<CGeometry> m_geometry;
 
// уровень деталировки, 0 = исходная геометрия
int m_detailLevel;      
 
// Если к объекту применены нелинейные преобразования
// то его "личные" вертексы и нормали должны использоваться
// (вместо тех же данных в m_geomretry)
TVerArray m_vertex, m_normal;
};
 
// класс данных рисования
struct CGeometry {
...
 TVerArray m_vertex, m_normal, m_color, m_uv0, m_uv1;
 TIndArray m_indices, m_ver_per_face;
};
Как уже говорилось, все это везде примерно одинаково. Ну у меня 2 массива UV, у кого-то может быть другое число, или доп массивы напр для бампа - но в любом случае это должны быть "линейные" контейнеры (данные подряд) и число эл-тов в каждом в точности такое как у m_vertex

Без буферов и деталировок структура выше шарится простейшими средствами - скопировали шаред m_geometry, вот и все. Если "объект изменился", тоже все работает, 2 варианта

1) Изменилась "топология" (расклад полигонов). Объект получает новую m_geometry (т.е. все с нуля)
2) Объект может менять свои вертексы (а значит и нормали). Они записываются отдельно от m_geometry (см выше) и на рисовании используются они. Эти 2 контейнера мы не шарим, считаем что слишком сложно отследить что к 2 объектам были применены одни и те же действия (хотя возможно это и не так). Однако остальные данные рисования (индексы, UV и др) по-прежнему берутся из m_geometry.

Буфер (класс QOpenGLBuffer) свой для каждого контейнера данных, по существу просто хендл данных сохраненных на карте. Перед вызовом рисования (glDrawElements) мы должны сообщить OpenGL какие буфера должны быть использованы, подробности API опускаем. Создание буфера: в простейшем случае мы просто копируем данные контейнеров на видео чтобы рисовало шустрее. В случае "деталировок" (SDS по-нашему) мы хотим хранить hi-poly данные на видео и рисовать их. В любом случае один объект (CRenderObject) имеет лишь один набор буферов, по одному на каждый из контейнеров.

Вот об этом идет речь.


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 27, 2016, 18:16
Код:
typedef QVector<QVector3D> TVerArray;  
typedef QVector<int> TIndArray;
 
struct CRenderObject {
...
// данные рисования, могут шариться любым кол-вом CRenderObject
 QSharedPointer<CGeometry> m_geometry;
 
// уровень деталировки, 0 = исходная геометрия
 int m_detailLevel;      
 
// Если к объекту применены нелинейные преобразования
// то его "личные" вертексы и нормали должны использоваться
// (вместо тех же данных в m_geomretry)
 bool m_has_own_data;
 TVerArray m_vertex, m_normal;

  // если m_has_own_data == false то шарим все буффера из мапы m_buffers_cache
  // если m_has_own_data == true то храним два своих уникальных буфера, остальные шарим
  Buffers m_buffers;
};

struct Buffers
{
    QSharedPointer<QOpenGLBuffer> m_vertex_buffer;
    QSharedPointer<QOpenGLBuffer> m_color_buffer;
  // итд
};
 
// класс данных рисования
struct CGeometry {
...
  TVerArray m_vertex, m_normal, m_color, m_uv0, m_uv1;
  TIndArray m_indices, m_ver_per_face;
  QMap<int /*level*/, Buffers> m_buffers_cache;
};

Ну что, не пойдёт?


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 28, 2016, 16:32
Код:
  QMap<int /*level*/, Buffers> m_buffers_cache;
};
Такая мапа выдаст набор буферов по ключу "уровень деталировки" - но ничего более. Напр CRenderObject удаляется. Возможно он был единственным кто пользовал напр уровень 2. Как буфера этого уровня будут убиты?

Также мне кажется что связь между контейнером и его буфером куда более тесная. Хотя бы
Код
C++ (Qt)
m_vertex.clear();
// базовые данные изменились, буфер "no longer valid". Кто его прибьет?
 


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 28, 2016, 16:44
Такая мапа выдаст набор буферов по ключу "уровень деталировки" - но ничего более. Напр CRenderObject удаляется. Возможно он был единственным кто пользовал напр уровень 2. Как буфера этого уровня будут убиты?

Также мне кажется что связь между контейнером и его буфером куда более тесная. Хотя бы
Код
C++ (Qt)
m_vertex.clear();
// базовые данные изменились, буфер "no longer valid". Кто его прибьет?
 

1) ну храните в мапе не структуру а weak_pointer на неё. В основной структуре будет шаред + 2 буфера придётся вынести отдельно.
2) тот же, кто зовёт clear(). Придётся сделать ОБОЖЕ дополнительную ф-ию clearVertexes() в CGeometry, сделать массив приватным, а также добавить свойство "isValid" у буфера (например, в виде std::optional или еще одного указателя, который равен 0 когда буфер "пуст"/"не валиден"). Ну а что вы хотели, если надо синхронно менять 2 переменные, без обертки не обойтись. К сожалению (или к счастью) давно прошли времена, когда можно было сделать обычную структуру и руками лезть в её внутренности.

Можно на самом деле пойти дальше и объединить буфер и вершины в один класс (что вполне логично, раз они парные). И хранить уже указатели на пары буфер+данные.
Задача чисто техническая.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 29, 2016, 06:21
1) ну храните в мапе не структуру а weak_pointer на неё. В основной структуре будет шаред + 2 буфера придётся вынести отдельно.
Тогда что будет на копировании? Сейчас я шарю всю структуру m_geometry (что вполне логично), переходить на "почленное" как-то не резон. И вообще, стоит ли буферами засорять основные структуры (CGeometry и др)? Буфера используются только на рисовании - вот пусть рисование их и достает как шаред пойнтеры.

Да, а куда подевались все эти любители итераторов и.т.п.?  :)



Название: Re: Использование QOpenGLBuffer
Отправлено: Old от Ноябрь 29, 2016, 07:01
Да, а куда подевались все эти любители итераторов и.т.п.?  :)
Берегут свои нервы.


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 29, 2016, 12:59
Тогда что будет на копировании? Сейчас я шарю всю структуру m_geometry (что вполне логично), переходить на "почленное" как-то не резон. И вообще, стоит ли буферами засорять основные структуры (CGeometry и др)? Буфера используются только на рисовании - вот пусть рисование их и достает как шаред пойнтеры.


Всё тоже и будет, в моем примере m_geometry как и буфера копируются целиком чуть ли не дефолтным оператором копирования.
Вся сложность в сеттере уровня детализации и "своих" данных (надо вместо кэша сгенерить свои копии).
Ну не хотите засорять CGeometry, вынесите всё из неё в отдельную структуру и храните больше указателей. Делов-то.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Ноябрь 30, 2016, 15:51
Ну не хотите засорять CGeometry, вынесите всё из неё в отдельную структуру и храните больше указателей. Делов-то.
Если (какая-то) мапа неизбежна то почему бы и не хранить прямо в ней, зачем так щедро заводить новые члены? Вообще кто он такой этот буфер (зависимая козявка) чтобы стоять в одном ряду с капитальными данными? А так членов больше - ясности меньше, напр
Код:
  // если m_has_own_data == false то шарим все буффера из мапы m_buffers_cache
  // если m_has_own_data == true то храним два своих уникальных буфера, остальные шарим
  Buffers m_buffers;
Ну хорошо, допустим m_has_own_data стал false. Ладно, лезу в мапу m_buffers_cache, там weak на Buffers вроде живой. Но можно ли верить его 2 первым буферам, может их и не было? Или наоборот m_has_own_data стал true. Ну просчитал деталировку (про StencilTable пока и не заикаюсь) создал буфера в m_buffers. Так ведь поместить его в общую мапу как weak значение не могу, первые 2 буфера личные.

Думается неверен сам подход, мол, заведем вумный уазатель(и) - и он сам нам все сделает в лучшем виде. Как только расклад чуть сложнее (как в данном случае)  эта вумность быстро заканчивается.


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Ноябрь 30, 2016, 18:17
Причем тут вообще умные указатели?
Цитировать
Но можно ли верить его 2 первым буферам, может их и не было?
Как напишите, так и будет. Хотите отложенную инициализацию - ок, создаем при первом обращении. Не хотим - создает когда записываем исходные данные.

Код:
Так ведь поместить его в общую мапу как weak значение не могу, первые 2 буфера личные.
Можете, я разрешаю. Нет серьезно, просто записываем в мапу а остальные 2 буффера отложенно инициализируем когда понадобятся. Ну только надо класть не наш Buffers, а его копию.

Или у вас при виде умных указателей отключается мозг и вы сразу забываете, что можно делать явный clone разделенного объекта?


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Декабрь 01, 2016, 08:20
Можете, я разрешаю. Нет серьезно, просто записываем в мапу а остальные 2 буффера отложенно инициализируем когда понадобятся. Ну только надо класть не наш Buffers, а его копию.
Не понял. Вы писали
1) ну храните в мапе не структуру а weak_pointer на неё. В основной структуре будет шаред + 2 буфера придётся вынести отдельно.
Приведите структуру данных, трудно что-либо обсуждать имея ввиду это изменение. Вероятно говорим о разных вещах.

Заметим что все уже стало как-то безумно сложно, я плохо представляю что надо делать при том или ином действии юзера - а ведь в задачке ничего сложного нет, буфер - один из самых популярных/ходовых приемов. Что-то не так


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Декабрь 01, 2016, 13:06
Цитировать
Заметим что все уже стало как-то безумно сложно, я плохо представляю что надо делать при том или ином действии юзера - а ведь в задачке ничего сложного нет, буфер - один из самых популярных/ходовых приемов. Что-то не так

Ну просто у вас потихоньку появляются дополнительные требования, которые усложняют (например "не хочу хранить буфер и данные парой").
Кроме того, мы обсуждаем структуру данных, а не реальные классы (пачка классов с хорошими именами всё сиииильно упрощает).

Цитировать
Приведите структуру данных, трудно что-либо обсуждать имея ввиду это изменение. Вероятно говорим о разных вещах.

Код:
typedef QVector<QVector3D> TVerArray;  
typedef QVector<int> TIndArray;

struct Buffers
{
    QSharedPointer<QOpenGLBuffer> m_vertex_buffer;
    QSharedPointer<QOpenGLBuffer> m_color_buffer;
  // итд
};

enum class BufferTypes {
     VertexBuffer = 0x1;
     ColorBuffer = 0x2,
     AllBufers = 0xff
};

struct CRenderObject {
...
// данные рисования, могут шариться любым кол-вом CRenderObject
    QSharedPointer<CGeometry> m_geometry;
 
// уровень деталировки, 0 = исходная геометрия
    int m_detailLevel;       
 
// Если к объекту применены нелинейные преобразования
// то его "личные" вертексы и нормали должны использоваться
// (вместо тех же данных в m_geomretry)
     bool m_has_own_data;
    TVerArray m_vertex, m_normal;

    QSharedPointer<Buffers> m_buffers;

    QSharedPointer<Buffers> buffers() // можно и ссылку
    {
         if (!m_buffers) {
             if (!m_has_own_data) { // вместо булки можно заюзаьт пару Optional<TVerArray> для массивов вершин и нормалей (ходят они парой или нет, яхз)
                  // берем шаредную копию
                  m_buffers = m_geometry->buffers(m_detailLevel, BufferTypes::AllBuffers);
             } else {  // берем шаредную копию и модифицируем её своими буферами
                  // qMakeShared в Qt нет
                  // оптимизация - передаем флаги нужных буферов чтобы не создавать лишнего (можно не передавать, тогда будут лишние операции)
                  auto copy = qMakeShared<Buffers>(*m_geometry->buffers(m_detailLevel, BufferTypes::AllBuffers & BufferTypes::~VertexBuffer));
                  copy->m_vertex_buffer = createVertexBuffer(m_vertex);
                  m_buffers = copy;
             }
         }
         return m_buffers;
    }

    void setVerArray(TVerArray array)
    {
        m_buffers.reset(); // сбросили кешированное значение
        m_has_own_data = true;
        m_vertex = array;
    }
};

struct CGeometry {

     TVerArray m_vertex, m_normal, m_color, m_uv0, m_uv1;
     TIndArray m_indices, m_ver_per_face;
     QMap<int /*level*/, QWeakPointer<Buffers>> m_buffers_cache;

     QSharedPointer buffers(int level, BufferTypes types)
     {
          auto result = m_buffers_cache[level].lock();
          if (!result) {
              result = qMakeShared<Buffers>();
              m_buffers_cache[level] = result; // записали в кэш
          }

          // заполнили недостающие буфера
          if (types & BufferTypes::VertexBuffer)
             result->m_vertex_buffer = createVertexBuffer(m_vertex);
          ...

          return result;
     }

     void setVertex(TVerArray vertex)
     {
          m_vertex = vertex;
          for (auto item : m_buffers_cache) {
                if (item.second.m_vertex_buffer)
                    updateVertexBuffer(m_vertex, item.second.m_vertex_buffer); // обновляет буфер, не пересоздавая объект. Нужно чтобы те, кто хранит копии, обновили буфера
          }
     }
};



Название: Re: Использование QOpenGLBuffer
Отправлено: Racheengel от Декабрь 01, 2016, 18:14
Заметим что все уже стало как-то безумно сложно, я плохо представляю что надо делать при том или ином действии юзера - а ведь в задачке ничего сложного нет, буфер - один из самых популярных/ходовых приемов. Что-то не так

Надо разделить действия на "модифицирующие" (редактирование модели) и "не модифицирующие" (перемещение, поворот и пр.).
В случае "редактирования" данные копировать, соответственно и буфера с ними.
В случае "перемещения" юзать шареный буфер.
Убили модель - уменьшили счетчик у буфера. Проверили, если счетчик стал 0, убили буфер.


Название: Re: Использование QOpenGLBuffer
Отправлено: Igors от Декабрь 02, 2016, 07:39
Ну просто у вас потихоньку появляются дополнительные требования, которые усложняют (например "не хочу хранить буфер и данные парой").
??? Наоборот мне кажется правильным что каждый буфер должен быть членом контейнера, о чем я не раз говорил выше

Код:
typedef QVector<QVector3D> TVerArray;  
typedef QVector<int> TIndArray;

struct Buffers
{
    QSharedPointer<QOpenGLBuffer> m_vertex_buffer;
    QSharedPointer<QOpenGLBuffer> m_color_buffer;
  // итд
};

enum class BufferTypes {
     VertexBuffer = 0x1;
     ColorBuffer = 0x2,
     AllBufers = 0xff
};

struct CRenderObject {
...
// данные рисования, могут шариться любым кол-вом CRenderObject
    QSharedPointer<CGeometry> m_geometry;
 
// уровень деталировки, 0 = исходная геометрия
    int m_detailLevel;      
 
// Если к объекту применены нелинейные преобразования
// то его "личные" вертексы и нормали должны использоваться
// (вместо тех же данных в m_geomretry)
     bool m_has_own_data;
    TVerArray m_vertex, m_normal;

    QSharedPointer<Buffers> m_buffers;

    QSharedPointer<Buffers> buffers() // можно и ссылку
    {
         if (!m_buffers) {
             if (!m_has_own_data) { // вместо булки можно заюзаьт пару Optional<TVerArray> для массивов вершин и нормалей (ходят они парой или нет, яхз)
                  // берем шаредную копию
                  m_buffers = m_geometry->buffers(m_detailLevel, BufferTypes::AllBuffers);
             } else {  // берем шаредную копию и модифицируем её своими буферами
                  // qMakeShared в Qt нет
                  // оптимизация - передаем флаги нужных буферов чтобы не создавать лишнего (можно не передавать, тогда будут лишние операции)
                  auto copy = qMakeShared<Buffers>(*m_geometry->buffers(m_detailLevel, BufferTypes::AllBuffers & BufferTypes::~VertexBuffer));
                  copy->m_vertex_buffer = createVertexBuffer(m_vertex);
                  m_buffers = copy;
             }
         }
         return m_buffers;
    }
CRenderObject имеет шаред пойнтер на Buffers все члены которого тоже шаред пойнтеры. Трудновато понять кто кого шарит. Ну ладно, может "так надо", попробуем вникнуть, если где ошибся - поправляйте

Думаю метод buffers() вызывается рисованием - нормально. Но тогда чего мы держим его как член класса (типа "бери") если получаем (весьма) активным геттером? Ладно, это вкусовщина, дальше. Значит если у объекта личных вертексов нет - пусть m_geometry разбирается, если надо - сделает деталировку и буфера заполнит. Хорошо, согласен. А если вертексы личные.. все равно зовем m_geometry и потом "дозаполняем"

Так это проходит только без деталировки (уровень 0). Вот тогда мы можем заливать исходный контейнер в буфер или нет, т.е. исходные данные для буферирования у нас есть. А операция "деталировка" получает на вход ВСЕ контейнеры и возвращает ВСЕ новые (которые надо куда-то девать). Так работает любой генератор модели меняющий топологию. См картинку первого поста - число полигонов явно другое, ну значит и все остальное тоже.

Данный генератор имеет возможность "досчитать" вертексы и нормали, т.е. если расчет был произведен для какого-то уровня деталировки, то его можно относительно быстро повторить для того же уровня и той же топологии но с другими вертексами. Но тогда нужно создать развесистую структуру StencilTable в памяти, и ее тоже надо как-то шарить. Да, вот здесь действительно "потихоньку появляются дополнительные требования" - поэтому давайте пока без нее.

Надо разделить действия на "модифицирующие" (редактирование модели) и "не модифицирующие" (перемещение, поворот и пр.).
В случае "редактирования" данные копировать, соответственно и буфера с ними.
В случае "перемещения" юзать шареный буфер.
Убили модель - уменьшили счетчик у буфера. Проверили, если счетчик стал 0, убили буфер.
Разумно подытожить "какой ф-ционал должен быть зашарен". Допустим мы получаем такие сигналы или события

1) Само собой - копирование и удаление CRenderObject(s). 2 и более копий должны использовать одну копию буферов которая должна убиться если ее не юзает никто. Сюда же - если объект пере-создан полностью (изменилась топология) - то для него заводятся все новые буфера

2) Смена уровня деталировки - если копия с таким уровнем деталировки уже существует, то ее буфера должны юзаться (а не дублиться)

3) Вертексы объекта изменились (если он может их менять). Если объект имеет деталировку, то она должна быть пересчитана, новые вертексы и нормали слиты в буфера которые используются только данным CRenderObject и никем больше. Остальные буфера по-прежнему шарятся. StencilTable пока не трогаем

Вот собсно и все, скромный список  :)


Название: Re: Использование QOpenGLBuffer
Отправлено: Авварон от Декабрь 02, 2016, 13:30
Кажется, у меня не поддерживается только 3)
Чтобы его реализовать, надо "детачить" CGeometry у CRenderObject в момент, когда заполняются массивы.