Оптимизация рендеринга OpenGL - PullRequest
2 голосов
/ 07 мая 2019

Я столкнулся с проблемой низкой производительности при рендеринге в OpenGL с использованием Assimp.Сцена имеет 367727 треугольников.В то же время 298084 является моделью шахмат.Я не думаю, что проблема в шейдерах, потому что:

128x128 окно: 44 (43,7657) FPS, 22,849 мс

256x256 окно: 42 (40,9563) FPS, 24,4162 мс

Окно

512x512: 35 (34,8007) FPS, 28,7351 мс

1024x1024; окно: 22 (21,084) FPS, 47,4293 мс

Но если я не рисую шахматы, то в окне1366x763: 55 (54,8424) FPS, 18,2341 мс

Кроме того, изменение разрешения карты теней не сильно влияет на FPS.

На сцене имеется 2 точечных источника света с FPSПотери при рисовании этой модели для каждого из проходов ~ 10 FPS (от 23 до 55).Т.е. нет разницы, где я рисую эту модель: на карте глубины или в «цветовой текстуре».Потери ~ одинаковы.Я загружаю модель со следующими параметрами: aiProcess_Triangulate | aiProcess_CalcTangentSpace | aiProcess_JoinIdenticalVertices

И выполняю рендеринг следующим образом:

inline void pointer(GLint location, int count, GLuint buffer) {
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glVertexAttribPointer(
        location, // attribute location
        count,    // count (1, 2, 3 or 4)
        GL_FLOAT, // type
        GL_FALSE, // is normalized?
        0,        // step
        nullptr   // offset
    );
}

inline void pointerui(GLint location, int count, GLuint buffer) {
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glVertexAttribIPointer(
        location, // attribute location
        count,    // count (1, 2, 3 or 4)
        GL_UNSIGNED_INT, // type
        0,        // step
        nullptr   // offset
    );
}
...
pointer(cs.inPosition, 3, model.shape->meshes[i].getVerticesBuffer());
pointer(cs.inNormal, 3, model.shape->meshes[i].getNormalsBuffer());
pointer(cs.inTangent, 3, model.shape->meshes[i].getTangentsBuffer());
pointer(cs.inBitangent, 3, model.shape->meshes[i].getBitangentsBuffer());
pointer(cs.inTexCoord, 2, model.shape->meshes[i].getTexCoordsBuffer());

if (model.shape->bonesPerVertex != 0) {
    pointer(cs.inBoneWeights, 4, model.shape->meshes[i].getBoneWeightsBuffer());
    pointerui(cs.inBoneIds, 4, model.shape->meshes[i].getBoneIdsBuffer());
}

modelMatrix = &model.transformation;
updateMatrices();

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.shape->meshes[i].getIndicesBuffer());
glDrawElements(GL_TRIANGLES, model.shape->meshes[i].indices.size(), GL_UNSIGNED_INT, nullptr)

Вот сама сцена: scene

РЕДАКТИРОВАТЬ : vertex_shader.glsl , frag_shader.glsl

Я извиняюсь, что фрагментный шейдер трудно читать,Я еще не полностью завершил работу над ним

Мой графический процессор NVGF 920mx

EDIT : здесь захват из renderdoc

1 Ответ

1 голос
/ 10 мая 2019

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

В большинстве случаев такого типа сложно дать точный ответ, но есть общие идеи для поиска решения.

Знаете, использование шейдеров в любом случае создает иллюзию чего-то реального. Если вы стремитесь к точности, вы все равно обратили бы внимание на трассировку лучей и другие более реалистичные способы рендеринга изображений. Пока вы работаете с рендерингом в реальном времени и с шейдерами, все сводится к обману глаз, чтобы сделать сцену реалистичной, даже если это не так. Вот почему вы должны искать дешевые трюки, чтобы ваша сцена выглядела более реалистично, чем она есть на самом деле.


Самый эффективный способ улучшить свою производительность - просто рисовать меньше. Если вы можете, попробуйте превратить ваши высокополигональные сетки в низкополигональные с нормальным отображением. Это очень распространенный способ почти во всех проектах, направленных на повышение производительности в режиме реального времени, то есть повышение рейтинга FPS. В целом, если вы стремитесь к высокой детализации и точности, вам потребуется много вычислительной мощности, но если вы сможете пойти на какие-то новые компромиссы, чтобы сохранить ощущение детализации, вы можете улучшить производительность. Рисование примерно полумиллиона вершин с частотой 50 кадров в секунду (то есть около 25 миллионов вершин в секунду) может быть слишком много для вашего графического процессора.

То же самое относится и к вашим шейдерам. Вы уверены, что используете все источники света, настроенные вашим шейдером? Если у вас есть, например, три источника света, вы можете значительно улучшить производительность, если ваш шейдер может управлять только тремя источниками света. Помните, что количество источников света является константой для каждого фрагмента (*): вам не нужно думать, сколько источников света имеет ваша сцена, все зависит от того, сколько источников света учитывается для каждого пикселя фрагмента (то есть источники света обрабатываются во фрагментном шейдере).

(*) Ну, это, вероятно, специфическая для модели константа, потому что даже то, что имеет значение только то, сколько источников света используется для каждого фрагмента, вероятно, трудно посылать источники света для каждого фрагмента - легче отправлять источники света для каждой модели для рендеринга.

И, как правило, всегда хорошо переносить вычисления в вершинный шейдер, позволяя ему предварительно вычислять значения для вашего фрагментного шейдера. Например, вы можете подумать о переключении вашего фрагментного шейдера на использование пространства камеры, и в этом случае вы можете выполнять вычисления TBN полностью в вершинном шейдере.

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


В случае, если вы видите, что это узкое место в памяти, ограничивающее вашу производительность, есть старая, но все же хорошая статья об этой проблеме: https://www.khronos.org/opengl/wiki/Vertex_Specification_Best_Practices

В таких случаях первое, что вы можете сделать, - это сжать атрибуты вашей вершины. Например, вы вполне можете преобразовать матрицу TBN в тип GL_INT_2_10_10_10 (если доступно), что позволит сжать матрицу с 9-х чисел с плавающей точкой до 3-х с плавающей точкой. Это потеряет точность, но в основном это не вызовет видимых эффектов. Вы можете даже пойти так далеко, что отправите TBN в качестве кватерниона, который сожмет 9 x поплавков до одного, если вам достаточно точности.

Кроме того, вы можете попробовать чередующиеся буферы (формируя VBO из массива структур). Неизвестно, имеет ли он какой-либо эффект, и даже если он имеет, он очень сильно связан с GPU. В некоторых графических процессорах это может улучшить производительность вершинного кэша.

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

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

...