【问题标题】:UV Sphere - Getting Rid of join/vertex "dots"UV Sphere - 摆脱连接/顶点“点”
【发布时间】:2018-09-06 19:12:00
【问题描述】:

我正在创建一个 UV 球体(类似于划分为纬度线的地球球)。我这样做是:

  1. 计算每个平行纬度圆周围的所有顶点(例如,每个圆 72 个点)
  2. 使用 GL_TRIANGLE_STRIP 填充每个纬度圈之间的每个“切片”。

不幸的是,我一直在我原本完美的球体上看到点。

什么会导致这种情况,我该如何摆脱它?

void CSphere2::AddVertices( void )
{
  #define SPHERE2_RES 72

  // Create sphere using horizontal slices/circles
  int nPointsPerCircle = SPHERE2_RES;
  int nStackedCircles  = SPHERE2_RES;

  GLfloat r          = m_Size;
  GLfloat yAngle     = - (PI / 2.0f);  // Start at -90deg and work up to +90deg (south to north pole)
  GLfloat yAngleStep = PI / nStackedCircles;

  // Sweep angle is zero initially for pointing towards me (-Z direction)
  GLfloat horizSweepAngle = 0;
  GLfloat horizSweepStep  = ( 2 * PI ) / nPointsPerCircle;

  // Each time we have a slice, the top and bottom radii vary..
  GLfloat sweepRadiusTop;
  GLfloat sweepRadiusBottom;

  GLfloat xBottomPoint;
  GLfloat zBottomPoint;

  GLfloat xTopPoint;
  GLfloat zTopPoint;

  for( int c = 0; c < nStackedCircles; c ++ )
  {
    // Draw a circle - note that this always uses two circles - a top and bottom circle.
    GLfloat yBottomCircle;
    GLfloat yTopCircle;

    yTopCircle    = r * sin( yAngle + yAngleStep );
    yBottomCircle = r * sin( yAngle );

    std::vector<GLfloat> vBottom_x;
    std::vector<GLfloat> vBottom_z;

    std::vector<GLfloat> vTop_x;
    std::vector<GLfloat> vTop_z;

    sweepRadiusTop    = r * cos( yAngle + yAngleStep );
    sweepRadiusBottom = r * cos( yAngle );

    // Add 1 face - a triangle strip per slice..
    AddFace();

    m_Faces[ c ].m_DrawType = GL_TRIANGLE_STRIP;

    // Now work out the position of the points around each circle - bottom points will always be the
    //      same as the last top circle points.. but I'm not going to try optimising yet..
    for( int s = 0; s < nPointsPerCircle; s ++ )
    {
      GLfloat xBottomPoint = sweepRadiusBottom * sin( horizSweepAngle );
      GLfloat zBottomPoint = sweepRadiusBottom * cos( horizSweepAngle );

      GLfloat xTopPoint = sweepRadiusTop * sin( horizSweepAngle + horizSweepStep );
      GLfloat zTopPoint = sweepRadiusTop * cos( horizSweepAngle + horizSweepStep );

      vBottom_x.push_back( xBottomPoint );
      vBottom_z.push_back( zBottomPoint );

      vTop_x.push_back( xTopPoint );
      vTop_z.push_back( zTopPoint );

      horizSweepAngle += horizSweepStep;
    }

    // OPTIMISE THIS!!
    for( int s = 1; s <= nPointsPerCircle + 1; s ++ )
    {
      if( s == nPointsPerCircle + 1 )
      {
        // Join the last bottom point with the very first top point - go one more to fully close and leave no vertical gap
        xTopPoint = vTop_x[ 1 ];
        zTopPoint = vTop_z[ 1 ];

        xBottomPoint = vBottom_x[ 0 ];
        zBottomPoint = vBottom_z[ 0 ];
      }
      else
      if( s == nPointsPerCircle )
      {
        // Join the last bottom point with the very first top point
        xTopPoint = vTop_x[ 0 ];
        zTopPoint = vTop_z[ 0 ];

        xBottomPoint = vBottom_x[ s - 1 ];
        zBottomPoint = vBottom_z[ s - 1 ];
      }
      else
      {
        xTopPoint = vTop_x[ s ];
        zTopPoint = vTop_z[ s ];

        xBottomPoint = vBottom_x[ s - 1 ];
        zBottomPoint = vBottom_z[ s - 1 ];
      }

      // Calculate and add the Normal for each vertex.. Normal for a point on surface of a Sphere2 should be the unit vector going from centre
      //      of the Sphere2 to the surface (x,y,z).
      //
      //      If centre of Sphere2 is 0,0,0 then N = | {x,y,z} - {0,0,0} | = | {x,y,z} |
      glm::vec3 vNormalBottom = glm::vec3( xBottomPoint, yBottomCircle, zBottomPoint );
      vNormalBottom = glm::normalize( vNormalBottom );

      glm::vec3 vNormalTop = glm::vec3( xTopPoint, yTopCircle, zTopPoint );
      vNormalTop = glm::normalize( vNormalTop );

      // Add bottom of slice vertex..
      m_Faces[ c ].AddVertexWithNormal( xBottomPoint, yBottomCircle, zBottomPoint, vNormalBottom.x, vNormalBottom.y, vNormalBottom.z );

      // Add top of slice vertex, next step position..
      m_Faces[ c ].AddVertexWithNormal( xTopPoint, yTopCircle, zTopPoint, vNormalTop.x, vNormalTop.y, vNormalTop.z );
    }

    int nVertexCount = m_Faces[ c ].m_Vertices.size();

    m_Faces[ c ].m_SideCount = nVertexCount;

    // Face colouring colours the vertices so they need to be created first..
    m_Faces[ c ].SetRGB( m_RGBA.r, m_RGBA.g, m_RGBA.b );

    yAngle += yAngleStep;
  }
}

