使用Visual C ++在Opengl中创建3D球体

 2020-09-19 

Creating a 3D sphere in Opengl using Visual C++

我无法使用C ++中的OpenGL库函数glutSolidSphere()创建简单的3D球体。

这是我尝试过的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<GL/glu.h>
void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0,0.0,0.0);
    glLoadIdentity();
    glutSolidSphere( 5.0, 20.0, 20.0);
    glFlush();
}

void myInit()
{
    glClearColor(1.0,1.0,1.0,1.0);
    glColor3f(1.0,0.0,0.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0,499.0,0.0,499.0);
    glMatrixMode(GL_MODELVIEW);
}

void main(int argc,char **argv)
{
    qobj = gluNewQuadric();
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
    glutInitWindowSize(500,500);
    glutCreateWindow("pendulum");        
    glutDisplayFunc(display);
    myInit();
    glutMainLoop();
}


在OpenGL中,您无需创建对象,只需绘制它们即可。 绘制之后,OpenGL将不再关心您发送的几何图形。

glutSolidSphere只是将绘图命令发送到OpenGL。 但是,它并没有什么特别的。 而且由于它与GLUT相关,所以我不会使用它。 相反,如果您确实需要代码中的一些功能,那么如果自己创建,该怎么办?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#define _USE_MATH_DEFINES
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include <cmath>

// your framework of choice here

class SolidSphere
{
protected:
    std::vector<GLfloat> vertices;
    std::vector<GLfloat> normals;
    std::vector<GLfloat> texcoords;
    std::vector<GLushort> indices;

public:
    SolidSphere(float radius, unsigned int rings, unsigned int sectors)
    {
        float const R = 1./(float)(rings-1);
        float const S = 1./(float)(sectors-1);
        int r, s;

        vertices.resize(rings * sectors * 3);
        normals.resize(rings * sectors * 3);
        texcoords.resize(rings * sectors * 2);
        std::vector<GLfloat>::iterator v = vertices.begin();
        std::vector<GLfloat>::iterator n = normals.begin();
        std::vector<GLfloat>::iterator t = texcoords.begin();
        for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
                float const y = sin( -M_PI_2 + M_PI * r * R );
                float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
                float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

                *t++ = s*S;
                *t++ = r*R;

                *v++ = x * radius;
                *v++ = y * radius;
                *v++ = z * radius;

                *n++ = x;
                *n++ = y;
                *n++ = z;
        }

        indices.resize(rings * sectors * 4);
        std::vector<GLushort>::iterator i = indices.begin();
        for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
                *i++ = r * sectors + s;
                *i++ = r * sectors + (s+1);
                *i++ = (r+1) * sectors + (s+1);
                *i++ = (r+1) * sectors + s;
        }
    }

    void draw(GLfloat x, GLfloat y, GLfloat z)
    {
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glTranslatef(x,y,z);

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
        glNormalPointer(GL_FLOAT, 0, &normals[0]);
        glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
        glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
        glPopMatrix();
    }
};

SolidSphere sphere(1, 12, 24);

void display()
{
    int const win_width  =; // retrieve window dimensions from
    int const win_height =; // framework of choice here
    float const win_aspect = (float)win_width / (float)win_height;

    glViewport(0, 0, win_width, win_height);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, win_aspect, 1, 10);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

#ifdef DRAW_WIREFRAME
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
    sphere.draw(0, 0, -5);

    swapBuffers();
}

int main(int argc, char *argv[])
{
    // initialize and register your framework of choice here
    return 0;
}


到目前为止,似乎还没有人解决您的原始代码中的实际问题,所以即使现在这个问题已经很久了,我也想这样做。

该问题最初与与球体的半径和位置有关的投影有关。我认为您会发现问题并不太复杂。该程序实际上可以正常运行,只是所绘制的内容很难看到。

首先,使用调用创建了正交投影

1
gluOrtho2D(0.0, 499.0, 0.0, 499.0);

这"相当于用近= -1和远= 1调用glOrtho"。这意味着观察视锥的深度为2。因此,半径大于1(直径= 2)的球体将无法完全容纳在视锥中。

然后打电话

1
2
glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);

使用加载模型视图矩阵的单位矩阵,然后"渲染以指定半径的建模坐标原点为中心的球"。意思是,球体是在原点(x,y,z)=(0,0,0)处渲染的,半径为5。

