ПрограммированиеФорумГрафика

Billboard (Биллборд/Спрайт) (комментарии)

#0
13:54, 21 июля 2009

Billboard (Биллборд/Спрайт) (комментарии)

Это сообщение сгенерировано автоматически.

#1
13:54, 21 июля 2009

Такой говно-код будет наверняка быстрее "fixed pipeline" варианта.

void gld_StartDrawScene(void)
{
  inv_yaw=180.0f-yaw;
  cos_inv_yaw = (float)cos(inv_yaw * M_PI / 180.f);
  sin_inv_yaw = (float)sin(inv_yaw * M_PI / 180.f);
  billboard_pitch = ((pitch > 87.0f && pitch <= 90.0f) ? 87.0f : pitch);
  cos_billboard_pitch = (float)cos(billboard_pitch * M_PI / 180.f);
  sin_billboard_pitch = (float)sin(billboard_pitch * M_PI / 180.f);
}

void gld_DrawSprite(GLSprite *sprite)
{
    float x1, x2, x3, x4, z1, z2, z3, z4;
    float y1, y2, cy, ycenter, y1c, y2c;
    float y1z2_y, y2z2_y;

    ycenter = (float)fabs(sprite->y1 - sprite->y2) * 0.5f;
    y1c = sprite->y1 - ycenter;
    y2c = sprite->y2 - ycenter;
    cy = sprite->y + ycenter;

    y1z2_y = -(y1c * sin_billboard_pitch);
    y2z2_y = -(y2c * sin_billboard_pitch);

    x1 = +(sprite->x1 * cos_inv_yaw - y1z2_y * sin_inv_yaw) + sprite->x;
    x2 = +(sprite->x2 * cos_inv_yaw - y1z2_y * sin_inv_yaw) + sprite->x;
    x3 = +(sprite->x1 * cos_inv_yaw - y2z2_y * sin_inv_yaw) + sprite->x;
    x4 = +(sprite->x2 * cos_inv_yaw - y2z2_y * sin_inv_yaw) + sprite->x;

    y1 = +(y1c * cos_billboard_pitch) + cy;
    y2 = +(y2c * cos_billboard_pitch) + cy;

    z1 = -(sprite->x1 * sin_inv_yaw + y1z2_y * cos_inv_yaw) + sprite->z;
    z2 = -(sprite->x2 * sin_inv_yaw + y1z2_y * cos_inv_yaw) + sprite->z;
    z3 = -(sprite->x1 * sin_inv_yaw + y2z2_y * cos_inv_yaw) + sprite->z;
    z4 = -(sprite->x2 * sin_inv_yaw + y2z2_y * cos_inv_yaw) + sprite->z;

    glBegin(GL_TRIANGLE_STRIP);
      glTexCoord2f(sprite->ul, sprite->vt); glVertex3f(x1, y1, z1);
      glTexCoord2f(sprite->ur, sprite->vt); glVertex3f(x2, y1, z2);
      glTexCoord2f(sprite->ul, sprite->vb); glVertex3f(x3, y2, z3);
      glTexCoord2f(sprite->ur, sprite->vb); glVertex3f(x4, y2, z4);
    glEnd();
}

Когда-то я заменил им следующее

  float xcenter = (sprite->x1 + sprite->x2) * 0.5f;
  float ycenter = (sprite->y1 + sprite->y2) * 0.5f;
  glTranslatef(sprite->x + xcenter, sprite->y + ycenter, sprite->z);
  glRotatef(inv_yaw, 0.0f, 1.0f, 0.0f);
  glRotatef(billboard_pitch, 1.0f, 0.0f, 0.0f);
  glTranslatef(-xcenter, -ycenter, 0);

  glBegin(GL_TRIANGLE_STRIP);
    glTexCoord2f(sprite->ul, sprite->vt); glVertex3f(sprite->x1, sprite->y1, 0.0f);
    glTexCoord2f(sprite->ur, sprite->vt); glVertex3f(sprite->x2, sprite->y1, 0.0f);
    glTexCoord2f(sprite->ul, sprite->vb); glVertex3f(sprite->x1, sprite->y2, 0.0f);
    glTexCoord2f(sprite->ur, sprite->vb); glVertex3f(sprite->x2, sprite->y2, 0.0f);
  glEnd();

