Проблема с массивом вершин (GL_VERTEX_ARRAY).

Доброго времени суток!
С OpenGL разбираюсь около года. Всегда появляется одна и та же проблема - при большом количестве рисуемых объектов происходит ну просто МХАТовская пауза. Искал выход в дисплейных списках и массивах вершин, но как с ними нормально работать так и не понял.

По аналогии с уроком "Создание растрового редактора с помощью OpenGL - часть 5 (небольшая оптимизация)." попробовал изменить свой код, но увы, ничего не вышло.
Собственно нужно нарисовать сетку шестигранников (типа сот пчелиных) состоящую из 5-10 млн. шестигранников (код функции рисования приведу ниже). Если я рисую сетку из 30000 ячеек как в коде (переменная col_fig), то все нормально, но если больше 1 000 000, то видно как тормозить начинает. Хочу получить максимально возможную производительность кода потому, что прога рассчитана слабые компы.

<pre>private void DrawScene()
{
// Задем кол-во вершин;
int col_vert = 36; // кол-во точек на окружности - чем больше,
// тем более гладким будет круг;
int col_fig = 30000; // кол-во рисуемых кругов;
int all_point_okr = col_vert + 1; // Все точки окружности включая
// ее центр;
// Координаты точек окружности;
float[] CircleVertex = new float[all_point_okr * 2];
float Radius = 0.5f; // Радиус круга;

// Создаем массив под список вершин,
// каждая вершина задается 2 координатами.
Gl.glPushMatrix();
ARR_VERTEX = new float[col_fig * all_point_okr * 2];
// Записываем координаты точек окружности
// для их дальнейщей отрисовки;
for (int i = 0; i < col_fig; i++)
{
// Рисуем круг;
CreateCircle(Radius, col_vert, CircleVertex);
// Передвигаем его центр в следующую позицию;
MoveCenter(i, CircleVertex);
// Заполняем массив вершин
for (int j = 0; j < all_point_okr * 2; j += 2)
{
ARR_VERTEX[i * all_point_okr * 2 + j] = CircleVertex[j];
ARR_VERTEX[i * all_point_okr * 2 + j + 1] =
CircleVertex[j + 1];
}
}
Gl.glPopMatrix();

// Создание массива цветов,
// цвет имеет 3 компоненты цвета.
ARR_COLOR = new byte[col_fig * all_point_okr * 3];
// Заполняем массив цветов вершин.
// Пока не важно каким цветом.
for (int i = 0; i < col_fig * all_point_okr * 3; i += 3)
{
ARR_COLOR[i] = 30;
ARR_COLOR[i + 1] = 30;
ARR_COLOR[i + 2] = 30;
}

#region Заполнение массива отображения
// Массив индексов вершин - указывает какие
// из вершин находящихся всписке нужно нарисовать.
// Поскольку все рисуется треугольниками, то умножим на 3.
int[] index = new int[col_fig * col_vert * 3];
int npp = 0; // Нумератор индексов массива index;
// Цикл по фигурам;
for (int nom_fig = 0; nom_fig < col_fig; nom_fig++)
{
// Цикл по треугольникам каждой фигуры
for (int j = 0; j < col_vert; j++)
{
if (j != col_vert - 1)
{
index[npp] = nom_fig * all_point_okr;
index[npp + 1] = nom_fig * all_point_okr + j + 1;
index[npp + 2] = nom_fig * all_point_okr + j + 2;
}
else
{
index[npp] = nom_fig * all_point_okr;
index[npp + 1] = nom_fig * all_point_okr + j + 1;
index[npp + 2] = nom_fig * all_point_okr + 1;
}
npp += 3;
}
}
#endregion

Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);
Gl.glEnableClientState(Gl.GL_COLOR_ARRAY);

Gl.glVertexPointer(2, Gl.GL_FLOAT, 0, ARR_VERTEX);
Gl.glColorPointer(3, Gl.GL_UNSIGNED_BYTE, 0, ARR_COLOR);

Gl.glDrawElements(Gl.GL_TRIANGLES, index.Length,
Gl.GL_UNSIGNED_INT, index);

Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);
Gl.glDisableClientState(Gl.GL_COLOR_ARRAY);
}
</pre>
Хотелось бы конкретный совет получить :) Заранее большое спасибо!
Должно что-то типа такого получиться:
0       1625        03.08.2010        15

0  
04.08.2010 00:00:00
Привет, я ознакомился наскоро с кодом (просто тяжело думать после рабочего дня в +40 жары и в дыму… )

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

Тебе надо немного изменить подход. Смотри, то что ты делаешь сейчас:

Начинается визуализация кадра (нам надо успевать отрисовывать 30 кадров в секунду, чтобы у нас ничего не тормозило).

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

На какой принцип ты должен поменять схему работы:

1 этап:
Подготавливаем координаты одного объекта (!). Ведь у нас все объекты одинаковые, значит можно отрисовать один объект в дисплейный список (чтобы он был в кеше видеоадаптера).

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

3 этап: объект подготовлен, визуализирован в дисплейном списке. Теперь уже при визуализации кадра (DrawScene) мы перебираем объекты и визуализируем, вызывая диспленый список в нужных координатах.

for(...)
{
glPushMatrix
glTranslate -> в новое место для круга,
glCallList -> вызвали дисплейный список из кеша видеоадаптра (отрисовали круг в нужных координатах)
glPopMatrix
}

Вот такая схема примерно.

В уроке 13, где демонстрируется загрузка трехмерный моделей, для построения модели (упрощенный вариант) используется цикл с вызовами glVertex, но зато там можно легко отследить как строится дисплейный список и в последующем вызывается, при отрисовке кадра.

На счет 1 000 000 объектов — это конешн мощно. Тут надо еще учитывать, что врятли 10000000 объектов сразу видны — я думаю стоит задуматся над алгоритмом, который будет отсекать пирамиду видимости, проверять, какие из миллиона объектов видны сейчас, и визуализировать только их.

Еще вопрос — что за видеокарта на компьютере, где производительность падала только при подходе к 1000000 объектов, мне кажется что падение производительности должно было быть раньше :)
0  
04.08.2010 00:00:00
Большое спасибо!
Но дело все в том, что я начинал с подхода:
for(...)
{
glPushMatrix
glTranslate -> в новое место для круга,
glCallList -> вызвали дисплейный список из кеша видеоадаптра (отрисовали круг в нужных координатах)
glPopMatrix
}
и у меня тормоза были еще больше. Я подумал, что можно один раз нарисовать все, что я выше описал, запихнуть это в дисплейный список, а потом только цвет менять у нарисованной сетки.

Насчет «отсечения пирамиды видимости», здесь этот вариант не подходит потому, что на самом деле сетка состоит из 10 000 кругов и мне нужно от центра каждого круга сетки сделать радиальное размытие. Поскольку я не нашел в Джеле функции которая сама умеет делать радиальное размытие я решил создать «самоделку». На пример: центр каждой ячейки сетки черный и плавно к краям ячейки переходит в белый, я делаю так:
1. Рисую большой белый круг(основа ячейки, её нижний слой);
2. Уменьшаю радиус круга и изменяю цвет на пару единиц;
3. Рисую круг. И так, пока не будет достигнут черный цвет серединки ячейки.
Таким образом выходит, что для нормально смотрящегося радиального размытия нужно примерно 100 кругов разного диаметра и цвета, а в моем случае 10 000 * 100 = 1 000 000, вот так :( Поэтому нельзя отсекать ничего.
Я могу примерный рисунок того что должно быть на экране куда-нибудь положить, только не знаю куда.

Насчет «пересмотреть подход», по предложенному методу — сейчас буду пробовать, я увидел неплохой вариант, который может дать прирост производительности. Вечером еще отпишусь.
0  
04.08.2010 00:00:00
хм, надо будт как нить провести исследование, чтобы проверить что больше снижает производительность — большой массив геометрии для отрисовки или частый ее вызов из дисплейных списков. Думаю зависимость должна быть.

В остальном — теперь опробовать дисплейный список с твоим массивом геометрии, только создать его заранее.

>Я могу примерный рисунок того что должно быть на экране куда-нибудь положить, только не знаю куда.

Если вопрос хостинга изображений — ты можешь подгрузить изображение, при редактировании статьи (там в редакторе кнопка).
0  
04.08.2010 00:00:00
Я добавил рисунок к статье. Просто я думал, что можно как-то посреди комментов разместить картинку :)

