Проблема с массивом вершин (GL_VERTEX_ARRAY).
Доброго времени суток!
С OpenGL разбираюсь около года. Всегда появляется одна и та же проблема - при большом количестве рисуемых объектов происходит ну просто МХАТовская пауза. Искал выход в дисплейных списках и массивах вершин, но как с ними нормально работать так и не понял.
[spoiler]
По аналогии с уроком "Создание растрового редактора с помощью 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>
Хотелось бы конкретный совет получить Заранее большое спасибо!
Должно что-то типа такого получиться:
С OpenGL разбираюсь около года. Всегда появляется одна и та же проблема - при большом количестве рисуемых объектов происходит ну просто МХАТовская пауза. Искал выход в дисплейных списках и массивах вершин, но как с ними нормально работать так и не понял.
[spoiler]
По аналогии с уроком "Создание растрового редактора с помощью 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>
Хотелось бы конкретный совет получить Заранее большое спасибо!
Должно что-то типа такого получиться:
Насколько я понял, ты в цикле собираешь огромный массив координат, чтобы его пачкой передать для визуализации.
Тебе надо немного изменить подход. Смотри, то что ты делаешь сейчас:
Начинается визуализация кадра (нам надо успевать отрисовывать 30 кадров в секунду, чтобы у нас ничего не тормозило).
Ты подготавливаешь огромный массив, потом передаешь его для визуализации прямо во время кадра.
На какой принцип ты должен поменять схему работы:
1 этап:
Подготавливаем координаты одного объекта (!). Ведь у нас все объекты одинаковые, значит можно отрисовать один объект в дисплейный список (чтобы он был в кеше видеоадаптера).
2 этап: Мы заранее (еще до визуализации кадров) визуализируем один объект в дисплейный список. Теперь, когда в дальнейшем будет происходить визуализация, мы просто будем менять координаты.
3 этап: объект подготовлен, визуализирован в дисплейном списке. Теперь уже при визуализации кадра (DrawScene) мы перебираем объекты и визуализируем, вызывая диспленый список в нужных координатах.
for(...)
{
glPushMatrix
glTranslate -> в новое место для круга,
glCallList -> вызвали дисплейный список из кеша видеоадаптра (отрисовали круг в нужных координатах)
glPopMatrix
}
Вот такая схема примерно.
В уроке 13, где демонстрируется загрузка трехмерный моделей, для построения модели (упрощенный вариант) используется цикл с вызовами glVertex, но зато там можно легко отследить как строится дисплейный список и в последующем вызывается, при отрисовке кадра.
На счет 1 000 000 объектов — это конешн мощно. Тут надо еще учитывать, что врятли 10000000 объектов сразу видны — я думаю стоит задуматся над алгоритмом, который будет отсекать пирамиду видимости, проверять, какие из миллиона объектов видны сейчас, и визуализировать только их.
Еще вопрос — что за видеокарта на компьютере, где производительность падала только при подходе к 1000000 объектов, мне кажется что падение производительности должно было быть раньше
Но дело все в том, что я начинал с подхода:
for(...)
{
glPushMatrix
glTranslate -> в новое место для круга,
glCallList -> вызвали дисплейный список из кеша видеоадаптра (отрисовали круг в нужных координатах)
glPopMatrix
}
и у меня тормоза были еще больше. Я подумал, что можно один раз нарисовать все, что я выше описал, запихнуть это в дисплейный список, а потом только цвет менять у нарисованной сетки.
Насчет «отсечения пирамиды видимости», здесь этот вариант не подходит потому, что на самом деле сетка состоит из 10 000 кругов и мне нужно от центра каждого круга сетки сделать радиальное размытие. Поскольку я не нашел в Джеле функции которая сама умеет делать радиальное размытие я решил создать «самоделку». На пример: центр каждой ячейки сетки черный и плавно к краям ячейки переходит в белый, я делаю так:
1. Рисую большой белый круг(основа ячейки, её нижний слой);
2. Уменьшаю радиус круга и изменяю цвет на пару единиц;
3. Рисую круг. И так, пока не будет достигнут черный цвет серединки ячейки.
Таким образом выходит, что для нормально смотрящегося радиального размытия нужно примерно 100 кругов разного диаметра и цвета, а в моем случае 10 000 * 100 = 1 000 000, вот так Поэтому нельзя отсекать ничего.
Я могу примерный рисунок того что должно быть на экране куда-нибудь положить, только не знаю куда.
Насчет «пересмотреть подход», по предложенному методу — сейчас буду пробовать, я увидел неплохой вариант, который может дать прирост производительности. Вечером еще отпишусь.
В остальном — теперь опробовать дисплейный список с твоим массивом геометрии, только создать его заранее.
>Я могу примерный рисунок того что должно быть на экране куда-нибудь положить, только не знаю куда.
Если вопрос хостинга изображений — ты можешь подгрузить изображение, при редактировании статьи (там в редакторе кнопка).
Пока ни чего толкового не получилось Но ведь, как-то же в играх рисуют не текстурируя большое кол-во объектов? Дело еще вот в чем: я так понял, что сам C# (или Framework) отнимает часть производительности потому, что такой же код на С++ у меня тормозит гораздо меньше.
Еще вопрос, может все-таки существует нормальный алгоритм отрисовки радиального размытия?
<pre>
Твой отформатированный текст
</pre>
Смотри что получиться:
Твой отформатированный текст
Только в начале нужно все ручками отредактировать например в блокноте. И еще, там у меня ограничение по ширине окна получилось и нижний СкролБар вылез, но я немного текст по-переносил и все гуд теперь.
Действительно нужно было по-внимательнее код посмотреть и немного подход изменить, вернулся к
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;
}
С таким кодом рисуется на Ура ))
Но все таки хотелось бы разобраться, можно вызывать уже созданный дисплейный список (ДС) внутри создаваемого нового ДС или нельзя?
Заранее большое спасибо за ответы!
Thread S
с помощью нее в фоне будеш расчитывать 6 уголники в другом потоке
т.е. построить алгоритм таким образом чтобы отрисовывались изначально те 6 граники которые легче увидеть а остальное пусть рисуеться по мере надобнасти или вобще заюзай маску на плигон с масивом 6 угльников сеть получиться такая же и быстрее.
По-поводу маски на полигон я думал, но как-то криво у меня это получилось реализовать так что пока отказался от этой затеи. Тем более что сетка отрисовуется довольно быстро даже на слабых машинах.
Проверить сам не могу в связи с поломкой жесткого диска, стоит Linux на флехи(.
Если есть возможность проверить, проверь пожалуйста, и сообщи результаты.)