简体   繁体   English

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

[英]Creating a 3D sphere in Opengl using Visual C++

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

In OpenGL you don't create objects, you just draw them. 在OpenGL中,您不创建对象,只需绘制它们即可。 Once they are drawn, OpenGL no longer cares about what geometry you sent it. 绘制完成后,OpenGL不再关心您发送的几何体。

glutSolidSphere is just sending drawing commands to OpenGL. glutSolidSphere只是向OpenGL发送绘图命令。 However there's nothing special in and about it. 然而,它并没有什么特别之处。 And since it's tied to GLUT I'd not use it. 因为它与GLUT联系在一起我不会使用它。 Instead, if you really need some sphere in your code, how about create if for yourself? 相反,如果你的代码中确实需要一些球体,那么如何为自己创造呢?

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

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. 相当于用near = -1和far = 1调用glOrtho。 ”这意味着视锥体的深度为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,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. 由于窗口为500x500像素,并且视锥体的宽度和高度几乎为500(499.0),球体的小半径(5.0)使其投影面积仅略大于该尺寸的五十(2 * 5/499)每个维度的窗口。 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 . 这意味着球体的表观尺寸大约为整个窗口的1 / 2,500th(实际上是pi*5^2/499^2 2/499 pi*5^2/499^2 ,接近约1/3170),因此可能很难看到 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点中看到的那样。
  2. 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! 由于视锥体的左平面位于x = 0,底平面位于y = 0,因此球体的几何中心将在窗口的最左下角呈现,这样投影球体中只有一个象限可见! 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. 这意味着所看到的甚至更小,约为窗口大小的1 / 10,000(实际上是pi*5^2/(4*499^2) ,接近1 / 12,682)。 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. 特别是因为球体渲染得如此接近屏幕的边缘/角落,您可能不会想到它。
  3. 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. 实际上,该条子的厚度可能代表屏幕上不到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. 呈现以下图像。

r = 5.0

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. 呈现以下图像。

r = 1.1

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. 上面的图像显示了视锥体内部的更多球体,但是球体仍然比观察平截头体大0.2个深度单位。 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. 因此,如果我们希望整个球体适合具有深度2的视锥体,我们必须使半径小于或等于1。

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

renders the following image. 呈现以下图像。

r = 1.0

I hope this has helped someone. 我希望这对某人有所帮助。 Take care! 照顾自己!

I don't understand how can datenwolf`s index generation can be correct. 我不明白datenwolf的索引生成怎么可能是正确的。 But still I find his solution rather clear. 但我仍然觉得他的解决方案很清楚。 This is what I get after some thinking: 这是我在思考之后得到的:

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

Here's the code: 这是代码:

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

Datanewolf's code is ALMOST right. Datanewolf的代码几乎是正确的。 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: 以下工作正常,对我来说剔除或关闭:

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. 编辑:有一个关于如何绘制这个问题的问题...在我的代码中,我将这些值封装在G3DModel类中。 This is my code to setup the frame, draw the model, and end it: 这是我设置框架,绘制模型并结束它的代码:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM