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();
}
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;
}
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:
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.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.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.
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.
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.
J'espère que cela a aidé quelqu'un. Prends soin!
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);
}
}
}
Voici le code:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
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);
}
}
}
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();
}