在OpenGL Java中为球体对象生成三角网格索引

6

请问有人可以解释一下三角网格的索引是如何生成的吗?

该程序为球体对象生成了顶点数组,并生成了用于使用glDrawElements方法绘制的索引。

我不明白这些索引与堆栈和切片有什么关系。

我是OpenGL的新手,花了很多时间试图理解这段代码的工作原理。

提前感谢您的帮助。:)

    int stacks = 40, slices = 40; // stacks = no. of Latitude lines,
                                  // slices = no. of Longitude lines

    double deltaLong = PI * 2 / slices;
    double deltaLat = PI / stacks;

    // Generate vertices coordinates, normal values, and texture coordinates
    numVertices = (slices + 1) * (stacks - 1) + 2;
    vertices = new float[numVertices * 3];
    normals = new float[numVertices * 3];
    textures = new float[numVertices * 2];

    // North pole point
    normals[0] = 0; normals[1] = 0; normals[2] = 1;
    vertices[0] = 0; vertices[1] = 0; vertices[2] = radius;
    textures[0] = 0.5f; textures[1] = 1.0f;

        k = 1;
    // vertices on the main body
    for (i = 1; i < stacks; i++) {
        for (j = 0; j <= slices; j++) {
            normals[3 * k] = sin(deltaLat * i) * cos(deltaLong * j);
            normals[3 * k + 1] = sin(deltaLat * i) * sin(deltaLong * j);
            normals[3 * k + 2] = cos(deltaLat * i);
            vertices[3 * k] = radius * normals[3 * k];
            vertices[3 * k + 1] = radius * normals[3 * k + 1];
            vertices[3 * k + 2] = radius * normals[3 * k + 2];
            textures[2 * k] = (float) j / slices;
            textures[2 * k + 1] = 1 - (float) i / stacks;
            k++;
        }
    }    

    // South pole point
    normals[3 * k] = 0;normals[3 * k + 1] = 0;normals[3 * k + 2] = -1;
    vertices[3 * k] = 0;vertices[3 * k + 1] = 0;vertices[3 * k + 2] = -radius;
    textures[2 * k] = 0.5f; textures[2 * k + 1] = 0.0f; k++;


    //The above code is to generate vertices array and I think I understand how it works. 
    //**********************************
    // Below is what I don't understand
    int numIndices = (stacks - 1) * slices * 6; //why multiply by 6?
    int[] indices = new int[numIndices];

    int k = 0;

    //add indices in North Pole region (no. of elements is slices * 3)
    // WHY 3 times thenumber of slices?
    for (int j = 1; j<= slices; j++){
      indices[k++] = 0;
      indices[k++] = j; 
      indices[k++] = j+1;
     }

    //add indices in South Pole Region (no. of element is slices * 3)
    int temp = numVertices  - 1;
    for (int j  = temp-1; j > temp - slices - 1; j--){
    indices [k++] = temp;
    indices [k++] = j; 
    indices [k++] = j - 1;
    }

    // add body (no. of element is (stacks - 2) * slices * 6
     for (i = 1; i < stacks - 1; i++) {
        for (j = 1; j <= slices; j++) {
            // each quad gives two triangles
            // triangle one
            indices[k++] = (i - 1) * slices + j;
            indices[k++] = i * slices + j;
            indices[k++] = i * slices + j + 1;
            // triangle two
            indices[k++] = (i - 1) * slices + j;
            indices[k++] = i * slices + j + 1;
            indices[k++] = (i - 1) * slices + j + 1;
        }
    }

圆柱体

我终于理解了我展示的球体代码中的索引如何工作,并且找到了如何制作上面图片中显示的圆柱体。


这里有一个关于使用索引的好教程,网址是http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/。我建议你去看一下。 - sprinter
1个回答

3

您可以创建一个包含所有顶点的顶点数组,然后执行如下函数(抱歉用C语言示例,没有Java示例(将比C语言简单得多,使用泛型容器))。该算法允许您索引任意顶点数组以优化性能和内存消耗(非常有用),并解释了顶点索引的工作原理。

// 1254 Verticies
// 2141 Texture Coordinates
// 1227 Normals
// 2248 Triangles

