如何使用点图元将纹理映射到由参数方程渲染的球体

Posted

技术标签:

【中文标题】如何使用点图元将纹理映射到由参数方程渲染的球体【英文标题】:How to map texture to sphere that rendered by parametric equation using points primitive 【发布时间】:2018-01-22 11:05:03 【问题描述】:

是否可以将纹理映射到由参数方程生成并使用 GL_POINTS 基元渲染的球体?如果可能的话,它是如何完成的,在我的代码中,我从网上复制代码图像加载代码并按照指示加载它。我没有遵循它们的一件事是指定纹理坐标的顶点,我不确定在使用 Sphere 和 GL_POINTS 基元渲染它时如何准确指定它。

我正在为我的项目使用旧的 OpenGL2 并进行太阳系模拟

这是repository to the code,它是公开的

这就是我生成球体的方式

// void Sphere::render () method, inside src/sphere.cpp - line 28

void Sphere::render () 

    unsigned int angle = 0, angle2 = 0;
    const double degree_to_rad = 3.14 / 180.0;
    double value = .0, value2 = .0;

    if ( this -> texture_file_name != "" ) 

        glEnable ( GL_TEXTURE_2D );
        glBindTexture( GL_TEXTURE_2D, this -> texture );

    

    glBegin ( GL_POINTS );

        for ( ; angle < 360; ++ angle ) 

            value = angle * degree_to_rad;

            for ( ; angle2 < 180; ++ angle2 ) 

                value2 = angle2 * degree_to_rad;

                /*/////////////////////////////////////////////
                // do I need to do sth in here like glTexCoord2d ( ... ) ?
                ////////////////////////////////////////////*/

                glVertex3d (

                    this -> calculateX ( value, value2 ),
                    this -> calculateY ( value, value2 ),
                    this -> calculateZ ( value )

                );

            

            angle2 = 0;

        

    glEnd ();

    if ( this -> texture_file_name != "" ) 

        glDisable ( GL_TEXTURE_2D );

    

;

// void Sphere::draw () method, src/sphere.cpp - line 75

void Sphere::draw () 

    glPushMatrix ();

        glTranslated (

            this -> coordinate [ 0 ],
            this -> coordinate [ 1 ],
            this -> coordinate [ 2 ]

        );

        glRotated (

            this -> angle_degree,
            this -> rotation [ 0 ],
            this -> rotation [ 1 ],
            this -> rotation [ 2 ]

        );

        this -> render ();

    glPopMatrix ();

;

double Sphere::calculateX ( const double theta_degree_angle, const double phi_degree_angle ) 

    return this -> radius * sin ( theta_degree_angle ) * cos ( phi_degree_angle );

;

double Sphere::calculateY ( const double theta_degree_angle, const double phi_degree_angle ) 

    return this -> radius * sin ( theta_degree_angle ) * sin ( phi_degree_angle );

;

double Sphere::calculateZ ( const double theta_degree_angle ) 

    return this -> radius * cos ( theta_degree_angle );

;

这是我的 loadTexture 方法

void Object::loadTexture () 

    int & w = this -> texture_width, & h = this -> texture_height;
    unsigned char * data = new unsigned char [ w * h * 3 ];
    FILE * file;

    try 

        file = fopen ( this -> texture_file_name.c_str () , "rb" );

        if ( !file ) return;

        fread ( data, w * h * 3, 1, file );
        fclose ( file );

     catch ( std::exception & error ) 

        std::cout << "Loading Texture Error: " << error.what () << std::endl;

    

    glGenTextures ( 1, & this -> texture );
    glBindTexture ( GL_TEXTURE_2D, this -> texture );

    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data );

    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );

    glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

    delete [] data;

;

void Object::setTexture ( const char * file_name, const int width, const int height ) 

    this -> texture_file_name = file_name;
    this -> texture_width = width;
    this -> texture_height = height;

    this -> loadTexture ();

