4 votes

OpenGL - Rotation d'une "courbe" autour de l'axe Y

Selon mon question sur Math Stackexchange :

Je travaille sur un projet pour mon cours de graphisme 3D. Le projet est construit avec C++ et OpenGL / Glut. En gros, je crée une fenêtre rectangle horizontale, subdivisée en deux carrés. Sur la gauche, j'ai un plan de coordonnées bidimensionnel, qui permet aux utilisateurs de pointer et de cliquer pour définir une "courbe" de profil. Je dois ensuite enrouler cette courbe autour de l'axe Y un certain nombre de fois.

Quelqu'un pourrait-il m'indiquer comment utiliser la trigonométrie pour calculer les valeurs X et Z des points successifs ? Si par exemple, un utilisateur clique et crée le point :

(1, 1, 0)

Et si la résolution de balayage (n) est fixée à, disons, 10, je dois redessiner ce point tous les 36 (360/10) degrés autour de l'axe Y.

Ai-je raison de supposer que la trigonométrie m'aidera dans cette tâche ? Si oui, quelqu'un peut-il m'éclairer sur la façon de calculer la position d'un point translaté dans l'espace 3D ? Cela fait un moment que je n'ai pas pris de cours de trigonométrie, et je ne crois pas que nous ayons jamais quitté l'espace 2D.

EDIT : J'ai tenté d'utiliser :

x'=xcos(theta)-zsin(theta)

y'=y

z'=xsin(theta)+zcos(theta)

Selon ma compréhension de l'approche de l'AMPerrine répondre et je ne pense pas que cela ait fonctionné comme je l'espérais :

// this is in a loop

// setup the new angle
double angle = i>0 ? (360/sweepResolutionMod)*i : 0;

angle = angle * (M_PI/180);

// for each point...
for( int i=0; i<clickedPoints.size(); i++ )
{
    // initial point, normalized
    GLfloat tempX = (clickedPoints[i].x-250)/250;
    GLfloat tempY = (clickedPoints[i].y-250)/250;
    GLfloat tempZ = 0.0;

    // log the initial point
    cout << "(" << tempX << ", " << tempY << ", 0.0) by " << angle << " radians = ";

    // generate the new point
    GLfloat newX = (tempX * cos(angle)) - (tempZ * sin(angle));
    GLfloat newY = tempY;
    GLfloat newZ = (tempX * sin(angle)) - (tempZ * cos(angle));

    // log the new point
    cout << "(" << newX << ", " << newY << ", " << newZ << ")\n";

    // render the new point
    glVertex3d(newX, newY, newZ);
}

Cela ne produit aucune sortie à l'écran, mais la sortie console suivante :

(0.048, -0.296, 0.0) by 0 radians = (0.048, -0.296, 0)
(0.376, -0.508, 0.0) by 0 radians = (0.376, -0.508, 0)
(0.72, -0.204, 0.0) by 0 radians = (0.72, -0.204, 0)
(0.652, 0.176, 0.0) by 0 radians = (0.652, 0.176, 0)
(0.368, 0.504, 0.0) by 0 radians = (0.368, 0.504, 0)

(0.048, -0.296, 0.0) by 0.628319 radians = (0.0388328, -0.296, 0.0282137)
(0.376, -0.508, 0.0) by 0.628319 radians = (0.30419, -0.508, 0.221007)
(0.72, -0.204, 0.0) by 0.628319 radians = (0.582492, -0.204, 0.423205)
(0.652, 0.176, 0.0) by 0.628319 radians = (0.527479, 0.176, 0.383236)
(0.368, 0.504, 0.0) by 0.628319 radians = (0.297718, 0.504, 0.216305)