На уровне с 10к монстров в сцене прирост фпс был около 20%

#2
14:20, 21 июля 2009

А можно еще быстрее

 float modelview[16];
  if(BILLBOARDED)
  {
    // get the current modelview matrix
    glGetFloatv(GL_MODELVIEW_MATRIX , modelview);
    

    // undo all rotations
    // beware all scaling is lost as well 
    for(int i=0; i<3; i++ ) 
      for(int j=0; j<3; j++ ) {
        if ( i==j )
          modelview[i*4+j] = 1.0;
        else
          modelview[i*4+j] = 0.0;
      }

    // set the modelview with no rotations and scaling
    glLoadMatrixf(modelview);    
  }
#3
17:34, 21 июля 2009

Vel
> А можно еще быстрее
Кто тебе сказал, что это быстрее? По моим тестам это медленнее в разы. На готовой игре по таймдемо 89 фпс вместо 110.

static void l3dBillboardCheatSphericalBegin(void)
{
  float modelview[16];
  int i,j;

  // save the current modelview matrix
  glPushMatrix();

  // get the current modelview matrix
  glGetFloatv(GL_MODELVIEW_MATRIX , modelview);

  // undo all rotations
  // beware all scaling is lost as well 
  for(i = 0; i < 3; i++)
  {
    for(j = 0; j < 3; j++ )
    {
      if (i == j)
        modelview[i * 4 + j] = 1.0;
      else
        modelview[i * 4 + j] = 0.0;
    }
  }

  // set the modelview with no rotations
  glLoadMatrixf(modelview);
}

static void gld_DrawSprite(GLSprite *sprite)
{
    glPushMatrix();
    glTranslatef(sprite->x, sprite->y, sprite->z);
    l3dBillboardCheatSphericalBegin();
    glBegin(GL_TRIANGLE_STRIP);
    glTexCoord2f(sprite->ul, sprite->vt); glVertex3f(-sprite->x1, sprite->y1, 0.0f);
    glTexCoord2f(sprite->ur, sprite->vt); glVertex3f(-sprite->x2, sprite->y1, 0.0f);
    glTexCoord2f(sprite->ul, sprite->vb); glVertex3f(-sprite->x1, sprite->y2, 0.0f);
    glTexCoord2f(sprite->ur, sprite->vb); glVertex3f(-sprite->x2, sprite->y2, 0.0f);
    glEnd();
    glPopMatrix();
    glPopMatrix();
}
#4
18:50, 21 июля 2009

entryway
>Кто тебе сказал, что это быстрее? По моим тестам это медленнее в разы. На готовой игре по таймдемо получил 89 фпс вместо 110
Не может быть!

Vel
Лучше цикл не использовать, т.к. каждая итерация - 1 такт процессора, что медленнее чем задать вручную:

modelview[0] = 1.0;
modelview[1] = 0.0;
......
modelview[15] = 1.0;
#5
19:12, 21 июля 2009

vizgl
> Лучше цикл не использовать, т.к. каждая итерация - 1 такт процессора, что
> медленнее чем задать вручную
Не гони уже про такты цикла. Не в них проблема. Заменить цикл на прямую инициализацию - вообще никак не сказывается на fps.

#6
19:35, 21 июля 2009

entryway
> Не гони уже про такты цикла. Не в них проблема. Заменить цикл на прямую
> инициализацию - вообще никак не сказывается на fps.
Я и не говорил что в них проблема, это так, небольшая оптимизация. Код, который привел Vel, должен работать быстрее чем твой, т.к. нету кучи рассчетов!