void CSphere2::Create( GLfloat fSize )
{
  m_Size = fSize;

  // Must add vertices first..
  AddVertices();

  glGenBuffers( 1, &m_VBO );
  glBindBuffer( GL_ARRAY_BUFFER, m_VBO );

  int nFaces = m_Faces.size();
  int nVertexCount = 0;

  for( int f = 0; f < nFaces; f ++ )
  {
    nVertexCount += m_Faces[ f ].m_Vertices.size();
    m_Faces[ f ].m_SideCount = nVertexCount;
  }

  // Define the size of the buffer.. 
  glBufferData( GL_ARRAY_BUFFER, sizeof( COLVERTEX ) * nVertexCount, NULL, GL_STATIC_DRAW );

  int nOffset = 0;

  for( int f = 0; f < nFaces; f ++ )
  {
    // Copy in each vertice's data..
    for( int v = 0; v < (int) m_Faces[ f ].m_Vertices.size(); v ++ )
    {
      glBufferSubData( GL_ARRAY_BUFFER, nOffset, sizeof( COLVERTEX ), &m_Faces[ f ].m_Vertices[ v ].m_VertexData );

      nOffset += sizeof( COLVERTEX );
    }
  }

  glBindBuffer( GL_ARRAY_BUFFER, 0 );
}

我从其他地方复制的其他示例也遇到了同样的问题,所以我坐下来自己计算了一下,但我仍然遇到同样的问题。

顶点着色器:

char *vs3DShader  = 

"#version 140\n"

"#extension GL_ARB_explicit_attrib_location : enable\n"

"layout (location = 0) in vec3 Position;"
"layout (location = 1) in vec4 color;"
"layout (location = 2) in vec3 aNormal;"

"out vec4 frag_color;"
"out vec3 Normal;"
"out vec3 FragPos;"

"uniform mat4 model;"
"uniform mat4 view;"
"uniform mat4 projection;"

"void main()"
"{"
"  FragPos = vec3(model * vec4(Position, 1.0));"

"  gl_Position = projection * view * vec4(FragPos, 1.0);"

//  Rotate normals with respect to current Model matrix (object rotation).
"  Normal = mat3( transpose( inverse( model ) ) ) * aNormal; "

"  // Pass vertex color to fragment shader.. \n"
"  frag_color = color;"
"}"
;

片段着色器:

char *fs3DShader  = 

"#version 140\n"
"in  vec4 frag_color;"
"in  vec3 Normal;"
"in  vec3 FragPos;"

"out vec4 FragColor;"

"uniform vec3 lightPos; "
"uniform vec3 lightColor; "

"void main()"
"{"
"  // ambient\n"
"  float ambientStrength = 0.1;"
"  vec3 ambient = ambientStrength * lightColor;"

"  // diffuse \n"
"  vec3 norm = normalize(Normal);"
"  vec3 lightDir = normalize(lightPos - FragPos);"
"  float diff = max(dot(norm, lightDir), 0.0);"
"  vec3 diffuse = diff * lightColor;"

"  vec3 result = (ambient + diffuse) * frag_color;"

"  FragColor = vec4(result, 1.0);"
"}"                         
;

