将地球纹理贴图应用到球体 [英] Applying map of the earth texture a Sphere

查看:32
本文介绍了将地球纹理贴图应用到球体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,我一直在尝试在太阳系的 openGL(使用 JOGL)中实现 3D 动画,我有 5 个不同大小的行星,但我似乎遇到的问题是我无法在Sphere 谁能帮我看看它是怎么做的?

这是我目前在我的 Display 方法中的代码:

@Override公共无效显示(GLAutoDrawable可绘制){GL2 gl = drawable.getGL().getGL2();GLU glu = 新 GLU();gl.glClear(GL.GL_COLOR_BUFFER_BIT);//确保我们处于model_view模式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);//渲染地平面gl.glPushMatrix();gl.glTranslatef(-10.75f, 3.0f, -1.0f);gl.glColor3f(0.3f, 0.5f, 1f);GLUquadric 地球 = glu.gluNewQuadric();glu.gluQuadricDrawStyle(地球,GLU.GLU_FILL);glu.gluQuadricNormals(地球,GLU.GLU_FLAT);glu.gluQuadricOrientation(地球,GLU.GLU_OUTSIDE);最终浮动半径 = 3.378f;最终 int 切片 = 89;最终 int 堆栈 = 16;glu.gluSphere(地球,半径,切片,堆栈);glu.gluDeleteQuadric(地球);纹理地球;尝试 {地球 = TextureIO.newTexture(new File("earth.png"), true);}捕获(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);最终浮动半径1 = 3.378f;最终 int slices1 = 90;最终 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);最终浮动半径3 = 1.878f;最终 int slices3 = 89;最终 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);最终浮动半径4 = 1.078f;最终 int slices4 = 89;最终 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);最终浮动半径5 = 3.778f;最终 int slices5 = 90;最终 int stacks5 = 63;glu.gluSphere(earth5, radius5, slices5, stacks5);glu.gluDeleteQuadric(earth5);gl.glPopMatrix();}

解决方案

  1. 创建自己的球体网格

    简单的 2D 循环通过 2 个角度(球坐标系 2 笛卡尔坐标系).如果您想要更高的精度,您可以轻松添加椭球属性(地球不是球体).如果没有,那么您可以对所有行星使用单个球体网格,并在使用前对其进行缩放...

    a 为经度,b 为纬度,这样循环 a02*PI [rad] 和 b-0.5*PI+0.5*PI [rad] 其中 PI=3.1415... 是 Pi(在 C++ math.h 中称为 M_PI).如果您的数学 API 使用度数,则转换为度数 PI [rad] = 180.0 [deg]

  2. 为每个顶点添加必要的信息

    照明法线

    //只是单位球体nx=cos(b)*cos(a);ny=cos(b)*sin(a);nz=sin(b);

    纹理坐标(假设矩形无失真图像)

    //只是将 a,b 转换为 <0,1>范围tx=a/(2.0*PI)ty=(b/PI)+0.5;

    顶点位置

    //只是球体(rx=ry=rz=r)或椭球体(rx=ry=equatorial and rz=polar radius)//也可以使用 rx*nx,ry*ny,rz*nz 代替 ...x=rx*cos(b)*cos(a);y=ry*cos(b)*sin(a);z=rz*sin(b);

  3. 将所有这些发送给 OpenGL

    所以以上所有内容都存储在一些内存空间(CPUGPU)中,然后发送到渲染.您可以使用旧的 glBegin(QUAD_STRIP);... glEnd(); 或 displaylist/VBO/VAO.在每个行星/身体之前绑定正确的纹理,不要忘记更新 ModelView 矩阵.这是我的坐标系的样子:

还可以看看这些相关的问答:

  • 我知道它很丑,但它没有使用任何有趣的东西,只是传统的 OpenGLMath.h (cos(),sin(),M_PI) 和 VCL 用于位图加载.所以重写你的环境,你会没事的.不要忘记每个行星都有自己的纹理,因此您需要每个行星有一个 txrid,因此要么将每个行星作为单独的 planet 变量,要么重写 ...

    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. 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:

    [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 ...

    这篇关于将地球纹理贴图应用到球体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