Search code examples
c++openglglm-mathellipse

Issue in drawing ellipsoid with OpenGL


this is the code I use to create and draw an ellipsoid with OpenGL with shader

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

while this is the code that I use to render it:

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

It seems to work but I have some issue. Looking the image it is possible to see some vertex in the wrong position. I think that is something related to the indicies but I'm not sure. I have notice that depends by the number of stacks or slices that I use

enter image description here

UPDATE:

I take into account the suggestion of @Rabbid76 and this is the result. No more degenerated vertex and triangles in the rendering. However the rendering is not equal to the one of @Rabbid76 there is like a rotation of the vertex.

enter image description here enter image description here

FINAL:

This is the creation vertex and indices code:

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

This is the rendering one:

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

Solution

  • You have confused phi and theta. theta is the angle of the points around the circumference of a slice in range [0, 2*PI]. phi is the angle of the points form the south to the north in range [-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) );
        }
    }
    

    The number of points of a slice (around the circumference) is noPerSlice = slices + 1. The first index of a point of a quad is start_i = (i * noPerSlice) + j, where i is the index of the stack and j the index around the slice. Create slices quads around the circumference and stacks slices form the south to the north:

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