web-dev-qa-db-fra.com

Dessiner Sphère dans OpenGL sans utiliser gluSphere ()?

Existe-t-il des tutoriels qui expliquent comment dessiner une sphère dans OpenGL sans avoir à utiliser gluSphere()?

La plupart des didacticiels 3D pour OpenGL ne concernent que les cubes. J'ai cherché mais la plupart des solutions pour dessiner une sphère sont d'utiliser gluSphere(). Il y a aussi un site qui a le code pour dessiner une sphère sur ce site mais il n’explique pas les calculs derrière le dessin de la sphère. J'ai aussi d'autres versions sur la façon de dessiner la sphère dans un polygone au lieu de quads dans ce lien. Mais encore une fois, je ne comprends pas comment les sphères sont dessinées avec le code. Je veux pouvoir visualiser afin de pouvoir modifier la sphère si besoin est.

76
Carven

Une façon de le faire est de commencer avec un solide platonique à côtés triangulaires - un octaèdre , par exemple. Ensuite, prenez chaque triangle et coupez-le récursivement en triangles plus petits, comme suit:

recursively drawn triangles

Une fois que vous avez suffisamment de points, vous normalisez leurs vecteurs de sorte qu'ils soient tous à une distance constante du centre du solide. Cela provoque une déformation des côtés en une forme qui ressemble à une sphère, avec un lissage croissant au fur et à mesure que vous augmentez le nombre de points.

Normalisation signifie ici déplacer un point de manière à ce que son angle par rapport à un autre point soit le même, mais que la distance entre eux soit différente .. Voici un exemple en deux dimensions.

enter image description here

A et B sont séparés de 6 unités. Mais supposons que nous voulions trouver un point sur la ligne AB à 12 unités de A.

enter image description here

Nous pouvons dire que C est la forme normalisée de B par rapport à A, avec la distance 12. Nous pouvons obtenir C avec un code comme celui-ci:

#returns a point collinear to A and B, a given distance away from A. 
function normalize(a, b, length):
    #get the distance between a and b along the x and y axes
    dx = b.x - a.x
    dy = b.y - a.y
    #right now, sqrt(dx^2 + dy^2) = distance(a,b).
    #we want to modify them so that sqrt(dx^2 + dy^2) = the given length.
    dx = dx * length / distance(a,b)
    dy = dy * length / distance(a,b)
    point c =  new point
    c.x = a.x + dx
    c.y = a.y + dy
    return c

Si nous effectuons ce processus de normalisation sur de nombreux points, tous par rapport au même point A et à la même distance R, les points normalisés se situeront tous sur l’arc de cercle de centre A et de rayon R.

bulging line segment

Ici, les points noirs commencent sur une ligne et «se gonflent» en un arc.

Ce processus peut être étendu en trois dimensions, auquel cas vous obtenez une sphère plutôt qu'un cercle. Ajoutez simplement un composant dz à la fonction normaliser.

normalized polygons

level 1 bulging octahedronlevel 3 bulging octahedron

Si vous regardez la sphère à Epcot , vous pouvez en quelque sorte voir cette technique à l'œuvre. c'est un dodécaèdre aux visages bombés pour le faire paraître plus rond.

245
Kevin

J'expliquerai plus en détail un moyen populaire de générer une sphère à l'aide de la latitude et de la longitude (un autre moyen .__, icosphheres, a déjà été expliqué dans la réponse la plus populaire au moment de la rédaction de cet article.)

Une sphère peut être exprimée par l'équation paramétrique suivante:

F(u, v) = [cos (u) * sin (v) * r, cos (v) * r, sin (u) * sin (v) * r]

Où:

  • r est le rayon;
  • u est la longitude, allant de 0 à 2π; et
  • v est la latitude, allant de 0 à π.

La génération de la sphère implique ensuite l'évaluation de la fonction paramétrique à intervalles fixes.

Par exemple, pour générer 16 lignes de longitude, il y aura 17 lignes de grille le long de l'axe u, avec un pas de Π/8 (2π/16) (la 17e ligne est arrondie).

Le pseudocode suivant génère un maillage de triangle en évaluant une fonction paramétriqueà intervalles réguliers (cela fonctionne pour la fonction de surface paramétrique toute, pas seulement les sphères).

