it-swarm.com.de

Erstellen einer 3D-Kugel in OpenGL mit Visual C ++

Ich kann mit der OpenGL-Bibliotheksfunktion glutSolidSphere () in C++ keine einfache 3D-Kugel erstellen.

Folgendes habe ich versucht:

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

In OpenGL erstellen Sie keine Objekte, sondern zeichnen sie nur. Nachdem sie gezeichnet wurden, kümmert sich OpenGL nicht mehr darum, welche Geometrie Sie gesendet haben.

glutSolidSphere sendet nur Zeichenbefehle an OpenGL. Es gibt jedoch nichts Besonderes. Und da es an GLUT gebunden ist, würde ich es nicht benutzen. Wenn Sie stattdessen wirklich eine Kugel in Ihrem Code benötigen, wie wäre es, wenn Sie sie für sich selbst erstellen?

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

Es scheint, dass noch niemand das eigentliche Problem mit Ihrem ursprünglichen Code angesprochen hat. Deshalb dachte ich, ich würde das tun, obwohl die Frage zu diesem Zeitpunkt noch ziemlich alt ist.

Das Problem hatte ursprünglich mit der Projektion in Bezug auf den Radius und die Position der Kugel zu tun. Ich denke, Sie werden feststellen, dass das Problem nicht zu kompliziert ist. Das Programm funktioniert tatsächlich korrekt, es ist nur sehr schwer zu erkennen, was gezeichnet wird.

Zunächst wurde mit dem Aufruf eine orthogonale Projektion erstellt

gluOrtho2D(0.0, 499.0, 0.0, 499.0);

welches " entspricht dem Aufruf von glOrtho mit nah = -1 und fern = 1. " Dies bedeutet, dass das Betrachtungsstumpf eine Tiefe von 2 hat. Also eine Kugel mit einem Radius von etwas größer als 1 (Durchmesser = 2) wird nicht vollständig in den Betrachtungsstumpf passen.

Dann die Anrufe

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

werden verwendet, die die Identitätsmatrix der Modellansichtsmatrix lädt und dann " [r] beendet eine an den Modellkoordinaten zentrierte Kugel Ursprung des angegebenen Radius. " Das heißt, die Kugel wird am gerendert Ursprung, (x, y, z) = (0, 0, 0) und mit einem Radius von 5.

Nun ist das Problem dreifach:

  1. Da das Fenster 500 x 500 Pixel groß ist und die Breite und Höhe des Betrachtungsstumpfs fast 500 (499,0) beträgt, wird die projizierte Fläche durch den kleinen Radius der Kugel (5,0) nur geringfügig größer als ein Fünfzigstel (2 × 5/499) des Fensters in jeder Dimension. Dies bedeutet, dass die scheinbare Größe der Kugel ungefähr 1/2.500stel (tatsächlich pi*5^2/499^2, Was näher an 1/3170stel liegt) des gesamten Fensters ist, also ist möglicherweise schwer zu erkennen . Dies setzt voraus, dass der gesamte Kreis im Bereich des Fensters gezeichnet wird. Es ist jedoch nicht so, wie wir in Punkt 2 sehen werden.
  2. Da der Betrachtungskegel seine linke Ebene bei x = 0 und seine untere Ebene bei y = 0 hat, wird die Kugel mit ihrem geometrischen Mittelpunkt in der linken unteren Ecke des Fensters gerendert, sodass nur ein Quadrant der projizierten Kugel sichtbar ist ! Dies bedeutet, dass das, was zu sehen wäre, noch kleiner ist, etwa 1/10 000 (tatsächlich pi*5^2/(4*499^2), was näher an 1/12 682 liegt) der Fenstergröße. Dies würde es noch schwieriger machen zu sehen . Zumal die Kugel so nahe an den Rändern/Ecken des Bildschirms gerendert wird, dass Sie vielleicht nicht denken, sie zu sehen.
  3. Da die Tiefe des Betrachtungsstumpfes erheblich kleiner ist als der Durchmesser der Kugel (weniger als die Hälfte), befindet sich nur ein Teil der Kugel im Betrachtungsstumpf, wodurch nur dieser Teil gerendert wird. Sie sehen also eher wie ein hohler Kreis auf dem Bildschirm aus als wie eine durchgezogene Kugel/ein Kreis. Es kann vorkommen, dass die Dicke dieses Faserbandes weniger als 1 Pixel auf dem Bildschirm darstellt, was bedeutet, dass wir auf dem Bildschirm möglicherweise sogar nichts sehen , auch wenn es sich um einen Teil handelt der Kugel liegt in der Tat im Betrachtungskegelstumpf.

Die Lösung besteht einfach darin, den Betrachtungskegelstumpf und den Radius der Kugel zu ändern. Zum Beispiel,

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

rendert das folgende Bild.

r = 5.0

Wie Sie sehen, ist nur ein kleiner Teil der Kugel mit einem Radius von 5 um den "Äquator" sichtbar. (Ich habe die Projektion geändert, um das Fenster mit der Kugel zu füllen.)

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

rendert das folgende Bild.

r = 1.1

Das obige Bild zeigt mehr von der Kugel innerhalb des Betrachtungsstumpfes, aber die Kugel ist immer noch 0,2 Tiefeneinheiten größer als der Betrachtungsstumpf. Wie Sie sehen, fehlen die "Eiskappen" der Kugel sowohl im Norden als auch im Süden. Wenn wir also wollen, dass die gesamte Kugel in den Betrachtungsstumpf mit der Tiefe 2 passt, müssen wir den Radius kleiner oder gleich 1 machen.

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

rendert das folgende Bild.

r = 1.0

Ich hoffe das hat jemandem geholfen. Sich kümmern!

21
Victor Zamanian

Ich verstehe nicht, wie die Indexgenerierung von Datenwolf korrekt sein kann. Trotzdem finde ich seine Lösung ziemlich klar. Das bekomme ich nach einigem Nachdenken:

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

Hier ist der Code:

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

Ich mag die Antwort von Münzen. Es ist einfach zu verstehen und funktioniert mit Dreiecken. Jedoch sind die Indizes seines Programms manchmal über die Grenzen hinaus. Also poste ich hier seinen Code mit zwei winzigen Korrekturen:

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

Datanewolfs Code ist fast richtig. Ich musste sowohl die Wicklung als auch die Normalen umkehren, damit es mit der festen Rohrleitung richtig funktionierte. Das Folgende funktioniert bei mir mit ein- oder ausgeschaltetem Cull korrekt:

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;

}

Bearbeiten: Es gab eine Frage zum Zeichnen ... In meinem Code kapsele ich diese Werte in einer G3DModel-Klasse. Dies ist mein Code, um den Rahmen einzurichten, das Modell zu zeichnen und es zu beenden:

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