将地球纹理贴图应用到 Sphere

Posted

技术标签:

【中文标题】将地球纹理贴图应用到 Sphere【英文标题】:Applying map of the earth texture a Sphere 【发布时间】:2015-08-04 02:21:47 【问题描述】:

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

这是我的 Display 方法中的代码:

@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();        


【问题讨论】:

为什么要在显示循环中加载纹理?所有的初始化必须在显示循环之前完成,因为显示循环被称为 60>次/秒。关于你问的问题,你能在其他模型上看到纹理吗?如果不是,那么我会建议您更改您的 png 图像。您可以使用 GIMP 等软件。 宁可调用 GLU.createGLU(GL) 以创建功能更强大的 GLU 实例:jogamp.org/deployment/jogamp-next/javadoc/jogl/javadoc/com/… 如果不这样做,您可能会收到 UnsupportedOperationException,这取决于您调用的方法, GLU 实现的方法比 GLUgl2 少。 您的网格中缺少纹理坐标 谢谢你们的时间和帮助我让它工作 您好,您应该避免弃用 opengl 以支持着色器。我刚写完Hello Triangle,如果你想看看:) 【参考方案1】:

    创建自己的球体网格

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

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

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

    光照法线

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

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

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

    顶点位置

        // 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);
    

    将所有这些发送到 OpenGL

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

还可以查看这些相关的 Q/As:

realistic n-body solar system sphere mesh by subdivision

[edit1] C++ 示例

//---------------------------------------------------------------------------
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();
    
//---------------------------------------------------------------------------

用法:

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

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

【讨论】:

感谢 Spektre 的所有帮助,我让它工作了

以上是关于将地球纹理贴图应用到 Sphere的主要内容,如果未能解决你的问题,请参考以下文章

具有多个着色器程序的 OpenGL 多纹理

osg学习(七十二)SPHERE_MAP REFLECTION_MAP NORMAL_MAP

计算机图形学-纹理的应用,环境贴图凹凸贴图法线贴图位移贴图

计算机图形学-纹理的应用,环境贴图凹凸贴图法线贴图位移贴图

计算机图形学-纹理的应用,环境贴图凹凸贴图法线贴图位移贴图

应用纹理贴图