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

Llo*_*oyd 27 c++ opengl

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

这是我试过的:

#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(); 
}
Run Code Online (Sandbox Code Playgroud)

dat*_*olf 77

在OpenGL中,您不创建对象,只需绘制它们即可.绘制完成后,OpenGL不再关心您发送的几何体.

glutSolidSphere只是将绘图命令发送到OpenGL.然而,它并没有什么特别之处.因为它与GLUT联系在一起我不会使用它.相反,如果你的代码中确实需要一些球体,那么如何为自己创造呢?

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • @toeplitz:好的,我解决了所有问题. (2认同)

Vic*_*ian 21

到目前为止,似乎任何人都没有解决原始代码的实际问题,所以我想我会这样做,即使问题在这一点上已经很老了.

问题最初与投影相关的球体半径和位置有关.我想你会发现问题不是太复杂.该程序实际上正常工作,只是被绘制的东西很难看到.

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

gluOrtho2D(0.0, 499.0, 0.0, 499.0);
Run Code Online (Sandbox Code Playgroud)

" 相当于用near = -1和far = 1调用glOrtho. "这意味着视锥体的深度为2.因此,半径大于1(直径= 2)的球体将不完全适合在视锥体内.

然后是电话

glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
Run Code Online (Sandbox Code Playgroud)

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

现在,问题是三方面的:

  1. 由于窗口为500x500像素,并且视锥体的宽度和高度几乎为500(499.0),球体的小半径(5.0)使其投影面积仅略大于该尺寸的五十(2*5/499)每个维度的窗口.这意味着球体的表观尺寸大约pi*5^2/499^2是整个窗口的1/2,500 (实际上,接近约1/3170),因此可能很难看到.这假设整个圆圈是在窗口区域内绘制的.但是,正如我们将在第2点中看到的那样.
  2. 由于视锥体的左平面位于x = 0,底平面位于y = 0,因此球体的几何中心将在窗口的最左下角呈现,这样投影球体中只有一个象限可见!这意味着所看到的甚至更小,大约pi*5^2/(4*499^2)是窗口大小的1/10,000(实际上,接近1/12,682nd).这将使其更难以看到.特别是因为球体渲染得如此接近屏幕的边缘/角落,您可能不会想到它.
  3. 由于视锥台的深度明显小于球体的直径(小于一半),因此只有球体的一小部分将位于视锥台内,仅呈现该部分.因此,屏幕上的空心圆将比实心球体/圆形更像.实际上,该条子的厚度可能代表屏幕上不到1个像素,这意味着我们甚至可能在屏幕上看不到任何内容,即使球体的一部分确实位于视锥体内.

解决方案只是改变视锥体和球体的半径.例如,

gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);
Run Code Online (Sandbox Code Playgroud)

呈现以下图像.

r = 5.0

正如您所看到的,在半球为5的球体的"赤道"周围只能看到一小部分.(我更改了投影以用球体填充窗口.)另一个例子,

gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);
Run Code Online (Sandbox Code Playgroud)

呈现以下图像.

r = 1.1

上面的图像显示了视锥体内部的更多球体,但是球体仍然比观察平截头体大0.2个深度单位.正如你所看到的,球体的"冰盖"在北方和南方都缺失了.因此,如果我们希望整个球体适合具有深度2的视锥体,我们必须使半径小于或等于1.

gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);
Run Code Online (Sandbox Code Playgroud)

呈现以下图像.

r = 1.0

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

  • 这应该是OP对实际问题的接受答案. (3认同)

小智 10

我不明白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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)