#7
20:50, 21 июля 2009

> Я и не говорил что в них проблема, это так, небольшая оптимизация.
Оптимизация которая ничего не дает - бессмысленна.

vizgl
> Код, который привел Vel, должен работать быстрее чем твой, т.к. нету кучи
> рассчетов!
Если тебе кажется, что что-то должно работать быстрее, то это не значит, что так оно и есть.

Мой код быстр потому, что в нем кроме простейших арифметический вычислений, в количестви 40 штук, ничего нет. В коде Vel'а расчеты замаскированы. Заполнение массива и множественные вызовы матричных функций далеко не бесплатны. Универсальные матричные функции в принципе не бесплатны.

Только в одном glTranslate() только арифметических операций 24 штуки. Плюс затраты на передачу параметров. glPush(Pop)Matrix() - еще тяжелее чем glTranslate(), а они в коде Vel'а используются 4 раза. glLoadMatrix() - это еще один мемкопи на 16 элементов.

У меня же считается только то, что действительно нужно. Сорок элементарных математических операций (+, -, *, /) - это необходимый минимум, ну или почти минимум. Чудес не бывает. То, что нужно сделать - сделать придется.  Ориентировочно, мой код работает в 5-10 раз быстрее кода vel'а. Оно и не удивительно, я специально по совету Кармака (хех) переписывал матричные преобразования в инлайн математику. Не имея под рукой даже карандаша, на генерацию говно-кода с первого поста ушло больше часа :)

John Carmack said:
Using matrix ops to move floors and rotate sprites was less efficient than just duplicating the data for sectors and building the quad directly for sprites.

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

#8
21:23, 21 июля 2009

Быстрее все же шейдером ;)

#9
21:31, 21 июля 2009

Blew_zc
> Быстрее все же шейдером ;)
Скорее всего, но проверить "на скорую руку" не могу. Плюс оно мне не нужно, так как мое приложение должно работать даже на "калькуляторах", а реализовывать дополнительный более быстрый метод для современных акселераторов - бессмысленно.

#10
21:31, 21 июля 2009

entryway
Только лучше примитив заменить на GL_QUADS и glBegin()/glEnd() сделать один раз для всей пачки партиклов, а не для каждого партикла - прирост будет хорошим...

#11
21:47, 21 июля 2009

Executor
> Только лучше примитив заменить на GL_QUADS и glBegin()/glEnd() сделать один раз
> для всей пачки партиклов, а не для каждого партикла - прирост будет хорошим...
У меня не партиклы! У меня монстры и айтемы! :) Не думаю, что glDrawArrays() мне как-то поможет, так как повторяющихся спрайтов в кадре, как правило, или почти нет или совсем нет.

Кармак советовал такое.

John Carmack said:
Sprites were culled to the occlusion buffer.

Вернее не советовал, а констатировал изменения, которые он сделал в порте, над которым я работаю, при портировании на ифон для увеличения производительности. Но я, конечно же, ничего не понял, так как в геймдеве я нуб, а порт развиваю чисто на энтузиазме иногда по вечерам. Так что или вы мне сейчас раскажете, что он имел в виду :), или мне придется подождать релиза прбума на ифоне и посмотреть непосредственно в сорцах (GPL).

Вообще-то я пробовал угадать, что он имел в виду, но потерпел фиаско :)

John Carmack said:
I meant testing against the 1D occlusion buffer inside the game, not testing with occlusion query, which doesn't even exist on the iPhone. Using hardware occlusion query would almost certainly be a net performance loss for sprites, because it would rasterize all the empty pixels to see that it is visible, then draw the textured sprite. Even when using queries for complex objects, you need to at least double buffer your queries so that you are using the query from the previous frame to determine if you are going to draw the object. Failing to do that makes the query result call into almost a glFinish, which may make things half the speed.

John Carmack

ПрограммированиеФорумГрафика

Тема в архиве.