(0.048, -0.296, 0.0) by 1.25664 radians = (0.0148328, -0.296, 0.0456507)
(0.376, -0.508, 0.0) by 1.25664 radians = (0.11619, -0.508, 0.357597)
(0.72, -0.204, 0.0) by 1.25664 radians = (0.222492, -0.204, 0.684761)
(0.652, 0.176, 0.0) by 1.25664 radians = (0.201479, 0.176, 0.620089)
(0.368, 0.504, 0.0) by 1.25664 radians = (0.113718, 0.504, 0.349989)

...

(0.048, -0.296, 0.0) by 6.28319 radians = (0.048, -0.296, -1.17566e-17)
(0.376, -0.508, 0.0) by 6.28319 radians = (0.376, -0.508, -9.20934e-17)
(0.72, -0.204, 0.0) by 6.28319 radians = (0.72, -0.204, -1.76349e-16)
(0.652, 0.176, 0.0) by 6.28319 radians = (0.652, 0.176, -1.59694e-16)
(0.368, 0.504, 0.0) by 6.28319 radians = (0.368, 0.504, -9.0134e-17)

Je ne sais pas exactement ce qui se passe ici, mais je passe un temps fou à essayer de comprendre, alors ne pensez pas que j'essaie d'avoir une double réputation ou quoi que ce soit d'autre, je suis juste vraiment coincé.

EDIT 2 : Voici toute ma routine d'affichage pour ma vue secondaire en perspective :

void displayPersp(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();  

    gluLookAt (-2.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0);

    // draw the axis
    glBegin(GL_LINES);
    // x
    glVertex3f(500.0, 0.0, 0.0);
    glVertex3f(-500.0, 0.0, 0.0);
    // y
    glVertex3f(0.0, -500.0, 0.0);
    glVertex3f(0.0, 500.0, 0.0);
    // z
    glVertex3f(0.0, 0.0, -500.0);
    glVertex3f(0.0, 0.0, 500.0);

    glEnd(); 

    cout << endl;

    // loop as many number of times as we are going to draw the points around the Y-Axis
    for( int i=0; i<=sweepResolutionMod; i++ )
    {
        cout << endl;

        // setup the new angle
        double angle = i>0 ? (360/sweepResolutionMod)*i : 0;

        angle = angle * (M_PI/180);

        // for each point...
        for( int i=0; i<clickedPoints.size(); i++ )
        {
            GLfloat tempX = (clickedPoints[i].x-250)/250;
            GLfloat tempY = (clickedPoints[i].y-250)/250;
            GLfloat tempZ = 0.0;

            cout << "(" << tempX << ", " << tempY << ", 0.0) by " << angle << " degrees = ";

            GLfloat newX = (tempX * cos(angle)) - (tempZ * sin(angle));
            GLfloat newY = tempY;
            GLfloat newZ = (tempX * sin(angle)) - (tempZ * cos(angle));

            cout << "(" << newX << ", " << newY << ", " << newZ << ")\n";

            glVertex3d(newX, newY, newZ);
        }

        // the following was my old solution, using OpenGL's rotate(), but that
        // didn't allow me to get back the new point's coordinates.

        /*
        glRotatef(angle, 0.0, 1.0, 0.0);

        // draw a line?
        if( clickedPoints.size() > 1 )
        {
            glBegin(GL_LINE_STRIP);

            for(int i=0; i<clickedPoints.size(); i++ )
            {     
                glVertex3f((clickedPoints[i].x-250)/250, (clickedPoints[i].y-250)/250, 0.0);     
            }

            glEnd();
        }

        // everyone gets points
        glBegin(GL_POINTS);

        for(int i=0; i<clickedPoints.size(); i++ )
        { 
            glVertex3f((clickedPoints[i].x-250)/250, (clickedPoints[i].y-250)/250, 0.0);     
        }

        glEnd();
        */
    }

    glutSwapBuffers();
}

EDIT 3 : Voici une illustration terrible qui illustre ce que je dois faire. Je sais que la perspective semble fausse, mais ce que j'essaie d'acquérir, ce sont les "horizontales" vertes dans la sous-vue de droite (en utilisant le code glRotatef() commenté ci-dessus) :