;

这是我的 main.cpp

// main.cpp

// gloabl vars
NonStd::Sphere sun = NonStd::Sphere ( 10 );
NonStd::Sphere earth = NonStd::Sphere ( 3 );
NonStd::Sphere moon = NonStd::Sphere ( 1 );

NonStd::Object space = NonStd::Object ();

void render ();
void modelInit ();
void idle ();
void windowOnChange ( int width, int height );
void mouseOnDrag ( int x, int y );

int main ( int args_len, char ** args_context ) 

    glutInit ( &args_len, args_context );
    glutInitDisplayMode ( GLUT_SINGLE );
    glutInitWindowSize ( WINDOW_WIDTH, WINDOW_HEIGHT );
    glutInitWindowPosition ( 100, 100 );
    glutCreateWindow ( "Solar System Simulation" );

    glEnable ( GL_NORMALIZE );
    glEnable ( GL_COLOR_MATERIAL );

    // all models initialization
    modelInit ();

    // event handlers
    glutDisplayFunc ( render );
    glutReshapeFunc ( windowOnChange );
    // glutMotionFunc ( mouseOnDrag );

    // global idle func
    glutIdleFunc ( idle );

    glutMainLoop ();

    return 0;

;

void render () 

    glClearColor ( .2, .3, .5, .8 );
    glClear ( GL_COLOR_BUFFER_BIT );

    if ( sun.isObjectShown () ) 

        sun.draw ();

    

    if ( earth.isObjectShown () ) 

        earth.draw ();

    

    if ( moon.isObjectShown () ) 

        moon.draw ();

    

    glFlush ();

;

void modelInit () 

    // object visibility default is false
    sun.setVisible ( true );
    // move to proper position to for object for better viewing
    sun.translateZ ( -90.0 ); 
    // set object texture
    sun.setTexture ( "resources/earth.jpg", 100, 100 );
    // spin default is false, toggle it for spinning
    sun.toggleSpin (); 

    earth.setVisible ( true );
    earth.translateZ ( -90.0 );
    earth.translateX ( 26.0 );
    earth.setTexture ( "resources/earth.jpg", 100, 100 );
    earth.toggleSpin ();
    earth.setSpinSpeed ( 2 );

    moon.setVisible ( true );
    moon.translateZ ( -90.0 );
    moon.translateX ( 20.0 );
    moon.setTexture ( "resources/earth.jpg", 100, 100 );
    moon.toggleSpin ();

;

After I set the texture on my sphere object, the sphere turn into this yellow color and before setting the texture, it was white, does this mean the texture already set but I have not yet specify the texture coordinate for it ?

仅供参考:该项目说它是 2D,但实际上我是在 3D 中进行的,只是为了澄清它。

【问题讨论】:

您必须在问题本身中显示最少但完整的代码。链接到外部资源只能作为补充材料。另外:目前还不清楚你在做什么。球体本身是由参数方程生成的,还是每个球体都只是一个点? 好的,我会在里面添加一个 GL_POINTS 是用来渲染点的。你想把纹理应用到什么地方?你想以什么结束?点应该改变颜色,还是球体应该被填充? @BartekBanachewicz 我希望我的球体结束,说它是一个地球,所以我想将地球纹理映射到它 @BartekBanachewicz 好的,我将尝试将其渲染为 GL_TRIANGLE_FAN 并使用纹理坐标 【参考方案1】:

球体既可以通过镶嵌现有对象(如二十面体)来创建,也可以通过堆叠圆盘来创建。

堆叠光盘:

以下代码通过堆叠多个圆盘 (layers) 创建一个球体。每一层的周边都有circumferenceTiles 瓦片。 U 纹理坐标环绕在圆周上。 V纹理坐标从球体的南极包裹到北极。

