使用 OpenGL 绘制椭球的问题

Posted

技术标签:

【中文标题】使用 OpenGL 绘制椭球的问题【英文标题】:Issue in drawing ellipsoid with OpenGL 【发布时间】:2020-03-14 19:17:41 【问题描述】:

这是我使用带有着色器的 OpenGL 创建和绘制椭圆体的代码

  const float _2pi = 2.0f * M_PI;

  std::vector<glm::vec3> positions;
  std::vector<glm::vec3> normals;
  std::vector<glm::vec2> textureCoords;

  for(int i = 0; i <= stacks; ++i) 

    // V texture coordinate
    float V = i / (float)stacks;
    float phi = V * M_PI;

    for( int j = 0; j <= slices; ++j) 

      // U texture coordinate
      float U = j / (float)slices;
      float theta = U * _2pi;

      float X = a * cos(theta) * cos(phi);
      float Y = b * cos(theta) * sin(phi);
      float Z = c * sin(theta);

      positions.push_back( glm::vec3( X, Y, Z) );
      normals.push_back( glm::vec3(X, Y, Z) );
      textureCoords.push_back( glm::vec2(U, V) );

    

  

  // Now generate the index buffer
  std::vector<GLuint> indicies;

  for(int i=0; i <slices*stacks+slices; ++i) 

    indicies.push_back(i);
    indicies.push_back(i + slices + 1);
    indicies.push_back(i + slices);

    indicies.push_back(i + slices + 1);
    indicies.push_back(i);
    indicies.push_back(i + 1);

  

  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);

  glGenBuffers(4, vbo);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
  glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(glm::vec3), positions.data(), GL_STATIC_DRAW);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
  glEnableVertexAttribArray(0);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
  glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_STATIC_DRAW);
  glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
  glEnableVertexAttribArray(2);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
  glBufferData(GL_ARRAY_BUFFER, textureCoords.size() * sizeof(glm::vec2), textureCoords.data(), GL_STATIC_DRAW);
  glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  glEnableVertexAttribArray(8);

  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
  glBufferData( GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(GLuint), indicies.data(), GL_STATIC_DRAW);

  glBindVertexArray(0);
  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

虽然这是我用来渲染它的代码:

  glBindVertexArray(vao);

  glEnableVertexAttribArray(0);

  if(style == glObject::STYLE::WIREFRAME) glDrawElements(GL_LINES,     (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);
  if(style == glObject::STYLE::SOLID)     glDrawElements(GL_TRIANGLES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);

  glBindVertexArray(0);

它似乎有效,但我有一些问题。 查看图像可能会在错误的位置看到一些顶点。 我认为这与指标有关,但我不确定。 我注意到这取决于我使用的堆栈或切片的数量

更新:

我考虑了@Rabbid76 的建议,这就是结果。 渲染中不再有退化的顶点和三角形。 但是渲染不等于@Rabbid76的渲染,就像顶点的旋转。

决赛:

这是创建顶点和索引的代码:

      std::vector<glm::vec3> positions;
      std::vector<glm::vec3> normals;
      std::vector<glm::vec2> textureCoords;

      for(int i = 0; i <= stacks; ++i) 

        // V texture coordinate.
        float V = i / (float)stacks;
        float phi = V * M_PI;

        for( int j = 0; j <= slices; ++j) 

          // U texture coordinate.
          float U = j / (float)slices;
          float theta = U * 2.0f * M_PI;

          float X = cos(theta) * sin(phi);
          float Y = cos(phi);
          float Z = sin(theta) * sin(phi);

          positions.push_back( glm::vec3( X, Y, Z) * radius );
          normals.push_back( glm::vec3(X, Y, Z) );
          textureCoords.push_back( glm::vec2(U, V) );

        

      

      // Now generate the index buffer
      std::vector<GLuint> indicies;

      int noPerSlice = slices + 1;

      for(int i=0; i < stacks; ++i) 

        for (int j=0; j < slices; ++j) 

          int start_i = (i * noPerSlice) + j;

          indicies.push_back( start_i );
          indicies.push_back( start_i + noPerSlice + 1 );
          indicies.push_back( start_i + noPerSlice );

          indicies.push_back( start_i + noPerSlice + 1 );
          indicies.push_back( start_i );
          indicies.push_back( start_i + 1 );

        

      

      glGenVertexArrays(1, &vao);
      glBindVertexArray(vao);

      glGenBuffers(4, vbo);

      glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
      glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(glm::vec3), positions.data(), GL_STATIC_DRAW);
      glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
      glEnableVertexAttribArray(0);

      glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
      glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_STATIC_DRAW);
      glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
      glEnableVertexAttribArray(2);

      glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
      glBufferData(GL_ARRAY_BUFFER, textureCoords.size() * sizeof(glm::vec2), textureCoords.data(), GL_STATIC_DRAW);
      glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
      glEnableVertexAttribArray(8);

      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(GLuint), indicies.data(), GL_STATIC_DRAW);

      glBindVertexArray(0);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

