【问题标题】:obj file - averaging normalsobj 文件 - 平均法线
【发布时间】:2012-08-21 19:33:12
【问题描述】:

我有一个以这种方式存储数据的 obj 文件:

v value1 value2 value3  
f value1 value2 value3

首先我计算面部的法线,然后为该面部的每个顶点分配:

for(int i = 0; i < verticesInd.size(); i+=3)
{
    glm::vec3 normal = glm::normalize(glm::cross(glm::vec3(vertices[verticesInd[i + 1]]) - glm::vec3(vertices[verticesInd[i]]), glm::vec3(vertices[verticesInd[i + 2]]) - glm::vec3(vertices[verticesInd[i]])));
    out_Normals[i] = normal;
    out_Normals[i + 1] = normal;
    out_Normals[i + 2] = normal;
}

为了实现平面着色,我可以复制顶点:

for(int i = 0; i < verticesInd.size(); i++)
{
    out_Vertices.push_back(vertices[verticesInd[i]]);
} 

然后使用 glDrawArrays 绘制对象:

glDrawArrays(GL_TRIANGLES, 0, out_Vertices.size());

为了获得平滑的阴影,我需要平均每个顶点的法线,但我不知道如何找到相邻的面。

Edit1:我没注意到f之前有一个s参数:

v value1 value2 value3
s 1  
f value1 value2 value3

Edit2:法线平均

glm::vec3 tNormal;
for(int i = 0; i < vertices.size(); i++)
{
    for(int  j = 0; j < verticesInd.size(); j++)
    {
        if(verticesInd[j] == i)
        {
            tNormal += faceNormals[j / 3];
        }
    }
    aNormals.push_back(glm::normalize(tNormal));
    tNormal = glm::vec3(0,0,0);
}

编辑 3 人脸法线:

for(int i = 0; i < verticesInd.size(); i+=3)
{
    glm::vec3 normal = glm::normalize(glm::cross(glm::vec3(vertices[verticesInd[i + 1]]) - glm::vec3(vertices[verticesInd[i]]), glm::vec3(vertices[verticesInd[i + 2]]) - glm::vec3(vertices[verticesInd[i]])));
    faceNormals.push_back(normal);
}

【问题讨论】:

    标签: opengl 3d


    【解决方案1】:

    在每个片段照明上关注这个人https://www.youtube.com/watch?v=MRD_zN0SWh0&feature=plcp。据我所知,它包含相同的方程式,用于在每个顶点的基础上找到法线。

    您需要一张面孔列表。如果您曾经见过波前 obj。它们包含多个三角形索引 4,7,2 1,2,3。每 3 个数字代表一张脸。使用普通的 Mesh,每个点只能使用 3 次。如果你找到每个组有一个 3 你可以找到每张脸。找到它们对应的正常值,然后平均。

    Alnitak 也有有效的信息。您有一个顶点列表,然后将它们按 3 个一组列出,以便您可以重用顶点(共享)作为面部数据。

    【讨论】:

    • 我的每个片段照明工作正常,您上面附加的视频与问题无关。在将它们发送到着色器之前,我需要平均法线。
    • 我不明白为什么你不能在着色器中平均它们。如果您的对象是静态的,那么预先计算法线很有用。否则,您可以通过多种方式将法线输入着色器。制服、属性、顶点数组、缓冲区对象并在那里平均它们。在编辑中详细了解您当前的问题。
    • 我的对象是静态的,所以预先计算法线应该更快,否则我应该在着色器中进行吗?
    • 如果只计算一次,则不应在着色器中执行此操作。着色器将继续计算不变的数字。除非您的物体移动或您的灯光移动,否则您应该预先计算它。我不会,因为让着色器更容易做到这一点。但就性能而言,您可以阻止着色器完成这项工作。
    【解决方案2】:

    我不知道如何找到相邻的面孔。

    将面列表添加到您的临时 OBJ-loader 顶点存储中。在处理面线时,将新面添加到它所引用的每个顶点的面列表中。

    这样你可以在最后旋转所有顶点,查找它所属的面,抓取面法线,然后平均它们。

    如果您的 OBJ 没有如此好的内置连接信息(并且没有要求必须这样做),那么您必须对每个顶点进行最近邻搜索以找到顶点(和相应的面)就在它附近。使用您最喜欢的spatial index 来加快此类查询的速度。

    【讨论】:

      【解决方案3】:

      在大多数对象格式中,相邻面应该是共享个顶点。在顶点处找到平滑的阴影法线应该只是对使用该顶点的任何面的法线进行平均的问题。

      我建议您再创建一个与现有顶点数组大小相同的新数组。

      遍历每个面,并为每个顶点索引,将该顶点的面法线添加到新数组。

      在过程结束时,对结果法线向量进行归一化,然后使用它来代替之前计算的人脸法线。

      如果我正确理解了您的数据结构,它看起来像这样:

      glm::vec3 aNormals[];   // one for each vertex - use the appropriate constructor
      
      for (int i = 0; i < verticesInd.size(); ++i) {
          int f = i / 3;                 // which face is this index part of (3 per face?)
          int v = verticesInd[i];        // which vertex number is being used
          aNormals[v] += faceNormals[f]; // add the face normal to this vertex
      }
      
      // now normalise aNormals
      

      【讨论】:

      • 对象有 50000 个顶点和 100000 个面。对于每个顶点,我需要检查每个面,并且在我的 cpu 上计算大约需要 15 分钟。
      • @Irbis 听起来您正在以错误的方式迭代结构。查看更新。
      • “@Irbis 听起来你正在以错误的方式迭代结构” - 请查看 Edit2,它会带来正确的结果。
      • @Irbis 所以verticesInd 是(原始)数组f 来自文件映射三元组顶点偏移到面? (是的,这就是我所说的“错误的方式” - 你有一个来自 face -> vertex number 的数组,所以你应该使用它)。
      • 是的。感谢您的提示,现在计算只需几秒钟。
      猜你喜欢
      • 1970-01-01
      • 2013-12-22
      • 2015-06-30
      • 2014-08-09
      • 2017-09-01
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 2023-03-12
      相关资源
      最近更新 更多