Pic

ÉDITION FINALE (pour les générations futures !):

Voici ce que j'ai finalement réussi à faire fonctionner, après avoir discuté d'algèbre linéaire avec un professeur à l'université :

void displayPersp(void)
{
   glClear(GL_COLOR_BUFFER_BIT);

   gluLookAt (-2.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0);

   glMatrixMode (GL_MODELVIEW);
   glLoadIdentity ();  

   // draw the axis
   glBegin(GL_LINES);
     // x
     glVertex3f(500.0, 0.0, 0.0);
     glVertex3f(-500.0, 0.0, 0.0);
     // y
     glVertex3f(0.0, -500.0, 0.0);
     glVertex3f(0.0, 500.0, 0.0);
     // z
     glVertex3f(0.0, 0.0, -500.0);
     glVertex3f(0.0, 0.0, 500.0);

   glEnd(); 

   cout << endl;

   double previousTheta = 0.0;

   for( int i=0; i<=sweepResolutionMod; i++ )
   {
     double theta = i>0 ? (360/sweepResolutionMod)*i : 0;

     theta = theta * (M_PI/180);

     if( clickedPoints.size() > 1 )
     {
       // the 'vertical' piece
       glBegin(GL_LINE_STRIP);

       for(int i=0; i<clickedPoints.size(); i++ )
       {     
         // normalize
         GLfloat tempX = (clickedPoints[i].x-250)/250;
         GLfloat tempY = (clickedPoints[i].y-250)/250;
         GLfloat tempZ = 0.0;

         // new points
         GLfloat newX = ( tempX * cos(theta) ) + ( tempZ * sin(theta) );
         GLfloat newY = tempY;
         GLfloat newZ = ( tempZ * cos(theta) ) - ( tempX * sin(theta) );

         glVertex3f(newX, newY, newZ);     
       }

       glEnd();

       // the 'horizontal' piece
       if( previousTheta != theta )
       {
         glBegin(GL_LINES);

         for(int i=0; i<clickedPoints.size(); i++ )
         {     
           // normalize
           GLfloat tempX = (clickedPoints[i].x-250)/250;
           GLfloat tempY = (clickedPoints[i].y-250)/250;
           GLfloat tempZ = 0.0;

           // new points
           GLfloat newX = ( tempX * cos(theta) ) + ( tempZ * sin(theta) );
           GLfloat newY = tempY;
           GLfloat newZ = ( tempZ * cos(theta) ) - ( tempX * sin(theta) );

           // previous points
           GLfloat previousX = ( tempX * cos(previousTheta) ) + ( tempZ * sin(previousTheta) );
           GLfloat previousY = tempY;
           GLfloat previousZ = ( tempZ * cos(previousTheta) ) - ( tempX * sin(previousTheta) );

           // horizontal component           
           glVertex3f(newX, newY, newZ);     
           glVertex3f(previousX, previousY, previousZ);     
         }

         glEnd();
       }
     }

     previousTheta = theta;
   }

   glutSwapBuffers();
}

2voto

genpfault Points 28836

On dirait que vous essayez de construire un surface de révolution / solide de la révolution / "objet de tour" .

Un exemple concret :

lathe

#include <GL/glut.h>
#include <glm/glm.hpp>
#include <vector>
#include <cmath>

using namespace std;
using namespace glm;

struct Vertex
{
    Vertex( const vec3& position, const vec3& normal )
        : position( position )
        , normal( normal ) 
    {}
    vec3 position;
    vec3 normal;
};