这是渲染图:

    glBindVertexArray(vao);

    glEnableVertexAttribArray(0);

    if(style == glObject::STYLE::WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    if(style == glObject::STYLE::SOLID)     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glDrawElements(GL_TRIANGLES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);

    glBindVertexArray(0);

【问题讨论】:

【参考方案1】:

您混淆了phithetatheta 是 [0, 2*PI] 范围内切片圆周上的点的角度。 phi 是 [-PI, PI] 范围内的点从南到北的角度:

for (int i = 0; i <= stacks; ++i) 

    // V texture coordinate
    float V = i / (float)stacks;
    float phi = V * M_PI - M_PI/2.0;

    for ( int j = 0; j <= slices; ++j) 

        // U texture coordinate
        float U = j / (float)slices;
        float theta = U * _2pi;

        float X = a * cos(phi) * cos(theta);
        float Y = b * cos(phi) * sin(theta);
        float Z = c * sin(phi);

        positions.push_back( glm::vec3( X, Y, Z) );
        normals.push_back( glm::vec3(X, Y, Z) );
        textureCoords.push_back( glm::vec2(U, V) );
    

切片(围绕圆周)的点数为noPerSlice = slices + 1。四边形点的第一个索引是start_i = (i * noPerSlice) + j,其中i 是堆栈的索引,j 是切片周围的索引。围绕圆周创建slices 四边形,从南到北创建stacks 切片:

int noPerSlice = slices + 1;
for(int i=0; i < stacks; ++i) 
    for (int j = 0; j < slices; ++j) 

        int start_i = (i * noPerSlice) + j;

        indicies.push_back( start_i );
        indicies.push_back( start_i + noPerSlice + 1 );
        indicies.push_back( start_i + noPerSlice );

        indicies.push_back( start_i + noPerSlice + 1 );
        indicies.push_back( start_i );
        indicies.push_back( start_i + 1 );
    


【讨论】:

感谢重播!!!我更新了代码,但有问题。我喜欢在渲染中的顶点旋转。请参阅更新问题。谢谢;) @thewoz 这个问题是造成的,因为你正在生成三角形,但你却发现了GL_LINES。生成Line primitives或绘制GL_TRIANGLES并使用glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);【参考方案2】:

我认为

slices*stacks+slices

应该是

slices*stacks+stacks

+stacks 用于每个堆栈中重复顶点的额外四边形

虽然这固定了索引的数量,但沿 theta 等于 0 的重复顶点仍有退化三角形

【讨论】:

以上是关于使用 OpenGL 绘制椭球的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 OpenGL 保存 OpenGL 绘制?

OpenGL:使用退化三角形绘制线

使用 glut、opengl 绘制散点图矩阵

旋转使用 OpenGL 绘制的 2D 对象

在 OpenGL 中绘制形状的标准(常用)方法是啥?

OpenGL——点的绘制(使用OpenGL来绘制可旋转坐标系的螺旋线)