Рисование нескольких движущихся объектов - PullRequest
0 голосов
/ 28 января 2012

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

Из-за моего бесконечного количества исследований по этому вопросу большинство, похоже, упоминают VBO, однако я не уверен, что это подойдет для моего случая, когда положение каждого объекта меняется в каждом кадре.

Я использую OpenGL 1 вна данный момент - у меня есть рабочий код, и на устройствах поколения 3/4 + (те, которые поддерживают OpenGL 2, га) он работает с приемлемой частотой кадров - однако при тестировании на моем (старом, да) iPod touch второго поколения этоочень медленный и практически неиграбельный.

Мой код состоит из статического массива вершин для «куба» и массива, содержащего позицию и цвет каждого куба.Мой игровой логический цикл обновляет положение каждого куба в массиве.На данный момент я перебираю массив кубов, вызывая glTranslatef и glDrawArrays для каждого куба.Из того, что я прочитал, это очень неэффективно, однако я совершенно не понимаю, как бы вы его оптимизировали.Любые идеи?

(может быть, я не должен стремиться к старым, снятым с производства устройствам iOS, но, учитывая мое убеждение, что мой код невероятно неэффективен, я полагаю, что это поможет моим будущим усилиям независимо от того, найду ли я способадрес это)

Ответы [ 2 ]

0 голосов
/ 28 января 2012

Для таких простых объектов я бы сделал один большой VBO, скажем, 200 Objects * NrVerticesPerCube, поместил бы все данные с чередованием Vertex, Normal, UV, Vertex, Normal, UV и т. Д.

Я делаю нечто подобное в анимации ключевых кадров бобра в моей игре, я начинаю с чего-то вроде этого:

glGenBuffers(1, &vboObjects[vboGroupBeaver]);
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]);
glBufferData(GL_ARRAY_BUFFER, beaverVerts*8*sizeof(GLfloat), 0, GL_STATIC_DRAW);
vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
NSString *path;
path = [[NSBundle mainBundle] pathForResource:@"beaver01" ofType:@"bin"];
NSFileHandle *model = [NSFileHandle fileHandleForReadingAtPath:path];
float vertice[8];
int counter = 0;
while (read([model fileDescriptor], &vertice, 8*sizeof(float))) {
    memcpy(vbo_buffer, vertice, 8*sizeof(GLfloat));        // 0
    vbo_buffer += 8*sizeof(GLfloat);
    counter++;
}
glUnmapBufferOES(GL_ARRAY_BUFFER); 
glBindBuffer(GL_ARRAY_BUFFER, 0);

Это создает мой буфер VBO с правильным размером (в данном случае 8 * sizeof (GLfloat) с 3 вертами, 3 нормалями и 2UV) и копирует первый ключевой кадр в буфер, вы можете сделать то же самое с исходным положение объекта, или просто оставьте это и вычислите последнее ...

Затем в каждом кадре я делаю интерполяцию между 2 ключевыми кадрами для каждой вершины моего бобра и просто выполняю один вызов отрисовки, это очень быстро для 4029 вершин, которые есть у моего бобра, и работает на скорости 60FPS на моем iPhone 3G.

Для вас, выполняющих только gltranslations, было бы еще проще, просто добавьте значения x, y, z к каждой вершине каждого куба.

Вы бы обновили его так:

    glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]);
    GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);

Свяжите буфер vbo и mapit с буфером var. Рассчитайте, что вы хотите на временную переменную.

            memcpy(vbo_buffer, currentVert, 6*sizeof(GLfloat));        // 0
            vbo_buffer += 8*sizeof(GLfloat);

Скопируйте его и обновите буфер до следующего объекта, повторяйте, пока все объекты не обновятся ... Вы также можете сделать все обновления в отдельном массиве и скопировать весь массив, но тогда вы будете копировать дополнительную информацию, которая обычно не меняется (нормали и УФ). Или вы не можете использовать чередующиеся данные и копировать это ...

        glUnmapBufferOES(GL_ARRAY_BUFFER);

Отмена отображения буфера VBO

    glVertexPointer(3, GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL));
    glNormalPointer(GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat)));
    glTexCoordPointer(2, GL_FLOAT,8*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat))); 
    glDrawArrays(GL_TRIANGLES, 0, beaverVerts);

