如何使用OpenGL和C++渲染球体,而不使用Glut/Glu?

3

我在OpenGL和C++技术上都比较新手,正在开发一个自定义框架,其中需要渲染球体。我进行了调研,并成功生成了由点组成的“球体”。如果可以的话,我想重新生成一个由三角形构成、具有半径等属性的真正球体。我使用的代码生成的点球体如下:

Mesh* Mesh::GenerateSphere()
{

    const int Y_SEGMENT= 10;
    const int X_SEGMENT = 20;
    //const float count;


    Mesh*m = new Mesh();
    m->numVertices = (X_SEGMENT+1)*(Y_SEGMENT+1);
    m->type = GL_POINTS;
    m->vertices = new Vector3[m->numVertices];

    //s->GenerateTriangle();
    for (int i = 0; i < Y_SEGMENT+1;i++)
    {
        float angleY = PI*i / Y_SEGMENT;
        float y = cos(angleY);
        float xz = sin(angleY);

        for (int j = 0; j < X_SEGMENT+1; j++)
        {

            float angleX = 2*PI*j / X_SEGMENT;
            float x = xz*cos(angleX);
            float z = xz*sin(angleX);
            Vector3 v(x,y,z);
            m->vertices[i * (X_SEGMENT+1)+j] = v;
        }
    }
    m->BufferData();
    return m;
}

1
你可以通过细分二十面体来获得一个更加“平滑”的球形。 (不过,一个真正的球体不是由三角形构成的。) - molbdnilo
这是在freeglut项目中实现球体的代码:http://sourceforge.net/p/freeglut/code/HEAD/tree/trunk/freeglut/freeglut/src/fg_geometry.c#l8 - Lucian
由于框架的布局,我无法使用glut。 - Jim Rod
我不认为这是一个重复的问题,因为该帖子的答案依赖于使用 glu/glut,而我无法使用它们。 - Jim Rod
关于渲染球体的更多问题/答案:http://stackoverflow.com/q/24137198/3530129,https://dev59.com/sITca4cB1Zd3GeqPBfXb。 - Reto Koradi
显示剩余2条评论
2个回答

1

这是我很久以前写的一些代码。我用三角形扇绘制了极点周围的区域,用四边形带绘制了球体的其余部分。当然,您也可以使用三角形代替四边形,但由于成对的三角形仍然是平面的,除非我弄错了,否则看起来不会有任何不同 - 这已经是很长时间没有接触过GL了。

正如molbdnilo所指出的那样,通过以不同的方式计算您的点,您将获得更好的球体。如果打算为球体贴图,则如果您对立方体进行细分和平滑处理,您将再次获得更好的结果,因为这避免了在极点周围的“捏合”现象。 这是一篇很好的文章,讨论了这个问题: http://www.iquilezles.org/www/articles/patchedsphere/patchedsphere.htm

我还要指出一个问题,就是在计算法向量或者旋转变换时,我可能存在一些问题——当球体旋转时,我会得到一些奇怪的光照结果。(我认为问题出在法向量上) 另外,现在看代码,我不确定我是否正确计算了所需顶点的数量——你需要仔细检查一下。看起来似乎我没有在数组中存储两个极点的顶点。 编辑: 这是输出的图片:

enter image description here

typedef struct {
    GLfloat x, y, z;
}vec3;

void myGlutBall(float radius, int numStacks, int numSides)
{
//    vec3 points[sides * (sides-1)];
    GLfloat curRadius, curTheta, curRho, deltaTheta, deltaRho, curX,curY,curZ;
    int curStack, curSlice, numVerts = (numStacks-1)*numSides;
    vec3 points[numVerts];
    int curVert = 0;
    int t;

    deltaTheta = (2*M_PI) / numSides;
    deltaRho = M_PI / numStacks;

        for (curStack=1; curStack<numStacks; curStack++)
        {
            curRho = (3.141/2.0) - curStack*deltaRho;
            curY = sin(curRho) * radius;
            curRadius = cos(curRho) * radius;
            for (curSlice=0; curSlice<numSides; curSlice++)
            {
                curTheta = curSlice * deltaTheta;
                curX = curRadius * cos(curTheta);
                curZ = -curRadius * sin(curTheta);
                points[curVert++] = vec3{curX,curY,curZ};
            }
        }

    // option 1 - points only
    /*
    glBegin(GL_POINTS);
    glNormal3d(0,1,0);
    glVertex3d(0,radius,0);
    for (t=0; t<numVerts; t++)
    {
        curX = points[t].x;
        curY = points[t].y;
        curZ = points[t].z;
        glNormal3d(curX, curY, curZ);
        glVertex3d(curX, curY, curZ);
    }
    glNormal3d(0,-1,0);
    glVertex3d(0,-radius,0);
    glEnd();
    */

    ///////////////////////////////
    // option 2 - solid
    ///////////////////////////////
    // part A - draw the top 'lid' (tris)
    glBegin(GL_TRIANGLE_FAN);
        glNormal3d(0,1,0);
        glVertex3d(0,radius,0);
        for (t=0; t<numSides; t++)
        {
            curX = points[t].x;
            curY = points[t].y;
            curZ = points[t].z;
            glNormal3d(curX, curY, curZ);
            glVertex3d(curX, curY, curZ);
        }
            curX = points[0].x;
            curY = points[0].y;
            curZ = points[0].z;
        glNormal3d(curX, curY, curZ);
        glVertex3d(curX, curY, curZ);
    glEnd();

    // part B - draw the 'sides' (quads)
    int vertIndex;
    for (curStack=0; curStack<numStacks-2; curStack++)
    {
        vertIndex = curStack * numSides;
        glBegin(GL_QUAD_STRIP);
            for (curSlice=0; curSlice<numSides; curSlice++)
            {
                glNormal3d(points[vertIndex+curSlice].x, points[vertIndex+curSlice].y, points[vertIndex+curSlice].z);
                glVertex3d(points[vertIndex+curSlice].x, points[vertIndex+curSlice].y, points[vertIndex+curSlice].z);

                glNormal3d(points[vertIndex+numSides+curSlice].x, points[vertIndex+numSides+curSlice].y, points[vertIndex+numSides+curSlice].z);
                glVertex3d(points[vertIndex+numSides+curSlice].x, points[vertIndex+numSides+curSlice].y, points[vertIndex+numSides+curSlice].z);
            }
            glNormal3d(points[vertIndex].x, points[vertIndex].y, points[vertIndex].z);
            glVertex3d(points[vertIndex].x, points[vertIndex].y, points[vertIndex].z);
            glNormal3d(points[vertIndex+numSides].x, points[vertIndex+numSides].y, points[vertIndex+numSides].z);
            glVertex3d(points[vertIndex+numSides].x, points[vertIndex+numSides].y, points[vertIndex+numSides].z);
        glEnd();
    }

    // part C - draw the bottom 'lid' (tris)
    glBegin(GL_TRIANGLE_FAN);
        glNormal3d(0,-1,0);
        glVertex3d(0,-radius,0);
        for (t=0; t<numSides-1; t++)
        {
            curX = points[numVerts-1-t].x;
            curY = points[numVerts-1-t].y;
            curZ = points[numVerts-1-t].z;
            glNormal3d(curX, curY, curZ);
            glVertex3d(curX, curY, curZ);
        }
            curX = points[numVerts-1].x;
            curY = points[numVerts-1].y;
            curZ = points[numVerts-1].z;
        glNormal3d(curX, curY, curZ);
        glVertex3d(curX, curY, curZ);
    glEnd();

}

1
在我的渲染课程中,我学到了将球体想象成一个圆形网格。首先,您需要采用维度为xy、位于位置(0,0,0)的尺寸为1的网格实现,该网格将被n行(rowMax)和m列(colMax)细分:
// Aux function
inline int index (int i, int j)
{
    return i + j*(m_colMax + 1);
}

float numCoords = 3*rowMax*colMax; // Array size

float *coordData = new float[numCoords]; // Array with coordinate positions

// Fill coordinate positions [to change]
for (int j = 0; j <= rowMax; j++) {
    for (int i = 0; i <= colMax; i++) {
        int k = index(i, j);
        coordData[k] = (float)i / m_colMax - (0.5f);
        coordData[k + 1] = (float)j / m_rowMax - (0.5f);
        coordData[k + 2] = 0;
    }
}

// Fill index
int k = 0;
GLuint *indexData = new GLuint[numCoords]; // Array with indexing data 
for (int j = 0; j < rowMax; j++) {
    for (int i = 0; i < colMax; i++) {
        indexData[k++] = index (i, j);
        indexData[k++] = index (i + 1, j + 1);
        indexData[k++] = index (i, j + 1);
        indexData[k++] = index (i, j);
        indexData[k++] = index (i + 1, j);
        indexData[k++] = index (i + 1, j + 1);          
    }
}

使用这些数据时,请记住使用DrawElements()GL_TRIANGLES以考虑索引(第二个链接有清晰的示意图)。由于您是OpenGL新手,这两个链接可以总结您需要学习的内容:
[1] 使用OGL 2.1而不使用着色器:http://www.songho.ca/opengl/gl_vertexarray.html [2] 使用更高级的技术(即,使用核心/兼容性配置文件的OGL 3.3+)http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/ 现在,为了完成你所需的功能,只需更改第一个for循环中的代码即可。
// Fill coordinate positions 
// Pi variable can be a define or declared somewhere in your code
float radius = 1.0f;
for (int j = 0; j <= rowMax; j++) {
    for (int i = 0; i <= colMax; i++) {
        int k = index(i, j);
        float teta = ((float)i / m_colMax) * 2 * (float)Pi;
        float fi = ((float)j / m_rowMax)*(float)Pi;
        coordData[k] = radius*(cos (teta))*(sin (fi));
        coordData[k + 1] = radius*(cos (fi));
        coordData[k + 2] = 1.0f*(sin (teta))*(sin (fi));
    }
}

你将拥有带索引的球体坐标。使用for循环在旧版OpenGL(2.1或兼容性配置文件)上绘制它,或在新版OpenGL(核心配置文件)上设置缓冲区(VAO,VBO)。

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