Dans le pseudo-code ci-dessous, UResolution est le nombre de points de la grille le long de l'axe U (Ici, les lignes de longitude), et VResolution est le nombre de points de la grille le long de l'axe V (ici, lignes de latitude)

var startU=0
var startV=0
var endU=PI*2
var endV=PI
var stepU=(endU-startU)/UResolution // step size between U-points on the grid
var stepV=(endV-startV)/VResolution // step size between V-points on the grid
for(var i=0;i<UResolution;i++){ // U-points
 for(var j=0;j<VResolution;j++){ // V-points
 var u=i*stepU+startU
 var v=j*stepV+startV
 var un=(i+1==UResolution) ? EndU : (i+1)*stepU+startU
 var vn=(j+1==VResolution) ? EndV : (j+1)*stepV+startV
 // Find the four points of the grid
 // square by evaluating the parametric
 // surface function
 var p0=F(u, v)
 var p1=F(u, vn)
 var p2=F(un, v)
 var p3=F(un, vn)
 // NOTE: For spheres, the normal is just the normalized
 // version of each vertex point; this generally won't be the case for
 // other parametric surfaces.
 // Output the first triangle of this grid square
 triangle(p0, p2, p1)
 // Output the other triangle of this grid square
 triangle(p3, p1, p2)
 }
}
21
Peter O.

Le code dans l'exemple est rapidement expliqué. Vous devriez regarder dans la fonction void drawSphere(double r, int lats, int longs). Les paramètres lat définissent le nombre de lignes horizontales que vous souhaitez avoir dans votre sphère et lon le nombre de lignes verticales. r est le rayon de votre sphère.

Maintenant, il y a une double itération sur lat/lon et les coordonnées du sommet sont calculées à l'aide d'une trigonométrie simple.

Les sommets calculés sont maintenant envoyés à votre GPU en utilisant glVertex...() en tant que GL_QUAD_STRIP, ce qui signifie que vous envoyez chacun deux sommets qui forment un quadrilatère avec les deux précédents envoyés.

Il ne vous reste plus qu'à comprendre comment fonctionnent les fonctions de trigonométrie, mais je suppose que vous pouvez le comprendre facilement.

2
Constantinius

Si vous vouliez être sournois comme un renard, vous pouvez convertir à moitié le code de GLU. Consultez le code source de MesaGL (http://cgit.freedesktop.org/mesa/mesa/).

1
blockchaindev

Bien que la réponse acceptée résolve la question, il existe une petite idée fausse à la fin. Les dodécaèdres sont (ou pourraient être) des polyèdres réguliers où toutes les faces ont la même surface. Cela semble être le cas de l'Epcot (qui, d'ailleurs, n'est pas du tout un dodécaèdre). Puisque la solution proposée par @Kevin ne fournit pas cette caractéristique, j'ai pensé que je pourrais ajouter une approche qui le fait. 

Un bon moyen de générer un polyèdre à N faces où tous les sommets se trouvent dans la même sphère et toutes ses faces ont une surface similaire comme suggéré dans la réponse acceptée). Les dodécaèdres, par exemple, sont en fait tronqués icosahedrons .

Icosaèdres réguliers ont 20 faces (12 sommets) et peuvent facilement être construits à partir de 3 rectangles dorés; c'est juste une question d'avoir ceci comme point de départ au lieu d'un octaèdre. Vous pouvez trouver un exemple ici .

Je sais que c'est un peu hors sujet, mais je pense que cela pourrait aider si quelqu'un arrive ici à la recherche de ce cas particulier.

1
Carles Araguz

Voir le livre rouge OpenGL: http://www.glprogramming.com/red/chapter02.html#name8 .__ Il résout le problème par subdivision de polygone.

1
Walter

Une solution consiste à créer un quad qui fait face à la caméra et à écrire un vertex et un fragment shader qui rendent quelque chose qui ressemble à une sphère. Vous pouvez utiliser des équations pour un cercle/une sphère que vous pouvez trouver sur Internet.

Une bonne chose est que la silhouette d’une sphère est identique sous tous les angles. Cependant, si la sphère n'est pas au centre d'une vue en perspective, elle ressemblera peut-être davantage à une ellipse. Vous pouvez trouver les équations pour cela et les mettre dans l’ombrage des fragments. Ensuite, les ombres claires doivent changer à mesure que le joueur bouge, si vous avez effectivement un joueur se déplaçant dans l'espace 3D autour de la sphère.