static short face_indicies[2248][9] = {
// Object #-1
    {0,15,14 ,0,1,2 ,0,1,2 }, {0,1,15 ,0,3,1 ,0,3,1 }, {1,16,15 ,3,4,1 ,3,4,1 },
    {1,2,16 ,3,5,4 ,3,5,4 }, {2,17,16 ,5,6,4 ,5,6,4 }, {2,3,17 ,5,7,6 ,5,7,6 },
    {3,18,17 ,7,8,6 ,7,8,6 }, {3,4,18 ,7,9,8 ,7,9,8 }, {4,19,18 ,9,10,8 ,9,10,8 },
    //.................................................................
};

static GLfloat vertices [1254][3] = {
{1.32715f,-1.99755f,-0.614826f},{1.32715f,-2.20819f,-0.343913f},{1.32715f,-2.5155f,-0.191263f},
{1.32715f,-2.85867f,-0.187049f},{1.32715f,-3.16964f,-0.332104f},{1.32715f,-3.38686f,-0.597763f},
{1.32715f,-3.46734f,-0.931359f},{1.32715f,-3.39508f,-1.26683f},{1.32715f,-3.18445f,-1.53774f},
    //..................................................................
};

static GLfloat normals [1227][3] = {
{-0.45634f,0.376195f,-0.80637f},{0.456348f,0.688811f,-0.563281f},{0.45634f,0.376194f,-0.80637f},
{-0.456348f,0.688811f,-0.563281f},{0.456341f,0.865005f,-0.208615f},{-0.456341f,0.865005f,-0.208615f},
{0.456341f,0.869868f,0.187303f},{-0.456341f,0.869868f,0.187303f},{0.456349f,0.702436f,0.546196f},
    //..................................................................
};

static GLfloat textures [2141][2] = {
{0.94929f,0.497934f},{0.99452f,0.477509f},{0.994669f,0.497506f},
{0.949142f,0.47796f},{0.994339f,0.457508f},{0.948961f,0.457992f},
};

////////////////////////////////////////////////////////////////
// These are hard coded for this particular example
GLushort uiIndexes[2248*3];   // Maximum number of indexes
GLfloat vVerts[2248*3][3];  // (Worst case scenario)
GLfloat vText[2248*3][2];
GLfloat vNorms[2248*3][3];
int iLastIndex = 0;         // Number of indexes actually used



/////////////////////////////////////////////////////////////////
// Compare two floating point values and return true if they are
// close enough together to be considered the same.
int IsSame(float x, float y, float epsilon)
    {
    if(fabs(x-y) < epsilon)
        return 1;

    return 0;
    }


///////////////////////////////////////////////////////////////
// Goes through the arrays and looks for duplicate verticies 
// that can be shared. This expands the original array somewhat
// and returns the number of true unique verticies that now
// populates the vVerts array.
int IndexTriangles(void)
    {
    int iFace, iPoint, iMatch;
    float e = 0.000001; // How small a difference to equate

    // LOOP THROUGH all the faces
    int iIndexCount = 0;
    for(iFace = 0; iFace < 2248; iFace++)
        {
        for(iPoint = 0; iPoint < 3; iPoint++)
            {
            // Search for match
            for(iMatch = 0; iMatch < iLastIndex; iMatch++)
                {
                // If Vertex is the same...
                if(IsSame(vertices[face_indicies[iFace][iPoint]][0], vVerts[iMatch][0], e) &&
                   IsSame(vertices[face_indicies[iFace][iPoint]][1], vVerts[iMatch][1], e) &&
                   IsSame(vertices[face_indicies[iFace][iPoint]][2], vVerts[iMatch][2], e) &&

                   // AND the Normal is the same...
                   IsSame(normals[face_indicies[iFace][iPoint+3]][0], vNorms[iMatch][0], e) &&
                   IsSame(normals[face_indicies[iFace][iPoint+3]][1], vNorms[iMatch][1], e) &&
                   IsSame(normals[face_indicies[iFace][iPoint+3]][2], vNorms[iMatch][2], e) &&

                   // And Texture is the same...
                   IsSame(textures[face_indicies[iFace][iPoint+6]][0], vText[iMatch][0], e) &&
                   IsSame(textures[face_indicies[iFace][iPoint+6]][1], vText[iMatch][1], e))
                    {
                    // Then add the index only
                    uiIndexes[iIndexCount] = iMatch;
                    iIndexCount++;
                    break;
                    }
                }

            // No match found, add this vertex to the end of our list, and update the index array
            if(iMatch == iLastIndex)
                {
                // Add data and new index
                memcpy(vVerts[iMatch], vertices[face_indicies[iFace][iPoint]], sizeof(float) * 3);
                memcpy(vNorms[iMatch], normals[face_indicies[iFace][iPoint+3]], sizeof(float) * 3);
                memcpy(vText[iMatch],  textures[face_indicies[iFace][iPoint+6]], sizeof(float) * 2);
                uiIndexes[iIndexCount] = iLastIndex;
                iIndexCount++;
                iLastIndex++;
                }
            }
        }
    return iIndexCount;
     }