现在,问题是三个方面:

  • 由于窗口为500x500像素,并且视锥的宽度和高度几乎为500(499.0),因此球体的小半径(5.0)使其投影区域仅略大于大小的五十分之一(2 * 5/499)每个维度中的窗口大小。这意味着球体的视在大小约为整个窗口的1 / 2,500(实际上是pi*5^2/499^2,大约是1/3170),因此可能很难看到。假设整个圆都绘制在窗口区域内。但是,正如我们在第2点中看到的那样。
  • 由于查看的视锥面的左平面为x = 0,底平面为y = 0,因此将以其几何中心位于窗口左下角的方式渲染该球体,以便仅看到投影球体的一个象限!这意味着所看到的甚至更小,约为窗口大小的1 / 10,000(实际上是pi*5^2/(4*499^2),更接近1 / 12,682nd)。这将使它更加难以看清。尤其是因为球体渲染得如此之靠近屏幕的边缘/角落,您可能不会想到它。
  • 由于观察视锥的深度显着小于球体的直径(小于一半),因此球体的一小部分将位于视视锥内,仅呈现该部分。因此,与实心球/圆相比,您在屏幕上会更像是空心圆。碰巧的是,该条的厚度在屏幕上可能不足1个像素,这意味着即使球体的一部分确实位于视锥中,我们也可能在屏幕上看不到任何东西。
  • 解决方案只是更改视角的视锥和球体的半径。例如,

    1
    2
    gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
    glutSolidSphere(5.0, 20, 20);

    呈现以下图像。

    r = 5.0

    如您所见,半径为5的球的"赤道"周围只有一小部分可见。(我更改了投影以将球填充到窗口中。)另一个示例,

    1
    2
    gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
    glutSolidSphere(1.1, 20, 20);

    呈现以下图像。

    r = 1.1

    上图显示了视锥内部的更多球体,但该球仍比视锥中大0.2个深度单位。如您所见,球体的"冰盖"不见了,无论是北部还是南部。因此,如果我们希望整个球体都适合于深度为2的视锥中,则必须使半径小于或等于1。

    1
    2
    gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
    glutSolidSphere(1.0, 20, 20);

    呈现以下图像。

    r = 1.0

    我希望这对某人有所帮助。照顾自己!


    我不知道datenwolf的索引生成如何正确。 但我仍然发现他的解决方案相当明确。 这是我经过一番思考后得到的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
        int curRow = r * sectors;
        int nextRow = (r+1) * sectors;

        indices.push_back(curRow + s);
        indices.push_back(nextRow + s);
        indices.push_back(nextRow + (s+1));

        indices.push_back(curRow + s);
        indices.push_back(nextRow + (s+1));
        indices.push_back(curRow + (s+1));
    }

    void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
                 float radius, unsigned int rings, unsigned int sectors)
    {
        float const R = 1./(float)(rings-1);
        float const S = 1./(float)(sectors-1);

        for(int r = 0; r < rings; ++r) {
            for(int s = 0; s < sectors; ++s) {
                float const y = sin( -M_PI_2 + M_PI * r * R );
                float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
                float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

                texcoords.push_back(vec2(s*S, r*R));
                vertices.push_back(vec3(x,y,z) * radius);
                push_indices(indices, sectors, r, s);
            }
        }
    }


    我喜欢硬币的答案。 这很容易理解,并且可以使用三角形。 但是,他的程序的索引有时会超出范围。 因此,我在这里发布了他的代码,做了两个小小的更正:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
        int curRow = r * sectors;
        int nextRow = (r+1) * sectors;
        int nextS = (s+1) % sectors;

        indices.push_back(curRow + s);
        indices.push_back(nextRow + s);
        indices.push_back(nextRow + nextS);

        indices.push_back(curRow + s);
        indices.push_back(nextRow + nextS);
        indices.push_back(curRow + nextS);
    }

    void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
                      float radius, unsigned int rings, unsigned int sectors)
    {
        float const R = 1./(float)(rings-1);
        float const S = 1./(float)(sectors-1);

        for(int r = 0; r < rings; ++r) {
            for(int s = 0; s < sectors; ++s) {
                float const y = sin( -M_PI_2 + M_PI * r * R );
                float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
                float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

                texcoords.push_back(vec2(s*S, r*R));
                vertices.push_back(vec3(x,y,z) * radius);
                if(r < rings-1)
                    push_indices(indices, sectors, r, s);
            }
        }
    }


    Datanewolf的代码几乎是正确的。 我必须扭转绕组和法线,以使其在固定管道上正常工作。 以下内容对我来说启用或禁用cull都可以正常工作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    std::vector<GLfloat> vertices;
    std::vector<GLfloat> normals;
    std::vector<GLfloat> texcoords;
    std::vector<GLushort> indices;

    float const R = 1./(float)(rings-1);
    float const S = 1./(float)(sectors-1);
    int r, s;

    vertices.resize(rings * sectors * 3);
    normals.resize(rings * sectors * 3);
    texcoords.resize(rings * sectors * 2);
    std::vector<GLfloat>::iterator v = vertices.begin();
    std::vector<GLfloat>::iterator n = normals.begin();
    std::vector<GLfloat>::iterator t = texcoords.begin();
    for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
        float const y = sin( -M_PI_2 + M_PI * r * R );
        float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
        float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

        *t++ = s*S;
        *t++ = r*R;

        *v++ = x * radius;
        *v++ = y * radius;
        *v++ = z * radius;

        *n++ = -x;
        *n++ = -y;
        *n++ = -z;
    }

    indices.resize(rings * sectors * 4);
    std::vector<GLushort>::iterator i = indices.begin();
    for(r = 0; r < rings-1; r++)
        for(s = 0; s < sectors-1; s++) {
           /*
            *i++ = r * sectors + s;
            *i++ = r * sectors + (s+1);
            *i++ = (r+1) * sectors + (s+1);
            *i++ = (r+1) * sectors + s;
            */

             *i++ = (r+1) * sectors + s;
             *i++ = (r+1) * sectors + (s+1);
            *i++ = r * sectors + (s+1);
             *i++ = r * sectors + s;

    }

    编辑:关于如何绘制此问题...在我的代码中,我将这些值封装在G3DModel类中。 这是我的代码来设置框架,绘制模型并结束它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    void GraphicsProvider3DPriv::BeginFrame()const{
            int win_width;
            int win_height;// framework of choice here
            glfwGetWindowSize(window, &win_width, &win_height); // retrieve window
            float const win_aspect = (float)win_width / (float)win_height;
            // set lighting
            glEnable(GL_LIGHTING);
            glEnable(GL_LIGHT0);
            glEnable(GL_DEPTH_TEST);
            GLfloat lightpos[] = {0, 0.0, 0, 0.};
            glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
            GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
            glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
            glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
            // set up world transform
            glClearColor(0.f, 0.f, 0.f, 1.f);
            glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT|GL_ACCUM_BUFFER_BIT);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();

            gluPerspective(45, win_aspect, 1, 10);

            glMatrixMode(GL_MODELVIEW);

        }


        void GraphicsProvider3DPriv::DrawModel(const G3DModel* model, const Transform3D transform)const{
            G3DModelPriv* privModel = (G3DModelPriv *)model;
            glPushMatrix();
            glLoadMatrixf(transform.GetOGLData());

            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_NORMAL_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);

            glVertexPointer(3, GL_FLOAT, 0, &privModel->vertices[0]);
            glNormalPointer(GL_FLOAT, 0, &privModel->normals[0]);
            glTexCoordPointer(2, GL_FLOAT, 0, &privModel->texcoords[0]);

            glEnable(GL_TEXTURE_2D);
            //glFrontFace(GL_CCW);
            glEnable(GL_CULL_FACE);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, privModel->texname);

            glDrawElements(GL_QUADS, privModel->indices.size(), GL_UNSIGNED_SHORT, &privModel->indices[0]);
            glPopMatrix();
            glDisable(GL_TEXTURE_2D);

        }

        void GraphicsProvider3DPriv::EndFrame()const{
            /* Swap front and back buffers */
            glDisable(GL_LIGHTING);
            glDisable(GL_LIGHT0);
            glDisable(GL_CULL_FACE);
            glfwSwapBuffers(window);

            /* Poll for and process events */
            glfwPollEvents();
        }


    这是代码:

    1
    2
    3
    4
    5
    6
    glPushMatrix();
    glTranslatef(18,2,0);
    glRotatef(angle, 0, 0, 0.7);
    glColor3ub(0,255,255);
    glutWireSphere(3,10,10);
    glPopMatrix();