Quelqu'un peut-il dire s'il a déjà essayé ou s'il serait trop coûteux d'être pratique?

0
Steven2163712
struct v3
{
    double x,y,z;
    v3(double _x=0, double _y=0, double _z=0){x=_x;y=_y;z=_z;  }
    v3   operator +  ( v3 v)     {return { x+v.x, y+v.y, z+v.z };}
    v3   operator *  ( double k) {return { x*k, y*k, z*k };}
    v3   operator /  ( double k) {return { x/k, y/k, z/k };}
    v3 normalize(){
       double L=sqrt( x*x+y*y+z*z);
       return { x/L , y/L , z/L };}
};

void draw_spheree(double r,int adim)
{

    //              z
    //              |
    //               __
    //             /|          
    //              |           
    //              |           
    //              |    *      \
    //              | _ _| _ _ _ |    _y
    //             / \c  |n     /                    a4 --- a3
    //            /   \o |i                           |     |
    //           /     \s|s      z=sin(v)            a1 --- a2
    //         |/__              y=cos(v) *sin(u)
    //                           x=cos(v) *cos(u) 
    //       /
    //      x
    //

    //glEnable(GL_LIGHTING);
    //glEnable(GL_LIGHT0);
    //glEnable(GL_TEXTURE_2D); 

    double pi=3.141592;
    double d=pi/adim;

    for(double u=-pi  ; u<pi  ; u+=d)   //horizonal  xy düzlemi     Longitude -180  -180
    for(double v=-pi/2; v<pi/2; v+=d)   //vertical   z aks          Latitude  -90     90
    {
        v3  a1 = {  cos(v)*cos(u)       ,cos(v)*sin(u)      ,sin(v)     },
            a2 = {  cos(v)*cos(u+d)     ,cos(v)*sin(u+d)    ,sin(v)     },
            a3 = {  cos(v+d)*cos(u+d)   ,cos(v+d)*sin(u+d)  ,sin(v+d)   },
            a4 = {  cos(v+d)*cos(u)     ,cos(v+d)*sin(u)    ,sin(v+d)   };

        v3 normal=(a1+a2+a3+a4)/4.0;   //normal vector

        a1=a1*r;
        a2=a2*r;
        a3=a3*r;
        a4=a4*r;

        double tu=(u+pi)  / (2*pi);  //0 to 1  horizonal
        double tv=(v+pi/2)/ pi;      //0 to 1  vertical

        double td=1.0/2./adim;

        glNormal3dv((double *)&normal);

        glBegin(GL_POLYGON);
            glTexCoord2d(tu    ,tv      ); glVertex3dv((double *) &a1);
            glTexCoord2d(tu+td ,tv      ); glVertex3dv((double *) &a2);
            glTexCoord2d(tu+td ,tv+2*td ); glVertex3dv((double *) &a3);
            glTexCoord2d(tu    ,tv+2*td ); glVertex3dv((double *) &a4);
        glEnd();                
    } 
 }
0

Mon exemple d'utilisation de 'triangle strip' pour dessiner une sphère "polaire" consiste à dessiner des points par paires:

const float PI = 3.141592f;
GLfloat x, y, z, alpha, beta; // Storage for coordinates and angles        
GLfloat radius = 60.0f;
int gradation = 20;

for (alpha = 0.0; alpha < GL_PI; alpha += PI/gradation)
{        
    glBegin(GL_TRIANGLE_STRIP);
    for (beta = 0.0; beta < 2.01*GL_PI; beta += PI/gradation)            
    {            
        x = radius*cos(beta)*sin(alpha);
        y = radius*sin(beta)*sin(alpha);
        z = radius*cos(alpha);
        glVertex3f(x, y, z);
        x = radius*cos(beta)*sin(alpha + PI/gradation);
        y = radius*sin(beta)*sin(alpha + PI/gradation);
        z = radius*cos(alpha + PI/gradation);            
        glVertex3f(x, y, z);            
    }        
    glEnd();
}

Le premier point entré (glVertex3f) est comme suit l’équation paramétrique et le second est décalé d’un pas d’angle alpha (du parallèle suivant).

0
bloody