【问题标题】:How can I optimize my Soft Normal calculation function?如何优化我的软法线计算功能?
【发布时间】:2020-04-21 04:50:54
【问题描述】:

我需要为我的 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 = {};
};

【问题讨论】:

    标签: c++ opengl optimization multimap normals


    【解决方案1】:

    这个任务不需要将每个面的法线收集到像std::multimap 这样的专用结构中。因为不需要区分一张脸和另一张脸,法线是归一化的向量 - 将每个三角形的法线直接累加到顶点数据中然后对其进行归一化更简单、更直接。

    复制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);
      }
    

    【讨论】:

      【解决方案2】:

      算法很慢,因为算法的运行时间是二次的(O(n2))。实际上,您有一个嵌套循环,您可以在其中迭代所有顶点,并在相邻面的面中搜索每个顶点。那可以简化。 当你计算人脸法向量时,那么相邻顶点的人脸法向量可以在同一个循环中求和:

      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;
          }
      }
      

      最后归一化Unit vector每个顶点的法线属性:

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

      注意,索引到它的面的映射是多余的,甚至面的列表也是不必要的。

      【讨论】:

        猜你喜欢
        • 2010-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-27
        • 1970-01-01
        • 2021-01-31
        • 2019-08-10
        相关资源
        最近更新 更多