void CreateSphereMesh( int layers, int circumferenceTiles, std::vector<float> &va, std::vector<int> &ia )

    const float pi = 3.1414927f;

    // create the vertex attributes
    va.reserve( (layers+1)*(circumferenceTiles+1)*5 );  // 5 floats: x, y, z, u, v 
    for ( int il = 0; il <= layers; ++ il )
    
        float layer_rel = (float)il / (float)layers;
        float layer_ang = (1.0f - 2.0f * layer_rel) * pi/2.0f ;
        float layer_sin = std::sin( layer_ang );
        float layer_cos = std::cos( layer_ang );
        for ( int ic = 0; ic <= circumferenceTiles; ic ++ )
        
            float circum_rel = (float)ic / (float)circumferenceTiles;
            float cricum_ang = circum_rel * 2.0f*pi - pi;
            float circum_sin = std::sin( cricum_ang );
            float circum_cos = std::cos( cricum_ang );

            va.push_back( layer_cos * circum_cos ); // x
            va.push_back( layer_cos * circum_sin ); // y
            va.push_back( layer_sin );              // z
            va.push_back( circum_rel );             // u
            va.push_back( 1.0f - layer_rel );       // v
        
    

    // create the face indices 
    ia.reserve( layers*circumferenceTiles*6 );
    for ( int il = 0; il < layers; ++ il )
    
        for ( int ic = 0; ic < circumferenceTiles; ic ++ )
        
          int i0 = il * (circumferenceTiles+1) + ic;
          int i1 = i0 + 1;
          int i3 = i0 + circumferenceTiles+1;
          int i2 = i3 + 1;

          int faces[] i0, i1, i2, i0, i2, i3 ;
          ia.insert(ia.end(), faces+(il==0?3:0), faces+(il==layers-1?3:6));
        
    

固定函数 pipline 的顶点数组对象可以这样指定:

GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );

GLuint vbo;
glGenBuffers( 1, &vbo );
glBindBuffer( GL_ARRAY_BUFFER, vbo );
glBufferData( GL_ARRAY_BUFFER, va.size()*sizeof(*va.data()), va.data(), GL_STATIC_DRAW );

GLuint ibo;
glGenBuffers( 1, &ibo );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, ia.size()*sizeof(*ia.data()), ia.data(), GL_STATIC_DRAW );

glVertexPointer( 3, GL_FLOAT, 5*sizeof(*va.data()), 0 );
glEnableClientState( GL_VERTEX_ARRAY );
glTexCoordPointer( 2, GL_FLOAT, 5*sizeof(*va.data()), (void*)(3*sizeof(*va.data())) );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );

glBindVertexArray( 0 );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );

对于现代 OpenGL,通用顶点属性数据数组必须这样定义:

GLint avert_loc = ....;
GLint atexc_loc = ....;
glVertexAttribPointer( avert_loc, 3, GL_FLOAT, GL_FALSE, 5*sizeof(*va.data()), 0 );
glEnableVertexAttribArray( avert_loc );
glVertexAttribPointer( atexc_loc, 2, GL_FLOAT, GL_FALSE, 5*sizeof(*va.data()), (void*)(3*sizeof(*va.data()))  );
glEnableVertexAttribArray( atexc_loc );

最后的绘图操作:

glEnable( GL_TEXTURE_2D ); // for fixed function pipeline only
glBindVertexArray( vao );
glDrawElements( GL_TRIANGLES, (GLsizei)ia.size(), GL_UNSIGNED_INT, 0 );
glBindVertexArray( 0 );

预览:

【讨论】:

以上是关于如何使用点图元将纹理映射到由参数方程渲染的球体的主要内容,如果未能解决你的问题,请参考以下文章

球体在渲染纹理时会显示多边形

球体在使用纹理渲染时显示多边形

OpenGL:纹理基元 + 高帧率

在 OpenGL 中将纹理映射到球体时出现接缝问题

OpenGL 纹理映射禁用着色

FBO:渲染到纹理,绘制渲染纹理时纹理映射错误