我是否缺少某种平滑选项?我尝试将我的视点移动到球体的两侧,并且点都在周围发生 - 所以问题不是三角形条带“关闭”的地方 - 它遍布整个球体。

请看下面的亮点:

更新:我只是想证明回绕到零度不是问题。下面是每个圆圈只有四分之一被扫过 90 度时的图像。点仍然出现在中间区域。

【问题讨论】:

    标签: c++ opengl opengl-es-2.0 fragment-shader vertex-shader


    【解决方案1】:

    浮点精度不是无限的,在处理超越数时,不可避免地会累积误差。

    这是一个示例程序,它执行与您的程序相同的循环,除了它只是打印出最终角度:

    #include <cmath>
    #include <cstdio>
    
    int main() {
        const int N = 72;
        const float step = std::atan(1.0f) * 8 / N;
        float x = 0.0f;
        for (int i = 0; i < N; i++) {
            x += step;
        }
        std::printf("x - 2pi = %f\n", x - 8 * std::atan(1.0f));
        return 0;
    }
    

    在我的系统上,它打印出 -0.000001。接近于零,但不是零。

    如果您希望网格中的两个点对齐,请不要给它们不同的值。否则你会得到像这样的小接缝。

    解决这个问题的典型方法是生成一个像这样的圆圈:

    #include <cmath>
    #include <cstdio>
    #include <vector>
    
    struct vec2 { float x, y; };
    
    int main() {
        const int N = 72;
        const float step = std::atan(1.0f) * 8 / N;
        std::vector<vec2> circle;
        for (int i = 0; i < N; i++) {
            float a = i * step;
            circle.push_back({ std::cos(a), std::sin(a) });
        }
        return 0;
    }
    

    在圆圈中的每个点circle[i],下一个点现在只是circle[(i+1)%N]。这可确保circle[N-1] 之后的点始终与circle[0] 完全相同。

    【讨论】:

    • 感谢您的回答。我不认为它的问题。您所描述的不会影响圆圈“加入”本身的位置 - 即 - 当完成 360 度扫描时?如果你看一下我更新的图像,我只扫过球体的 90 度,我仍然有圆点出现在带的“中间”。
    • 当切片的顶部圆变成上面切片的底部圆时,我也在重用相同的顶点坐标。这就是让我认为平滑或其他问题可能更多的原因。在点比周围区域更亮的情况下,这是否表明法线在某些位置可能是错误的,或者某些顶点“突出”太多?
    • 我不相信sin(x + y)x += y; 相同,然后计算sin(x)。您可以自己检查。
    • 哦,我明白你的意思了。这就说得通了。角度的垂直增量。我以为你指的是水平扫描。羞耻的人在寻求帮助时投反对票。必须让他们感觉良好,让某人失望。
    【解决方案2】:

    我发现问题中的顶点计算存在一些问题。由于我每次在水平切片周围扫描时都会计算底部和顶部顶点,因此会产生舍入/精度误差。当前切片顶部的一个点应该与下一个切片的底部点相同 - 但我在按照 Dietrich Epp 的建议递增后计算这个顶部和底部。这导致了不同的值。我的解决方案是重新使用之前的顶部圆顶点作为下一个切片的底部顶点。

    我也没有使用相同的扫描角计算顶部和底部圆的 x/z 位置 - 我增加了我不应该做的角度。

    所以从根本上说,问题是由 2 个重叠的顶点引起的,这些顶点本应具有相同的坐标,但是却略有不同。

    这是有效的解决方案:

    void CSphere2::AddVertices( void )
    {
      #define SPHERE2_RES 72
    
      // Create sphere using horizontal slices/circles
      int nPointsPerCircle = SPHERE2_RES;
      int nStackedCircles  = SPHERE2_RES;
    
      GLfloat r          = m_Size;
      GLfloat yAngle     = - (PI / 2.0f);  // Start at -90deg and work up to +90deg (south to north pole)
      GLfloat yAngleStep = PI / nStackedCircles;
    
      // Sweep angle is zero initially for pointing towards me (-Z direction)
      GLfloat horizSweepAngle = 0;
      GLfloat horizSweepStep  = ( 2 * PI ) / nPointsPerCircle;
    
      // Each time we have a slice, the top and bottom radii vary..
      GLfloat sweepRadiusTop;
      GLfloat sweepRadiusBottom;
    
      GLfloat xBottomPoint;
      GLfloat zBottomPoint;
    
      GLfloat xTopPoint;
      GLfloat zTopPoint;
    
      std::vector<GLfloat> vCircle_x;
      std::vector<GLfloat> vCircle_z;
    
      std::vector<GLfloat> vLastCircle_x;
      std::vector<GLfloat> vLastCircle_z;
    
      int nFace = 0;
    
      for( int c = 0; c <= nStackedCircles + 1; c ++ )
      {
        // Draw a circle - note that this always uses two circles - a top and bottom circle.
        GLfloat yBottomCircle;
        GLfloat yTopCircle;
    
        yTopCircle    = r * sin( yAngle + yAngleStep );
        yBottomCircle = r * sin( yAngle );
    
        sweepRadiusTop = r * cos( yAngle );
    
        GLfloat xCirclePoint;
        GLfloat zCirclePoint;
    
        horizSweepAngle = 0;
    
        vCircle_x.clear();
        vCircle_z.clear();
    
        // Now work out the position of the points around each circle - bottom points will always be the
        //      same as the last top circle points.. 
        for( int s = 0; s < nPointsPerCircle; s ++ )
        {
          zCirclePoint = sweepRadiusTop * sin( horizSweepAngle );
          xCirclePoint = sweepRadiusTop * cos( horizSweepAngle );
    
          vCircle_x.push_back( xCirclePoint );
          vCircle_z.push_back( zCirclePoint );
    
          horizSweepAngle += horizSweepStep;
        }
    
        if( c == 0 )
        {
          // First time around there is no last circle, so just use the same points..
          vLastCircle_x = vCircle_x;
          vLastCircle_z = vCircle_z;
    
          // And don't add vertices until next time..
          continue;
        }
    
        // Add 1 face - a triangle strip per slice..
        AddFace();
    
        m_Faces[ nFace ].m_DrawType = GL_TRIANGLE_STRIP;
    
        for( int s = 1; s <= nPointsPerCircle + 1; s ++ )
        {
          if( s == nPointsPerCircle + 1 )
          {
            // Join the last bottom point with the very first top point
            xTopPoint = vCircle_x[ 1 ];
            zTopPoint = vCircle_z[ 1 ];
    
            xBottomPoint = vLastCircle_x[ 0 ];
            zBottomPoint = vLastCircle_z[ 0 ];
          }
          else
          if( s == nPointsPerCircle )
          {
            // Join the last bottom point with the very first top point
            xTopPoint = vCircle_x[ 0 ];
            zTopPoint = vCircle_z[ 0 ];
    
            xBottomPoint = vLastCircle_x[ s - 1 ];
            zBottomPoint = vLastCircle_z[ s - 1 ];
          }
          else
          {
            xTopPoint = vCircle_x[ s ];
            zTopPoint = vCircle_z[ s ];
    
            xBottomPoint = vLastCircle_x[ s - 1 ];
            zBottomPoint = vLastCircle_z[ s - 1 ];
          }
    
          // Calculate and add the Normal for each vertex.. Normal for a point on surface of a Sphere2 should be the unit vector going from centre
          //      of the Sphere2 to the surface (x,y,z).
          //
          //      If centre of Sphere2 is 0,0,0 then N = | {x,y,z} - {0,0,0} | = | {x,y,z} |
          glm::vec3 vNormalBottom = glm::vec3( xBottomPoint, yBottomCircle, zBottomPoint );
          vNormalBottom = glm::normalize( vNormalBottom );
    
          glm::vec3 vNormalTop = glm::vec3( xTopPoint, yTopCircle, zTopPoint );
          vNormalTop = glm::normalize( vNormalTop );
    
          // Add bottom of slice vertex..
          m_Faces[ nFace ].AddVertexWithNormal( xBottomPoint, yBottomCircle, zBottomPoint, vNormalBottom.x, vNormalBottom.y, vNormalBottom.z );
    
          // Add top of slice vertex, next step position..
          m_Faces[ nFace ].AddVertexWithNormal( xTopPoint, yTopCircle, zTopPoint, vNormalTop.x, vNormalTop.y, vNormalTop.z );
        }
    
        // Now copy the current circle x/y positions as the last circle positions (bottom circle)..
        vLastCircle_x = vCircle_x;
        vLastCircle_z = vCircle_z;
    
        int nVertexCount = m_Faces[ nFace ].m_Vertices.size();
    
        m_Faces[ nFace ].m_SideCount = nVertexCount;
    
        // Face colouring colours the vertices so they need to be created first..
        m_Faces[ nFace ].SetRGB( m_RGBA.r, m_RGBA.g, m_RGBA.b );
    
        yAngle += yAngleStep;
    
        nFace ++;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-04
      • 2016-08-26
      • 2012-01-07
      • 2016-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多