Как я могу оптимизировать свою функцию расчета Soft Normal? - PullRequest
0 голосов
/ 21 апреля 2020

Мне нужно создать мягкие нормали для моего приложения OpenGL, но время обработки этой функции абсурдно. После его профилирования кажется, что время обработки моего многопользовательского emplace до абсурда велико. Я провел некоторое исследование и выяснил, что вы можете использовать подсказки для вставки / вставки, но не знаете, как это реализовать или даже если мой метод - лучший способ генерации нормалей. Если вы знаете лучший метод или способ ускорить этот сегмент кода, я хотел бы услышать его.

void Controller::TerrainFactory::GenerateNormals(std::vector<Blue::Vertex> &verticies,
                                             std::vector<unsigned int> indicies) {
std::vector<Blue::Faces> faces;
std::multimap<unsigned int, size_t> setFaces;

for (size_t index = 0; index < indicies.size(); index += 3) {
    faces.push_back(Blue::Faces{});
    auto &face       = faces.at(faces.size() - 1);
    face.indicies[0] = indicies.at(index);
    face.indicies[1] = indicies.at(index + 1);
    face.indicies[2] = indicies.at(index + 2);
    face.normal      = glm::triangleNormal(verticies.at(face.indicies[0]).position,
                                      verticies.at(face.indicies[1]).position,
                                      verticies.at(face.indicies[2]).position);
    const auto size  = faces.size() - 1;
    setFaces.emplace(face.indicies[0], size);
    setFaces.emplace(face.indicies[1], size);
    setFaces.emplace(face.indicies[2], size);
}

for (unsigned index = 0; index < verticies.size(); ++index) {
    int count      = 0;
    auto itr1      = setFaces.lower_bound(index);
    auto itr2      = setFaces.upper_bound(index);
    auto avgNormal = glm::vec3{};
    while (itr1 != itr2) {
        if (itr1->first == index) {
            avgNormal += faces.at(itr1->second).normal;
            ++count;
        }
        ++itr1;
    }
    verticies.at(index).normals = avgNormal / static_cast<float>(count);
}


struct Vertex {
    glm::vec3 position = {};
    glm::vec2 texCoords = {};
    glm::vec3 normals = {};
};

struct Faces {
    unsigned int indicies[3] = {};
    glm::vec3 normal = {};
};

Ответы [ 2 ]

0 голосов
/ 21 апреля 2020

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

Копирование Параметр indicies вместо передачи его по ссылке также звучит избыточно и дорого.

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

void Controller::TerrainFactory::GenerateNormals(std::vector<Blue::Vertex>& verticies,
                                                 const std::vector<unsigned int>& indicies)
{
  // first loop could be skipped, if verticies[i].normals were no spoiled before
  for (size_t vert = 0; vert < verticies.size(); ++vert)
  {
    verticies[vert].normals = glm::vec3();
  }

  // append triangle normals to every vertex
  for (size_t index = 0; index < indicies.size(); index += 3)
  {
    Blue::Vertex& vert1 = verticies[indicies[index]];
    Blue::Vertex& vert2 = verticies[indicies[index + 1]];
    Blue::Vertex& vert3 = verticies[indicies[index + 2]];
    // glm::triangleNormal() probably returns normalized vector,
    // which is better to compute unnormalized
    glm::vec3 triNormal = glm::triangleNormal(vert1.position,
                                              vert2.position,
                                              vert3.position);
    vert1.normals += triNormal;
    vert2.normals += triNormal;
    vert3.normals += triNormal;
  }

  // normalize normal vectors
  for (size_t vert = 0; vert < verticies.size(); ++vert)
  {
    Blue::Vertex& vertBlue = verticies[vert];
    vertBlue.normals = glm::normalize(vertBlue.normals);
  }
0 голосов
/ 21 апреля 2020

Алгоритм настолько медленный, потому что время выполнения алгоритма квадратично c (O (n2)). На самом деле у вас есть вложенное l oop, где вы перебираете все вершины и ищете для каждой вершины грани соседних граней. Это может быть упрощено. Когда вы вычисляете векторы нормали лица, то векторы нормали лица смежных вершин могут суммироваться в одном и том же l oop:

for (size_t i = 0; i < indicies.size(); i += 3)
{
    // compute face normal
    glm::vec3 faceNormal = glm::triangleNormal(
      verticies[indicies[i]].position,
      verticies[indicies[i+1]].position,
      verticies[indicies[i+2]].position);

    // add face normal to each of the 3 vertex normal of the face
    for (int j = 0; j < 3; ++j)
    {
      verticies[indicies[i+j]].normals += faceNormal;
    }
}

Наконец нормализовать Единичный вектор нормали атрибут каждой вершины:

for (auto &vertex : verticies)
{
  vertex.normals = glm::normalize(vertex.normals);
} 

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

...