/////////////////////////////////////////////
// Function to stitch the triangles together
// and draw the ship
void DrawModel(void)
    {
    static int iIndexes = 0;
    char cBuffer[32];


    // The first time this is called, reindex the triangles. Report the results
    // in the window title
    if(iIndexes == 0)
        {
        iIndexes = IndexTriangles();
        sprintf(cBuffer,"Verts = %d Indexes = %d", iLastIndex, iIndexes);
        glutSetWindowTitle(cBuffer); 
        }

    // Use vertices, normals, and texture coordinates
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    // Here's where the data is now
    glVertexPointer(3, GL_FLOAT,0, vVerts);
    glNormalPointer(GL_FLOAT, 0, vNorms);
    glTexCoordPointer(2, GL_FLOAT, 0, vText);

    // Draw them
    glDrawElements(GL_TRIANGLES, iIndexes, GL_UNSIGNED_SHORT, uiIndexes);
    } 

在您的情况下很简单。
slice    slice + 1
*--------*
|\       | stack
| \      |
|  \     |
|   \    |
|    \   |
|     \  |
|      \ | stack + 1
*------- *

我们希望将堆栈-切片2D坐标线性化为单个1D整体切片坐标。标准算法(1个堆栈包含n个切片)使我们的算法简单:current_stack * n + current_slice,这将给我们在早期创建的数组中特定顶点的法线和纹理索引。然后在您的循环中,我们简单地对它们进行排列,以指定适当的多边形缠绕。

我已经修改了您的代码,现在它以正确的方式绘制圆柱体。

      int numVertices = (10000); 
      float * vertices = new float[numVertices * 3]; 
      float* normals = new float[numVertices * 3]; 
      //float* textures = new float[numVertices * 2]; 

      // vertices body 
      k = 0; 
      for (i = 0; i < slices; i++) { 
         normals[3 * k] = cos(theta * i); 
         normals[3 * k + 1] = sin(theta * i); 
         normals[3 * k + 2] = 0; 

         vertices[3 * k] = radius * normals[3 * k]; 
         vertices[3 * k + 1] = radius * normals[3 * k + 1]; 
         vertices[3 * k + 2] = .5f; 
         k++; 
      } // end of for vertices on body 

      for (i = 0; i < slices; i++) { 
         normals[3 * k] = cos(theta * i); 
         normals[3 * k + 1] = sin(theta * i); 
         normals[3 * k + 2] = 0; 

         vertices[3 * k] = radius * normals[3 * k]; 
         vertices[3 * k + 1] = radius * normals[3 * k + 1]; 
         vertices[3 * k + 2] = -.5f; 

         k++; 
      } // end of for vertices on body 

     // Generate indices for triangular mesh 
     int numIndices = 100000; 
     unsigned int* indices = new unsigned int[numIndices]; 

     k = 0; 

     for (i = 0; i < slices; ++i) { 
          int i1 = i; 
          int i2 = (i1 + 1) % slices; 
          int i3 = i1 + slices; 
          int i4 = i2 + slices; 

          indices[k++] = i1; 
          indices[k++] = i3; 
          indices[k++] = i2; 

          indices[k++] = i4; 
          indices[k++] = i2; 
          indices[k++] = i3; 
     }

1
@user2070333:我的回答对你有用吗? - Mykola
@user2070333:它计算唯一顶点的总数。堆栈*堆栈+2个杆。在切片中+/-,堆栈是用于正确值计算的校正值。(这类似于面积计算)。 - Mykola
你能解释一下我如何以与球体顶点相同的方式索引圆柱体的顶点吗?我已经尝试过了,但不知道为什么它不能正常工作。 - user2070333
那是为了一个球体伙计。多亏了你,我已经弄清楚了索引的工作原理。 现在我正在尝试弄清楚一个没有盖子的圆柱体,当我添加了索引时,我得到了上面的图像。 - user2070333
@user2070333:这是纹理坐标,用于纹理包裹目的。 - Mykola
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接