Но я вижу главный недостаток:
- Для вращения ЦПУ должен рассчитать синус и косинус и выполнить 16 умножений для каждого спрайта.Насколько я знаю, вычисление синуса и косинуса очень дорого и медленно.
На самом деле sin и cos довольно быстры, на современных архитектурахдля выполнения требуется 1 такт, если конвейер ранее не останавливался.Однако, если каждый спрайт поворачивается индивидуально и используется обычная перспективная проекция усеченного конуса, автор этого кода не знает своей линейной алгебры.
Вся задача может быть значительно упрощена, если вспомнить, чтоМатрица просмотра модели отображает линейные локальные / мировые координаты в глазное пространство.Вращение находится в верхней левой подматрице 3 × 3, столбец, формирующий локальные базовые векторы.Взяв инверсию этой подматрицы, вы получите именно те векторы, которые вам нужны в качестве основы спрайта, чтобы отобразить планарное пространство глаза.В случае применения только поворотов (и, возможно, масштабирования), инверсия верхнего левого 3 × 3 является транспонированием;поэтому, используя верхнюю левую строку 3 × 3 в качестве основы спрайта, вы получите этот эффект, не выполняя никакой тригонометрии:
/* populates the currently bound VBO with sprite geometry */
void populate_sprites_VBO(std::vector<vec3> sprite_positions)
{
GLfloat mv[16];
GLfloat sprite_left[3];
GLfloat sprite_up[3];
glGetMatrixf(GL_MODELVIEW_MATRIX, mv);
for(int i=0; i<3; i++) {
sprite_left[i] = mv[i*4];
sprite_up[i] = mv[i*4 + 4];
}
std::vector<GLfloat> sprite_geom;
for(std::vector<vec3>::iterator sprite=sprite_positions.begin(), end=sprite_positions.end();
sprite != end;
sprite++ ){
sprite_geom.append(sprite->x + (-sprite_left[0] - sprite_up[0])*sprite->scale);
sprite_geom.append(sprite->y + (-sprite_left[1] - sprite_up[1])*sprite->scale);
sprite_geom.append(sprite->z + (-sprite_left[2] - sprite_up[2])*sprite->scale);
sprite_geom.append(sprite->x + ( sprite_left[0] - sprite_up[0])*sprite->scale);
sprite_geom.append(sprite->y + ( sprite_left[1] - sprite_up[1])*sprite->scale);
sprite_geom.append(sprite->z + ( sprite_left[2] - sprite_up[2])*sprite->scale);
sprite_geom.append(sprite->x + ( sprite_left[0] + sprite_up[0])*sprite->scale);
sprite_geom.append(sprite->y + ( sprite_left[1] + sprite_up[1])*sprite->scale);
sprite_geom.append(sprite->z + ( sprite_left[2] + sprite_up[2])*sprite->scale);
sprite_geom.append(sprite->x + (-sprite_left[0] + sprite_up[0])*sprite->scale);
sprite_geom.append(sprite->y + (-sprite_left[1] + sprite_up[1])*sprite->scale);
sprite_geom.append(sprite->z + (-sprite_left[2] + sprite_up[2])*sprite->scale);
}
glBufferData(GL_ARRAY_BUFFER,
sprite_positions.size() * sizeof(sprite_positions[0]), &sprite_positions[0],
GL_DRAW_STREAM);
}
Если шейдеры доступны, то вместо перекомпоновки данных спрайта на ЦП в каждом кадреМожно использовать геометрический шейдер или вершинный шейдер.Геометрический шейдер будет принимать вектор положения, масштаб, текстуру и т. Д. И испускать квадраты.Используя вершинный шейдер, вы отправите много квадратов [-1,1], где каждая вершина будет содержать центральное положение спрайта, которому она принадлежит, в качестве дополнительного атрибута vec3.
Наконец мои вопросы:
- Почему это быстрее?Действительно ли изменения состояния OpenGL настолько дороги?
Некоторые изменения состояния чрезвычайно дороги, вы будете стараться избегать их везде, где это возможно.Переключение текстур очень дорого, переключение шейдеров - умеренно дорого.
- Графический процессор оптимизирован для векторного умножения и вращения, а процессор - нет.Почему это не имеет значения?
Это не разница между GPU и CPU.Отличие графического процессора от центрального состоит в том, что он выполняет ту же последовательность операций с огромным количеством записей параллельно (каждый пиксель кадрового буфера отображается).ЦП, с другой стороны, запускает программу по одной записи за раз.
Но ЦП выполняют векторные операции так же хорошо, если не даже лучше, чем графические процессоры.Особенно там, где важна точность, процессоры по-прежнему предпочтительнее графических процессоров.MMX, SSE и 3DNow!такое наборы векторных математических инструкций.
- Можно ли использовать SpriteBatcher на рабочем столе с выделенной GFX-картой?
Возможно, не в этой формеТак как сегодня доступны геометрия и вершинные шейдеры, освобождающие процессор для других целей.Но что еще более важно это экономит пропускную способность между процессором и графическим процессором.Пропускная способность - узкое место, вычислительная мощность - не проблема номер один в наши дни (конечно, у человека никогда не хватает вычислительной мощности).
- Есть ли точка, в которой SpriteBatcher становится неэффективным?
Да, а именно узкое место передачи ЦП → ГП.Сегодня очень быстро используются геометрические шейдеры и экземпляры, чтобы делать такие вещи.