// spin the pts array around the Z axis.
// pts.x will become the radius, and pts.y will become the height
// pts should be sorted by y-coordinate
vector< Vertex > Lathe( const vector< vec2 >& pts, unsigned int segments = 32 )
{
    // precalculate circle points
    vector< vec2 > circlePts;
    for( unsigned int i = 0; i <= segments; ++i )
    {
        float angle = ( i / (float)segments ) * 3.14159f * 2.0f;
        circlePts.push_back( vec2( cos( angle ), sin( angle ) ) );
    }

    // fill each layer
    typedef vector< vec3 > Layer;
    typedef vector< Layer > Layers;
    Layers layers( pts.size(), Layer( circlePts.size() ) );
    for( size_t i = 0; i < pts.size(); ++i )
    {
        for( unsigned int j = 0; j < circlePts.size(); ++j )
        {
            layers[i][j] = vec3( circlePts[j] * pts[i].x, pts[i].y );
        }
    }

    // move through layers generating triangles
    vector< Vertex > verts;
    for( size_t i = 1; i < layers.size(); ++i )
    {
        const Layer& prvLayer = layers[ i-1 ];
        const Layer& curLayer = layers[ i-0 ];
        for( size_t j = 1; j < circlePts.size(); ++j )
        {
            //    upper = cur layer
            //        UL -- UR  
            // left   | 0 /  |  right 
            // = j-1  |  / 1 |  = j-0
            //        LL -- LR  
            //    lower = prv layer
            const vec3& LL = prvLayer[ j-1 ]; // lower-left
            const vec3& LR = prvLayer[ j-0 ]; // lower-right
            const vec3& UL = curLayer[ j-1 ]; // upper-left
            const vec3& UR = curLayer[ j-0 ]; // upper-right

            // triangle0: LL -> UR -> UL
            const vec3 normal0 = normalize( cross( UR - LL, UL - LL ) );
            verts.push_back( Vertex( LL, normal0 ) );
            verts.push_back( Vertex( UR, normal0 ) );
            verts.push_back( Vertex( UL, normal0 ) );

            // triangle1: LL -> LR -> UR
            const vec3 normal1 = normalize( cross( LR - LL, UL - LL ) );
            verts.push_back( Vertex( LL, normal1 ) );
            verts.push_back( Vertex( LR, normal1 ) );
            verts.push_back( Vertex( UR, normal1 ) );
        }
    }

    return verts;
}

// mouse state
int btn;
ivec2 startMouse;
ivec2 startRot, curRot;

void mouse(int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
    {
        btn = button;
        startMouse = ivec2( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
        startRot = curRot;
    }
}

void motion( int x, int y )
{
    ivec2 curMouse( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
    if( btn == GLUT_LEFT_BUTTON )
    {
        curRot = startRot + ( curMouse - startMouse );
    }
    glutPostRedisplay();
}

vector< Vertex > model;
void display()
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    double w = glutGet( GLUT_WINDOW_WIDTH );
    double h = glutGet( GLUT_WINDOW_HEIGHT );
    double ar = w / h;
    gluPerspective( 60, ar, 0.1, 40 );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    glTranslatef( 0, 0, -10 );

    glPushMatrix();
    glRotatef( curRot.x % 360, 0, 1, 0 );
    glRotatef( -curRot.y % 360, 1, 0, 0 );

    // draw model
    if( !model.empty() )
    {
        glColor3ub( 255, 0, 0 );
        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_NORMAL_ARRAY );
        glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), &model[0].position );
        glNormalPointer( GL_FLOAT, sizeof(Vertex), &model[0].normal );
        glDrawArrays( GL_TRIANGLES, 0, model.size() );
        glDisableClientState( GL_VERTEX_ARRAY );
        glDisableClientState( GL_NORMAL_ARRAY );
    }

    // draw bounding cube
    glDisable( GL_LIGHTING );
    glColor3ub( 255, 255, 255 );
    glutWireCube( 7 );
    glEnable( GL_LIGHTING );

    glPopMatrix();

    glutSwapBuffers();
}

