web-dev-qa-db-fra.com

Création d'une sphère 3D dans Opengl à l'aide de Visual C ++

Je ne suis pas en mesure de créer une sphère 3D simple à l'aide de la fonction de bibliothèque OpenGL glutSolidSphere () en C++.

Voici ce que j'ai essayé:

#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

Dans OpenGL, vous ne créez pas d'objets, vous les dessinez simplement. Une fois qu'ils sont dessinés, OpenGL ne se soucie plus de la géométrie que vous lui avez envoyée.

glutSolidSphere envoie simplement des commandes de dessin à OpenGL. Cependant, il n'y a rien de spécial à ce sujet. Et comme il est lié à GLUT, je ne l'utiliserais pas. Au lieu de cela, si vous avez vraiment besoin d'une sphère dans votre code, que diriez-vous de créer si pour vous-même?

#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

Il ne semble pas que quelqu'un ait jusqu'à présent résolu le problème réel avec votre code d'origine, donc j'ai pensé que je le ferais même si la question est assez ancienne à ce stade.

Le problème était à l'origine lié à la projection par rapport au rayon et à la position de la sphère. Je pense que vous constaterez que le problème n'est pas trop compliqué. Le programme fonctionne correctement, c'est juste que ce qui est dessiné est très difficile à voir.

Tout d'abord, une projection orthogonale a été créée à l'aide de l'appel

gluOrtho2D(0.0, 499.0, 0.0, 499.0);

qui " équivaut à appeler glOrtho avec près = -1 et loin = 1. " Cela signifie que le affichage du tronc a une profondeur de 2. Donc, une sphère avec un rayon de tout élément supérieur à 1 (diamètre = 2) ne rentrera pas entièrement dans le tronc de visualisation.

Ensuite, les appels

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

sont utilisés, qui charge la matrice d'identité de la matrice de vue du modèle, puis " [r] termine une sphère centrée sur les coordonnées de modélisation Origine du rayon spécifié. " Signification, la sphère est rendue à la Origine, (x, y, z) = (0, 0, 0), et avec un rayon de 5.

Maintenant, le problème est triple:

  1. Étant donné que la fenêtre est de 500 x 500 pixels et que la largeur et la hauteur du tronc de visualisation est de près de 500 (499,0), le petit rayon de la sphère (5,0) ne rend sa zone projetée que légèrement supérieure à un cinquantième (2 * 5/499) de la taille de la fenêtre dans chaque dimension. Cela signifie que la taille apparente de la sphère serait d'environ 1/2 500ème (en fait pi*5^2/499^2, Ce qui est plus proche d'environ 1/3170ème) de la fenêtre entière, donc peut être difficile à voir . Cela suppose que le cercle entier est dessiné dans la zone de la fenêtre. Ce n'est cependant pas le cas, comme nous le verrons au point 2.
  2. Étant donné que le tronc de visualisation a son plan gauche à x = 0 et son plan inférieur à y = 0, la sphère sera rendue avec son centre géométrique dans le coin inférieur gauche de la fenêtre de sorte qu'un seul quadrant de la sphère projetée sera visible ! Cela signifie que ce qui serait vu est encore plus petit, environ 1/10 000e (en fait pi*5^2/(4*499^2), ce qui est plus proche de 1/12 682e) de la taille de la fenêtre. Cela rendrait encore plus difficile à voir . D'autant plus que la sphère est rendue si près des bords/coins de l'écran où vous pourriez ne pas penser à regarder.
  3. Étant donné que la profondeur du tronc de visualisation est significativement plus petite que le diamètre de la sphère (moins de la moitié), seul un ruban de la sphère se trouvera dans le tronc de visualisation, ne rendant que cette partie. Ainsi, vous obtiendrez plus comme un cercle creux sur l'écran qu'une sphère/cercle solide. En l'occurrence, l'épaisseur de ce ruban pourrait représenter moins de 1 pixel à l'écran, ce qui signifie que nous pourrions même ne voir rien à l'écran, même si une partie de la sphère est en effet dans le tronc de visualisation.

La solution consiste simplement à modifier le tronc de visualisation et le rayon de la sphère. Par exemple,

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

rend l'image suivante.

r = 5.0

Comme vous pouvez le voir, seule une petite partie est visible autour de "l'équateur", de la sphère avec un rayon de 5. (J'ai changé la projection pour remplir la fenêtre avec la sphère.) Un autre exemple,

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

rend l'image suivante.

r = 1.1

L'image ci-dessus montre une plus grande partie de la sphère à l'intérieur du tronc de visualisation, mais la sphère est toujours 0,2 unités de profondeur plus grande que le tronc de visualisation. Comme vous pouvez le voir, les "calottes glaciaires" de la sphère manquent, tant au nord qu'au sud. Donc, si nous voulons que la sphère entière rentre dans le tronc de visualisation qui a la profondeur 2, nous devons rendre le rayon inférieur ou égal à 1.

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

rend l'image suivante.

r = 1.0

J'espère que cela a aidé quelqu'un. Prends soin!

21
Victor Zamanian

Je ne comprends pas comment la génération d'index de datenwolf peut être correcte. Mais je trouve toujours sa solution assez claire. Voici ce que j'obtiens après réflexion:

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

Voici le code:

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

J'aime la réponse de la monnaie. C'est simple à comprendre et fonctionne avec des triangles. Cependant, les index de son programme dépassent parfois les limites. Je poste donc ici son code avec deux petites 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);
        }
    }
}
3
Harald

Le code de Datanewolf est PRESQUE correct. J'ai dû inverser à la fois le bobinage et les normales pour le faire fonctionner correctement avec le pipeline fixe. Ce qui suit fonctionne correctement avec cull on ou off pour moi:

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: Il y avait une question sur la façon de dessiner cela ... dans mon code, j'encapsule ces valeurs dans une classe G3DModel. Voici mon code pour configurer le cadre, dessiner le modèle et le terminer:

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