Applying map of the earth texture a Sphere

2019-01-07 00:48发布

问题:

i been trying to implement a 3D animation in openGL (using JOGL) of a solar system so far i have 5 planets of different sizes but the problem i seem to be having is i cant add a map of the earth texture on a Sphere can anybody help me on how its done?

This is the code i have so far in my Display method:

@Override
public void display(GLAutoDrawable drawable) {
    GL2 gl = drawable.getGL().getGL2(); 
    GLU glu = new GLU();
    gl.glClear(GL.GL_COLOR_BUFFER_BIT);

    //make sure we are in model_view mode
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    glu.gluLookAt(10,20,20,0,3,0,0, 20, 0);
    //gl.glMatrixMode(GL2.GL_PROJECTION);
    //glu.gluPerspective(45,1,1,25);

    //render ground plane
    gl.glPushMatrix();
    gl.glTranslatef(-10.75f, 3.0f, -1.0f);
    gl.glColor3f(0.3f, 0.5f, 1f);
    GLUquadric earth = glu.gluNewQuadric();
    glu.gluQuadricDrawStyle(earth, GLU.GLU_FILL);
    glu.gluQuadricNormals(earth, GLU.GLU_FLAT);
    glu.gluQuadricOrientation(earth, GLU.GLU_OUTSIDE);
    final float radius = 3.378f;
    final int slices = 89;
    final int stacks = 16;
    glu.gluSphere(earth, radius, slices, stacks);
    glu.gluDeleteQuadric(earth);

    Texture earths;
    try {
      earths = TextureIO.newTexture(new File("earth.png"), true);
    }
    catch (IOException e) {    
      javax.swing.JOptionPane.showMessageDialog(null, e);
    }        
    gl.glPopMatrix();
    //gl.glEnd();

    gl.glPushMatrix();
    gl.glTranslatef(2.75f, 3.0f, -0.0f);
    gl.glColor3f(0.3f, 0.5f, 1f);
    GLUquadric earth1 = glu.gluNewQuadric();
    glu.gluQuadricDrawStyle(earth1, GLU.GLU_FILL);
    glu.gluQuadricNormals(earth1, GLU.GLU_FLAT);
    glu.gluQuadricOrientation(earth1, GLU.GLU_OUTSIDE);
    final float radius1 = 3.378f;
    final int slices1 = 90;
    final int stacks1 = 63;
    glu.gluSphere(earth1, radius1, slices1, stacks1);
    glu.gluDeleteQuadric(earth1);
    gl.glPopMatrix();

    gl.glPushMatrix();
    gl.glTranslatef(3.75f, 6.0f, -7.20f);
    gl.glColor3f(0.3f, 0.5f, 1f);
    GLUquadric earth3 = glu.gluNewQuadric();
    glu.gluQuadricDrawStyle(earth3, GLU.GLU_FILL);
    glu.gluQuadricNormals(earth3, GLU.GLU_FLAT);
    glu.gluQuadricOrientation(earth1, GLU.GLU_OUTSIDE);
    final float radius3 = 1.878f;
    final int slices3 = 89;
    final int stacks3 = 16;
    glu.gluSphere(earth3, radius3, slices3, stacks3);
    glu.gluDeleteQuadric(earth3);
    gl.glPopMatrix();   

    gl.glPushMatrix();
    gl.glTranslatef(12.75f, 2.0f, -7.20f);
    gl.glColor3f(0.3f, 0.5f, 1f);
    GLUquadric earth4 = glu.gluNewQuadric();
    glu.gluQuadricDrawStyle(earth4, GLU.GLU_FILL);
    glu.gluQuadricNormals(earth4, GLU.GLU_FLAT);
    glu.gluQuadricOrientation(earth4, GLU.GLU_OUTSIDE);
    final float radius4 = 1.078f;
    final int slices4 = 89;
    final int stacks4 = 16;
    glu.gluSphere(earth4, radius4, slices4, stacks4);
    glu.gluDeleteQuadric(earth4);

    gl.glPopMatrix(); 

    gl.glPushMatrix();
    gl.glTranslatef(2.75f, -6.0f, -0.0f);
    gl.glColor3f(0.3f, 0.5f, 1f);
    GLUquadric earth5 = glu.gluNewQuadric();
    glu.gluQuadricDrawStyle(earth5, GLU.GLU_FILL);
    glu.gluQuadricNormals(earth5, GLU.GLU_FLAT);
    glu.gluQuadricOrientation(earth5, GLU.GLU_OUTSIDE);
    final float radius5 = 3.778f;
    final int slices5 = 90;
    final int stacks5 = 63;
    glu.gluSphere(earth5, radius5, slices5, stacks5);
    glu.gluDeleteQuadric(earth5);
    gl.glPopMatrix();        

}

回答1:

  1. create your own sphere mesh

    simple 2D loop through 2 angles (spherical coordinate system 2 Cartesian). You can easily add ellipsoid properties (earth is not a sphere) if you want more precision. If not then you can use single sphere mesh for all planets and just scale it before use ...

    let a be the longitude and b the latitude so loop a from 0 to 2*PI [rad] and b from -0.5*PI to +0.5*PI [rad] where PI=3.1415... is the Pi (in C++ math.h it is called M_PI). If your math api uses degrees then convert to degrees PI [rad] = 180.0 [deg]

  2. add necessary info per vertex

    normals for lighting

        // just unit sphere
        nx=cos(b)*cos(a);
        ny=cos(b)*sin(a);
        nz=sin(b);
    

    texture coordinate (assuming rectangle non distorted image)

        // just convert a,b to <0,1> range
        tx=a/(2.0*PI)
        ty=(b/PI)+0.5;
    

    vertex position

        // just sphere(rx=ry=rz=r) or ellipsoid (rx=ry=equatorial and rz=polar radius)
        // can also use rx*nx,ry*ny,rz*nz instead ...
        x=rx*cos(b)*cos(a);
        y=ry*cos(b)*sin(a);
        z=rz*sin(b);
    
  3. send all of this to OpenGL

    so all above store in some memory space (CPU or GPU) and then send to rendering. You can use legacy glBegin(QUAD_STRIP); ... glEnd(); or displaylist/VBO/VAO. Bind the right texture before each planet/body and do not forget to update ModelView matrix too. This is how mine coordinate systems looks like:

Also have a look at these related Q/As:

  • realistic n-body solar system
  • sphere mesh by subdivision

[edit1] C++ example

//---------------------------------------------------------------------------
const int nb=15;        // slices
const int na=nb<<1;     // points per equator
class planet
    {
public:
    bool _init;             // has been initiated ?
    GLfloat x0,y0,z0;       // center of planet [GCS]
    GLfloat pos[na][nb][3]; // vertex
    GLfloat nor[na][nb][3]; // normal
    GLfloat txr[na][nb][2]; // texcoord
    GLuint  txrid;          // texture id
    GLfloat t;              // dayly rotation angle [deg]
    planet() { _init=false; txrid=0; x0=0.0; y0=0.0; z0=0.0; t=0.0; }
    ~planet() { if (_init) glDeleteTextures(1,&txrid); }
    void init(GLfloat r,AnsiString texture);        // call after OpenGL is already working !!!
    void draw();
    };
void planet::init(GLfloat r,AnsiString texture)
    {
    if (!_init) { _init=true; glGenTextures(1,&txrid); }

    GLfloat x,y,z,a,b,da,db;
    GLfloat tx0,tdx,ty0,tdy;// just correction if CLAMP_TO_EDGE is not available
    int ia,ib;

    // a,b to texture coordinate system
    tx0=0.0;
    ty0=0.5;
    tdx=0.5/M_PI;
    tdy=1.0/M_PI;

    // load texture to GPU memory
    if (texture!="")
        {
        Byte q;
        unsigned int *pp;
        int xs,ys,x,y,adr,*txr;
        union { unsigned int c32; Byte db[4]; } c;
        Graphics::TBitmap *bmp=new Graphics::TBitmap;   // new bmp
        bmp->LoadFromFile(texture); // load from file
        bmp->HandleType=bmDIB;      // allow direct access to pixels
        bmp->PixelFormat=pf32bit;   // set pixel to 32bit so int is the same size as pixel
        xs=bmp->Width;              // resolution should be power of 2
        ys=bmp->Height;
        txr=new int[xs*ys];
        for(adr=0,y=0;y<ys;y++)
            {
            pp=(unsigned int*)bmp->ScanLine[y];
            for(x=0;x<xs;x++,adr++)
                {
                // rgb2bgr and copy bmp -> txr[]
                c.c32=pp[x];
                q      =c.db[2];
                c.db[2]=c.db[0];
                c.db[0]=q;
                txr[adr]=c.c32;
                }
            }
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D,txrid);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_RGBA, GL_UNSIGNED_BYTE, txr);
        glDisable(GL_TEXTURE_2D);
        delete bmp;
        delete[] txr;

        // texture coordinates by 1 pixel from each edge (GL_CLAMP_TO_EDGE)
        tx0+=1.0/GLfloat(xs);
        ty0+=1.0/GLfloat(ys);
        tdx*=GLfloat(xs-2)/GLfloat(xs);
        tdy*=GLfloat(ys-2)/GLfloat(ys);
        }
    // correct texture coordinate system (invert x)
    tx0=1.0-tx0; tdx=-tdx;

    da=(2.0*M_PI)/GLfloat(na-1);
    db=     M_PI /GLfloat(nb-1);
    for (ib=0,b=-0.5*M_PI;ib<nb;ib++,b+=db)
    for (ia=0,a= 0.0     ;ia<na;ia++,a+=da)
        {
        x=cos(b)*cos(a);
        y=cos(b)*sin(a);
        z=sin(b);
        nor[ia][ib][0]=x;
        nor[ia][ib][1]=y;
        nor[ia][ib][2]=z;
        pos[ia][ib][0]=r*x;
        pos[ia][ib][1]=r*y;
        pos[ia][ib][2]=r*z;
        txr[ia][ib][0]=tx0+(a*tdx);
        txr[ia][ib][1]=ty0+(b*tdy);
        }
    }
void planet::draw()
    {
    if (!_init) return;
    int ia,ib0,ib1;
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(x0,y0,z0);
    glRotatef(90.0,1.0,0.0,0.0); // rotate planets z axis (North) to OpenGL y axis (Up)
    glRotatef(-t,0.0,0.0,1.0); // rotate planets z axis (North) to OpenGL y axis (Up)

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,txrid);
    glColor3f(1.0,1.0,1.0);
    for (ib0=0,ib1=1;ib1<nb;ib0=ib1,ib1++)
        {
        glBegin(GL_QUAD_STRIP);
        for (ia=0;ia<na;ia++)
            {
            glNormal3fv  (nor[ia][ib0]);
            glTexCoord2fv(txr[ia][ib0]);
            glVertex3fv  (pos[ia][ib0]);
            glNormal3fv  (nor[ia][ib1]);
            glTexCoord2fv(txr[ia][ib1]);
            glVertex3fv  (pos[ia][ib1]);
            }
        glEnd();
        }
    glDisable(GL_TEXTURE_2D);
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    }
//---------------------------------------------------------------------------

usage:

// variable to store planet (global)
planet earth;

// init after OpenGL initialisation
earth.init(1.0,"earth.bmp");

// position update
earth.x0=  0.0;
earth.y0=  0.0;
earth.z0=-20.0;

// add this to render loop
earth.draw(); // draws the planet
earth.t+=2.5; // just rotate planet by 2.5 deg each frame...

I know its ugly but it does not use any funny stuff just legacy OpenGL and Math.h (cos(),sin(),M_PI) and VCL for bitmap loading. So rewrite to your environment and you will be fine. Do not forget that each planet has its own texture so you need to have one txrid per planet so either have each planet as separate planet variable or rewrite ...



标签: java opengl jogl