在Opengl中使用Visual c++创建一个3D球体。

时间:2022-09-10 19:05:30

I am not able to create a simple 3D sphere using the OpenGL library function glutSolidSphere() in C++.

我不能使用c++中的OpenGL库函数glutSolidSphere()创建一个简单的3D球体。

Here's what I tried:

这就是我试着:

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

6 个解决方案

#1


71  

In OpenGL you don't create objects, you just draw them. Once they are drawn, OpenGL no longer cares about what geometry you sent it.

在OpenGL中,你不创建对象,你只是绘制它们。一旦它们被绘制出来,OpenGL就不再关心你发送的几何图形了。

glutSolidSphere is just sending drawing commands to OpenGL. However there's nothing special in and about it. And since it's tied to GLUT I'd not use it. Instead, if you really need some sphere in your code, how about create if for yourself?

glutSolidSphere只是向OpenGL发送绘图命令。然而这并没有什么特别之处。因为它和供过于求有关,所以我不会用它。相反,如果您在代码中确实需要一些领域,那么为您自己创建if如何?

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

#2


20  

It doesn't seem like anyone so far has addressed the actual problem with your original code, so I thought I would do that even though the question is quite old at this point.

到目前为止,似乎还没有人解决过您的原始代码的实际问题,所以我想我应该这么做,尽管这个问题已经很老了。

The problem originally had to do with the projection in relation to the radius and position of the sphere. I think you'll find that the problem isn't too complicated. The program actually works correctly, it's just that what is being drawn is very hard to see.

这个问题最初与投影有关,它与球面的半径和位置有关。我想你会发现这个问题并不复杂。这个程序实际上是正确的,只是画出来的东西很难看到。

First, an orthogonal projection was created using the call

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

gluOrtho2D(0.0, 499.0, 0.0, 499.0);

which "is equivalent to calling glOrtho with near = -1 and far = 1." This means that the viewing frustum has a depth of 2. So a sphere with a radius of anything greater than 1 (diameter = 2) will not fit entirely within the viewing frustum.

这相当于将glOrtho与near = -1和far = 1联系在一起。这意味着观测截体的深度为2。因此,一个半径大于1(直径= 2)的球体并不完全符合观测结果。

Then the calls

然后调用

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

are used, which loads the identity matrix of the model-view matrix and then "[r]enders a sphere centered at the modeling coordinates origin of the specified radius." Meaning, the sphere is rendered at the origin, (x, y, z) = (0, 0, 0), and with a radius of 5.

被使用,它加载模型-视图矩阵的单位矩阵,然后“[r]在指定半径的建模坐标原点处放置一个球体。”表示球面在原点处,(x, y, z) =(0,0),半径为5。

Now, the issue is three-fold:

现在,问题有三个方面:

  1. Since the window is 500x500 pixels and the width and height of the viewing frustum is almost 500 (499.0), the small radius of the sphere (5.0) makes its projected area only slightly over one fiftieth (2*5/499) of the size of the window in each dimension. This means that the apparent size of the sphere would be roughly 1/2,500th (actually pi*5^2/499^2, which is closer to about 1/3170th) of the entire window, so it might be difficult to see. This is assuming the entire circle is drawn within the area of the window. It is not, however, as we will see in point 2.
  2. 由于窗口为500x500像素,且视锥体的宽度和高度几乎为500(499.0),因此球体的小半径(5.0)使得其投影面积仅略高于窗口尺寸的五十分之一(2*5/499)。这意味着球的表观尺寸将大致1/2,500th(π* 5 ^ 2/499 ^ 2,这是接近1/3170th)整个窗口,所以它可能很难看到。这是假设整个圆被画在窗口的区域内。然而,这并不是我们将在第2点看到的。
  3. Since the viewing frustum has it's left plane at x = 0 and bottom plane at y = 0, the sphere will be rendered with its geometric center in the very bottom left corner of the window so that only one quadrant of the projected sphere will be visible! This means that what would be seen is even smaller, about 1/10,000th (actually pi*5^2/(4*499^2), which is closer to 1/12,682nd) of the window size. This would make it even more difficult to see. Especially since the sphere is rendered so close to the edges/corner of the screen where you might not think to look.
  4. 由于观测的截体在x = 0处有左平面,在y = 0处有下平面,因此球体将以其几何中心在窗口的左下角呈现,因此只能看到投影球体的四分之一。这意味着将会看到更小,对刀锋(实际上π* 5 ^ 2 /(4 * 499 ^ 2),这是接近1/12,682nd)窗口的大小。这将使它更加难以看到。特别是因为球体被渲染的如此接近屏幕的边缘/角落,你可能不会想到去看。
  5. Since the depth of the viewing frustum is significantly smaller than the diameter of the sphere (less than half), only a sliver of the sphere will be within the viewing frustum, rendering only that part. So you will get more like a hollow circle on the screen than a solid sphere/circle. As it happens, the thickness of that sliver might represent less than 1 pixel on the screen which means we might even see nothing on the screen, even if part of the sphere is indeed within the viewing frustum.
  6. 由于观测截体的深度明显小于球体的直径(小于一半),因此只有一小部分的球体会在观测截体中,只呈现出这一部分。所以你会在屏幕上看到一个空心的圆,而不是一个实心的球体/圆。碰巧的是,这条线的厚度可能在屏幕上表示小于1像素,这意味着我们甚至可能在屏幕上看不到任何东西,即使球体的一部分确实在可视屏幕上。

The solution is simply to change the viewing frustum and radius of the sphere. For instance,

解决的办法就是改变球体的视锥和半径。例如,

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

renders the following image.

如下图呈现。

在Opengl中使用Visual c++创建一个3D球体。

As you can see, only a small part is visible around the "equator", of the sphere with a radius of 5. (I changed the projection to fill the window with the sphere.) Another example,

正如你所看到的,在“赤道”周围只有一小部分是可见的,半径为5。(我改变了投影,用球体填充窗口。)另一个例子,

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

renders the following image.

如下图呈现。

在Opengl中使用Visual c++创建一个3D球体。

The image above shows more of the sphere inside of the viewing frustum, but still the sphere is 0.2 depth units larger than the viewing frustum. As you can see, the "ice caps" of the sphere are missing, both the north and the south. So, if we want the entire sphere to fit within the viewing frustum which has depth 2, we must make the radius less than or equal to 1.

上面的图像显示了更多的球形在查看的水果中,但仍然是球体的0.2深度单位比查看的水果中。正如你所看到的,这个球体的“冰帽”在北方和南方都不见了。所以,如果我们想让整个球面与景深为2的圆锥相吻合,我们必须使半径小于等于1。

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

renders the following image.

如下图呈现。

在Opengl中使用Visual c++创建一个3D球体。

I hope this has helped someone. Take care!

我希望这对某人有所帮助。保重!

#3


8  

I don't understand how can datenwolf`s index generation can be correct. But still I find his solution rather clear. This is what I get after some thinking:

我不明白datenwolf的索引生成怎么会是正确的。但我仍然觉得他的解决方案相当明确。这是我思考后得到的:

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

#4


2  

Here's the code:

这是代码:

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

#5


2  

I like the answer of coin. It's simple to understand and works with triangles. However the indexes of his program are sometimes over the bounds. So I post here his code with two tiny corrections:

我喜欢硬币的答案。理解和处理三角形很简单。然而,他的程序的索引有时超出了界限。所以我把他的代码贴在这里,并做了两个小小的修改:

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

#6


2  

Datanewolf's code is ALMOST right. I had to reverse both the winding and the normals to make it work properly with the fixed pipeline. The below works correctly with cull on or off for me:

Datanewolf的代码几乎是正确的。我必须把卷绕和法线都倒回去,使它在固定的管道上正常工作。下面的工作对我来说是正确的:

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;

}

Edit: There was a question on how to draw this... in my code I encapsulate these values in a G3DModel class. This is my code to setup the frame, draw the model, and end it:

编辑:有个问题是怎么画的……在我的代码中,我将这些值封装在G3DModel类中。这是我的代码设置框架,绘制模型,并结束它:

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


71  

In OpenGL you don't create objects, you just draw them. Once they are drawn, OpenGL no longer cares about what geometry you sent it.

在OpenGL中,你不创建对象,你只是绘制它们。一旦它们被绘制出来,OpenGL就不再关心你发送的几何图形了。

glutSolidSphere is just sending drawing commands to OpenGL. However there's nothing special in and about it. And since it's tied to GLUT I'd not use it. Instead, if you really need some sphere in your code, how about create if for yourself?

glutSolidSphere只是向OpenGL发送绘图命令。然而这并没有什么特别之处。因为它和供过于求有关,所以我不会用它。相反,如果您在代码中确实需要一些领域,那么为您自己创建if如何?

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

#2


20  

It doesn't seem like anyone so far has addressed the actual problem with your original code, so I thought I would do that even though the question is quite old at this point.

到目前为止,似乎还没有人解决过您的原始代码的实际问题,所以我想我应该这么做,尽管这个问题已经很老了。

The problem originally had to do with the projection in relation to the radius and position of the sphere. I think you'll find that the problem isn't too complicated. The program actually works correctly, it's just that what is being drawn is very hard to see.

这个问题最初与投影有关,它与球面的半径和位置有关。我想你会发现这个问题并不复杂。这个程序实际上是正确的,只是画出来的东西很难看到。

First, an orthogonal projection was created using the call

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

gluOrtho2D(0.0, 499.0, 0.0, 499.0);

which "is equivalent to calling glOrtho with near = -1 and far = 1." This means that the viewing frustum has a depth of 2. So a sphere with a radius of anything greater than 1 (diameter = 2) will not fit entirely within the viewing frustum.

这相当于将glOrtho与near = -1和far = 1联系在一起。这意味着观测截体的深度为2。因此,一个半径大于1(直径= 2)的球体并不完全符合观测结果。

Then the calls

然后调用

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

are used, which loads the identity matrix of the model-view matrix and then "[r]enders a sphere centered at the modeling coordinates origin of the specified radius." Meaning, the sphere is rendered at the origin, (x, y, z) = (0, 0, 0), and with a radius of 5.

被使用,它加载模型-视图矩阵的单位矩阵,然后“[r]在指定半径的建模坐标原点处放置一个球体。”表示球面在原点处,(x, y, z) =(0,0),半径为5。

Now, the issue is three-fold:

现在,问题有三个方面:

  1. Since the window is 500x500 pixels and the width and height of the viewing frustum is almost 500 (499.0), the small radius of the sphere (5.0) makes its projected area only slightly over one fiftieth (2*5/499) of the size of the window in each dimension. This means that the apparent size of the sphere would be roughly 1/2,500th (actually pi*5^2/499^2, which is closer to about 1/3170th) of the entire window, so it might be difficult to see. This is assuming the entire circle is drawn within the area of the window. It is not, however, as we will see in point 2.
  2. 由于窗口为500x500像素,且视锥体的宽度和高度几乎为500(499.0),因此球体的小半径(5.0)使得其投影面积仅略高于窗口尺寸的五十分之一(2*5/499)。这意味着球的表观尺寸将大致1/2,500th(π* 5 ^ 2/499 ^ 2,这是接近1/3170th)整个窗口,所以它可能很难看到。这是假设整个圆被画在窗口的区域内。然而,这并不是我们将在第2点看到的。
  3. Since the viewing frustum has it's left plane at x = 0 and bottom plane at y = 0, the sphere will be rendered with its geometric center in the very bottom left corner of the window so that only one quadrant of the projected sphere will be visible! This means that what would be seen is even smaller, about 1/10,000th (actually pi*5^2/(4*499^2), which is closer to 1/12,682nd) of the window size. This would make it even more difficult to see. Especially since the sphere is rendered so close to the edges/corner of the screen where you might not think to look.
  4. 由于观测的截体在x = 0处有左平面,在y = 0处有下平面,因此球体将以其几何中心在窗口的左下角呈现,因此只能看到投影球体的四分之一。这意味着将会看到更小,对刀锋(实际上π* 5 ^ 2 /(4 * 499 ^ 2),这是接近1/12,682nd)窗口的大小。这将使它更加难以看到。特别是因为球体被渲染的如此接近屏幕的边缘/角落,你可能不会想到去看。
  5. Since the depth of the viewing frustum is significantly smaller than the diameter of the sphere (less than half), only a sliver of the sphere will be within the viewing frustum, rendering only that part. So you will get more like a hollow circle on the screen than a solid sphere/circle. As it happens, the thickness of that sliver might represent less than 1 pixel on the screen which means we might even see nothing on the screen, even if part of the sphere is indeed within the viewing frustum.
  6. 由于观测截体的深度明显小于球体的直径(小于一半),因此只有一小部分的球体会在观测截体中,只呈现出这一部分。所以你会在屏幕上看到一个空心的圆,而不是一个实心的球体/圆。碰巧的是,这条线的厚度可能在屏幕上表示小于1像素,这意味着我们甚至可能在屏幕上看不到任何东西,即使球体的一部分确实在可视屏幕上。

The solution is simply to change the viewing frustum and radius of the sphere. For instance,

解决的办法就是改变球体的视锥和半径。例如,

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

renders the following image.

如下图呈现。

在Opengl中使用Visual c++创建一个3D球体。

As you can see, only a small part is visible around the "equator", of the sphere with a radius of 5. (I changed the projection to fill the window with the sphere.) Another example,

正如你所看到的,在“赤道”周围只有一小部分是可见的,半径为5。(我改变了投影,用球体填充窗口。)另一个例子,

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

renders the following image.

如下图呈现。

在Opengl中使用Visual c++创建一个3D球体。

The image above shows more of the sphere inside of the viewing frustum, but still the sphere is 0.2 depth units larger than the viewing frustum. As you can see, the "ice caps" of the sphere are missing, both the north and the south. So, if we want the entire sphere to fit within the viewing frustum which has depth 2, we must make the radius less than or equal to 1.

上面的图像显示了更多的球形在查看的水果中,但仍然是球体的0.2深度单位比查看的水果中。正如你所看到的,这个球体的“冰帽”在北方和南方都不见了。所以,如果我们想让整个球面与景深为2的圆锥相吻合,我们必须使半径小于等于1。

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

renders the following image.

如下图呈现。

在Opengl中使用Visual c++创建一个3D球体。

I hope this has helped someone. Take care!

我希望这对某人有所帮助。保重!

#3


8  

I don't understand how can datenwolf`s index generation can be correct. But still I find his solution rather clear. This is what I get after some thinking:

我不明白datenwolf的索引生成怎么会是正确的。但我仍然觉得他的解决方案相当明确。这是我思考后得到的:

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

#4


2  

Here's the code:

这是代码:

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

#5


2  

I like the answer of coin. It's simple to understand and works with triangles. However the indexes of his program are sometimes over the bounds. So I post here his code with two tiny corrections:

我喜欢硬币的答案。理解和处理三角形很简单。然而,他的程序的索引有时超出了界限。所以我把他的代码贴在这里,并做了两个小小的修改:

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

#6


2  

Datanewolf's code is ALMOST right. I had to reverse both the winding and the normals to make it work properly with the fixed pipeline. The below works correctly with cull on or off for me:

Datanewolf的代码几乎是正确的。我必须把卷绕和法线都倒回去,使它在固定的管道上正常工作。下面的工作对我来说是正确的:

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;

}

Edit: There was a question on how to draw this... in my code I encapsulate these values in a G3DModel class. This is my code to setup the frame, draw the model, and end it:

编辑:有个问题是怎么画的……在我的代码中,我将这些值封装在G3DModel类中。这是我的代码设置框架,绘制模型,并结束它:

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