int main( int argc, char **argv )
{
    vector< vec2 > pts;
    pts.push_back( vec2( 0.1, -3 ) );
    pts.push_back( vec2( 2, -2 ) );
    pts.push_back( vec2( 3, -1 ) );
    pts.push_back( vec2( 1, 0 ) );
    pts.push_back( vec2( 3, 1 ) );
    pts.push_back( vec2( 4, 2 ) );
    pts.push_back( vec2( 4, 3 ) );
    model = Lathe( pts );

    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutInitWindowSize( 640, 480 );
    glutCreateWindow( "GLUT" );
    glutDisplayFunc( display );
    glutMouseFunc( mouse );
    glutMotionFunc( motion );

    glEnable( GL_DEPTH_TEST );

    // set up lighting
    glShadeModel( GL_SMOOTH );
    glEnable( GL_COLOR_MATERIAL );
    glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
    glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );
    glEnable( GL_LIGHTING );

    // set up "headlamp"-like light
    glEnable( GL_LIGHT0 );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    GLfloat position[] = { 0, 0, 1, 0 };
    glLightfv( GL_LIGHT0, GL_POSITION, position );

    glPolygonMode( GL_FRONT, GL_FILL );
    glPolygonMode( GL_BACK, GL_LINE );

    glutMainLoop();
    return 0;
}

1voto

Gabe Points 1664

Les fonctions trigonométriques prennent les angles en radians et non en degrés.

Je soupçonne également que votre fenêtre de visualisation n'est pas correctement configurée, ce qui explique pourquoi vous ne voyez rien à l'écran. Généralement, lorsque je pense que des choses ne sont pas rendues, elles le sont, mais je n'ai pas configuré la caméra, l'éclairage et d'autres choses correctement.

1voto

Jerry Coffin Points 237758

Edit 2 : Ok, je vois le problème que vous rencontrez -- c'est une limitation que j'avais oubliée (donc le code que j'avais posté précédemment était complètement faux et n'aurait pas fonctionné du tout). Le problème est que vous n'avez pas le droit d'appeler glRotate entre un glBegin / glEnd Si c'est le cas, un drapeau d'erreur sera mis en place et aucun dessin ne sera effectué.

Cela signifie que vous devez pratiquement vous occuper vous-même de la rotation. Heureusement, c'est un peu plus simple que ce que vous avez essayé de faire :

static const double pi = 3.1416;

for (int point=0; point<NUM_POINTS; point++) {
    glBegin(GL_LINE_STRIP);
    for (double theta = 0.0; theta < 2.0 * pi; theta += pi/6.0) {
        double x = cos(theta);
        double z = sin(theta);
        glVertex3d(points[point][0]*x, points[point][1], -1.0-points[point][0]*z);
    }
    glEnd();
}   

Tel quel, ce code utilise -1,0 le long de l'axe Z comme centre de rotation. Vous pouvez évidemment déplacer ce centre où vous le souhaitez, mais tout ce qui se trouve en dehors de votre frustum d'écrêtage ne s'affichera évidemment pas.

Notez également que pour obtenir une image filaire, vous devrez dessiner les lignes "verticales" et "horizontales" séparément, de sorte que le code ressemblera à quelque chose comme ceci :

for (int point=0; point<NUM_POINTS; point++) {
    glBegin(GL_LINE_STRIP);
    for (double theta = 0.0; theta < 2.0 * pi; theta += pi/6.0) {
        double x = cos(theta);
        double z = sin(theta);
        glVertex3d(points[point][0]*x, points[point][1], -1.0 - points[point][0]*z);
    }
    glEnd();
}

for (double theta = 0.0; theta < 2.0 * pi; theta += pi/6.0) {
    glBegin(GL_LINE_STRIP);
    for (int point=0; point<NUM_POINTS; point++) {
        double x = cos(theta);
        double z = sin(theta);
        glVertex3d(points[point][0]*x, points[point][1], -1.0 - points[point][0]*z);
    }
    glEnd();
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X