Настройте свой вызов на отрисовку и нарисуйте все это ...

Если вам нужно вращать объекты, а не просто переводить их, вам нужно будет добавить некоторые умножения матриц по пути ...

РЕДАКТИРОВАТЬ **

хорошо, сделать gltranste вручную очень просто (вращение и т. Д. Немного сложнее).

Я использую чередующуюся плоскость, нарисованную с использованием TRIANGLE_STRIP вместо треугольников, но принцип тот же.

  float beltInter[] = {
0.0, 0.0, 0.0,             // vertices[0]
0.0, 0.0, 1.0,             // Normals [0]
6.0, 1.0,                  // UV [0]
0.0, 480, 0.0,             // vertices[1]
0.0, 0.0, 1.0,             // Normals [1]
0.0, 1.0,                  // UV      [1]
320.0, 0.0, 0.0,           // vertices[2]
0.0, 0.0, 1.0,             // Normals [2]
6.0, 0.0,                  // UV      [2]
320.0, 480, 0.0,       // vertices[3]
0.0, 0.0, 1.0,             // Normals [3]
0.0, 0.0                   // UV      [3]
  };

Так что это чередующаяся вершина, у вас есть вершина, затем нормаль, затем UV, если вы не используете текстуры, замените UV цветом.

Самый простой способ - иметь массив со всеми объектами внутри (это легко сделать, если все ваши объекты имеют одинаковый размер) и выполнять обновление положения после рисования (вместо в середине фрейма opengl), лучше сделать отдельный поток, создайте 2 VBO, обновите одно из них, одновременно рисуя из другого, что-то вроде этого:

  • Тема 1 OpenGL DrawFrom VBO0
  • Поток 2 Обновления игры, обновите позиции во внутреннем массиве и скопируйте в VBO1, установите Var, сказав VBO1 да готово (поэтому поток 1 изменяется только с рисования на VBO1, когда все обновления сделаны).
  • Тема 1 OpenGL DrawFrom VBO1
  • Тема 2 Обновление игры, тоже самое, но обновление VBO0
  • продолжить с той же логикой

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

В любом случае, вернемся к теме

чтобы сделать эквивалент gltranslatef (10,20,30), просто сделайте:

 int   maxvertices = 4;
 float x = 10;
 float y = 20;
 float z = 30;
 int   counter = 0;
 int   stride  = 8;                // stride is 8 = 3 x vertice + 3 x normal + 2 x UV change to 3 x color or 4 x color depending on your needs
 glBindBuffer(GL_ARRAY_BUFFER, vboObjects[myObjects]);
 GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
 while (counter < (maxVertices*8)) {
      beltInter[counter]   += x;   // just sum the corresponding values to each
      beltInter[counter+1] += y;  
      beltInter[counter+2] += z;
      memcpy(vbo_buffer, currentVert, 3*sizeof(GLfloat));  // again only copy what you need, in this case only copying the vertices, if your're updating all the data, you can just do a single memcpy at the end instead of these partial ones
      vbo_buffer += stride*sizeof(GLfloat);     // forward the buffer
      counter += stride;         // only update the vertex, but you could update everything
 }
 glUnmapBufferOES(GL_ARRAY_BUFFER);

 glVertexPointer(3, GL_FLOAT, stride*sizeof(GLfloat), (GLvoid*)((char*)NULL));
 glNormalPointer(GL_FLOAT, stride*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat)));
 glTexCoordPointer(2, GL_FLOAT,stride*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat)));   
 glDrawArrays(GL_TRIANGLE_STRIP, 0, maxVertices);

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

Все это было написано по памяти на лету, так что, возможно, драконы: -)

Надеюсь, это поможет.

0 голосов
/ 28 января 2012

Вы могли бы немного оптимизировать, прикрепив все координаты для всех ваших кубов в одном массиве и нарисовав его одним вызовом glDrawArrays.

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

Сброс координат куба в один большой массив,и присвойте каждому объекту куба индекс в этом массиве, чтобы вы могли сохранять свою логику обновления достаточно разделенной (как, например, куб n владеет координатами в диапазоне от x до y и отвечает за их обновление, но когда вы фактически рисуете координатывы запускаете glDrawArrays непосредственно в централизованном массиве координат вместо циклического прохождения объектов куба и их рендеринга по отдельности).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...