Пока ни чего толкового не получилось :( Но ведь, как-то же в играх рисуют не текстурируя большое кол-во объектов? Дело еще вот в чем: я так понял, что сам C# (или Framework) отнимает часть производительности потому, что такой же код на С++ у меня тормозит гораздо меньше.

Еще вопрос, может все-таки существует нормальный алгоритм отрисовки радиального размытия?
0  
05.08.2010 00:00:00
а как ты сделал что у тебя код выделен стал?
0  
05.08.2010 00:00:00
Привет, isaer! Это все бубен помогает :) При создании комментов можно использовать html теги, не все конечно(я так и не смог туда запихнуть код с нормальной подсветкой символов, как в Microsoft Visual Studio), но стандартные теги можно использовать точно. Для выделения кода я писал следующее:
<pre>
Твой отформатированный текст
</pre>
Смотри что получиться:

Твой отформатированный текст

Только в начале нужно все ручками отредактировать например в блокноте. И еще, там у меня ограничение по ширине окна получилось и нижний СкролБар вылез, но я немного текст по-переносил и все гуд теперь.
0  
05.08.2010 00:00:00
о псиб
0  
11.08.2010 00:00:00
Ну что же, наконец я справился со своей проблемой!
Действительно нужно было по-внимательнее код посмотреть и немного подход изменить, вернулся к
for(...)
{
glPushMatrix
glTranslate -> в новое место для круга,
glCallList -> вызвали дисплейный список из кеша видеоадаптра (отрисовали круг в нужных координатах)
glPopMatrix
}
Работает намного быстрее, но возникла другая проблемка: для того, чтобы нарисовать сетку (картинка сверху прикреплена) с размытием ячеек хотел вначале рисовать один круг в DisplayList и начать создавать следующий DisplayList в котором будет храниться один из слоев сетки. При этом делал так:

1. Создал дисплейный список КРУГ
if (Gl.glIsList(LIST_CIRCLE) == Gl.GL_TRUE)
{
Gl.glDeleteLists(LIST_CIRCLE, 1);
LIST_CIRCLE = Gl.glGenLists(1);
}
Gl.glNewList(LIST_CIRCLE, Gl.GL_COMPILE);

// Разрешаем использование массивов вершин;
Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);
// Инициализируем точки вершин из массива;
Gl.glVertexPointer(2, Gl.GL_FLOAT, 0, ARR_VERTEX);
// Отрисовуем фигуру по точкам из памяти. Массив index
// задает последовательность соединения точек в фигуру;
Gl.glDrawElements(Gl.GL_TRIANGLES, index.Length, Gl.GL_UNSIGNED_INT, index);
// Запрещаем массивы вершин;
Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);

Gl.glEndList();


2. Далее в цикле создавал слои сетки, при этом функция создания дисплейного списка КРУГ вызавалась несколько раз:

for (int BlurLayer = 0; BlurLayer < LAYERS; BlurLayer++)
{
// Создаем дисплейный список КРУГ с новым радиусом;
LIST_CIRCLE = CreateCircle(Radius, Precision, LIST_CIRCLE);

Gl.glNewList((FLN_net + BlurLayer), Gl.GL_COMPILE);
Gl.glPushMatrix();

Gl.glTranslatef(shift_X, 0.0f, 0.0f);

for (int y = 0; y < col_vo/2; y++)
{
for (int x = 0; x < col_vo; x++)
{
Gl.glCallList(LIST_CIRCLE);
// Зеркально рисуем нижнюю строку ячеек;
Gl.glTranslatef(0.0f, -y * 3 * MAX_RAD, 0.0f);
Gl.glCallList(LIST_CIRCLE);
// Возвращаемсяна место;
Gl.glTranslatef(0.0f, y * 3 * MAX_RAD, 0.0f);
// Сдвигаемся по Х для рисования следующей
// ячейки текущей строки;
Gl.glTranslatef((float)(MAX_RAD * Math.Pow(3, 0.5)), 0.0f, 0.0f);
}
}

Gl.glPopMatrix();
Gl.glEndList();

// Изменяем (уменьшаем) радиус;
Radius -= step_radius;
}

При таком подходе я во всем списке СЕТКА (FLN_net), в котором было от 1 до 10 слоев (по разному пробовал), получал сетку с одинаковым радиусом ячеек. Т.е. все слои с одинаковым радиусом :(
Я подумал, что нельзя вызывать существующий дисплейный список внутри создаваемого, т.е.:
glNewList(new_list, GL_COMPILE);
glCallList(exist_list);
glEndList();
И сделал так: из функции создания списка КРУГ выбросил создание дисплейного списка и в функции СЕТКА вызывал отрисовку из массива вершин:
3. Измененная функция рисования дисплейного списка СЕТКА

if (Gl.glIsList(FLN_net) == Gl.GL_TRUE)
{
Gl.glDeleteLists(FLN_net, LAYERS);
FLN_net = Gl.glGenLists(LAYERS);
}
Gl.glNewList(FLN_net, Gl.GL_COMPILE);

for (int BlurLayer = 0; BlurLayer < LAYERS; BlurLayer++)
{
ARR_VERTEX = new float[Precision];
CreateCircle(Radius, Precision, ARR_VERTEX);
// Инициализируем точки вершин из массива;
Gl.glVertexPointer(2, Gl.GL_FLOAT, 0, ARR_VERTEX);

Gl.glNewList((FLN_net + BlurLayer), Gl.GL_COMPILE);
Gl.glPushMatrix();

Gl.glTranslatef(shift_X, 0.0f, 0.0f);
for (int y = 0; y < col_vo / 2; y++)
{
for (int x = 0; x < col_vo; x++)
{
// Отрисовуем фигуру по точкам из памяти.
Gl.glDrawElements(Gl.GL_TRIANGLES, index.Length, Gl.GL_UNSIGNED_INT, index);
// Зеркально рисуем нижнюю строку ячеек;
Gl.glTranslatef(0.0f, -y * 3 * MAX_RAD, 0.0f);
Gl.glDrawElements(Gl.GL_TRIANGLES, index.Length, Gl.GL_UNSIGNED_INT, index);
// Возвращаемсяна место;
Gl.glTranslatef(0.0f, y * 3 * MAX_RAD, 0.0f);
// Сдвигаемся по Х для рисования следующей
// ячейки текущей строки;
Gl.glTranslatef((float)(MAX_RAD * Math.Pow(3, 0.5)), 0.0f, 0.0f);
}
}

Gl.glPopMatrix();
Gl.glEndList();

// Изменяем (уменьшаем) радиус;
Radius -= step_radius;
}

С таким кодом рисуется на Ура :)))
Но все таки хотелось бы разобраться, можно вызывать уже созданный дисплейный список (ДС) внутри создаваемого нового ДС или нельзя?
Заранее большое спасибо за ответы!
0  
25.11.2010 00:00:00
Можно использовать функцию из класса using System.Threading;
Thread S
с помощью нее в фоне будеш расчитывать 6 уголники в другом потоке
т.е. построить алгоритм таким образом чтобы отрисовывались изначально те 6 граники которые легче увидеть а остальное пусть рисуеться по мере надобнасти или вобще заюзай маску на плигон с масивом 6 угльников сеть получиться такая же и быстрее.
0  
01.12.2010 00:00:00
Привет! Спасибо за варианты, но ведь увеличение количества потоков тоже дает дополнительную нагрузку на процессор, не так ли?

По-поводу маски на полигон я думал, но как-то криво у меня это получилось реализовать :) так что пока отказался от этой затеи. Тем более что сетка отрисовуется довольно быстро даже на слабых машинах.
0  
01.12.2010 00:00:00
каждому потоку например в Csharp можно установить приоритет выставив его можно сбалансировать нагрузку на процессор
0  
08.08.2011 00:00:00
а что такое?.. не работает на другом компе?.. надо проверить, как только будет возможность
0  
29.08.2011 00:00:00
Не работает. Там где компилирую(mingw) работает, а на другом компе нет. Прога видит(на др. компе) все файлы доступ к которым производился стандартным fstream, а те файлы, что грузятся с помощью ilLoadImage(«c_str»), не видит. Не знаю с чем связанно, но предполагаю, что с кодировкой пути к файлу.
Проверить сам не могу в связи с поломкой жесткого диска, стоит Linux на флехи(.
Если есть возможность проверить, проверь пожалуйста, и сообщи результаты.)
0  
03.09.2011 00:00:00
ок, проверю, мне счас должны ноут подвезти — вот на нём и проверю.Результаты сообщу
0  
04.11.2011 00:00:00
так, как результаты?
^