【问题标题】:How do I texture a cylinder in OpenGL created with triangle_strip?如何在使用 triangle_strip 创建的 OpenGL 中对圆柱体进行纹理处理?
【发布时间】:2014-12-19 14:55:15
【问题描述】:

这是我创建的圆柱体:

void drawCylinder(float r, float g, float b) {
    setMaterialColors(r, g, b);
    glColor4f(r, g, b, 1.0);
    /* top triangle */
    double i, resolution  = 0.1;
    double height = 1, radius = 0.5;
    glPushMatrix();
    glTranslatef(0, -0.5, 0);
    glBegin(GL_TRIANGLE_FAN);
        glVertex3f(0, height, 0);  /* center */
        for (i = 0; i <= 2 * PI; i += resolution)
            glVertex3f(radius * cos(i), height, radius * sin(i));
    glEnd();

    /* bottom triangle: note: for is in reverse order */
    glBegin(GL_TRIANGLE_FAN);
        glVertex3f(0, 0, 0);  /* center */
        for (i = 2 * PI; i >= 0; i -= resolution)
            glVertex3f(radius * cos(i), 0, radius * sin(i));
        /* close the loop back to 0 degrees */
        glVertex3f(radius, height, 0);
    glEnd();

    /* middle tube */
    glBegin(GL_QUAD_STRIP);
        for (i = 0; i <= 2 * PI; i += resolution)
        {
            glVertex3f(radius * cos(i), 0, radius * sin(i));
            glVertex3f(radius * cos(i), height, radius * sin(i));
        }
        /* close the loop back to zero degrees */
        glVertex3f(radius, 0, 0);
        glVertex3f(radius, height, 0);
    glEnd();
    glPopMatrix();
}

现在,我无法将纹理映射到它。我尝试保持纹理坐标与三角形条带坐标相同,但没有奏效。

PS:我不想使用 gluCylinder。我知道它存在。


编辑:如果我只能对磁盘进行纹理处理就可以了。四边形不需要纹理。

【问题讨论】:

    标签: c++ opengl opengl-es texture-mapping


    【解决方案1】:

    对于中心:将i 除以2 * PI 得到一个在0.01.0 之间变化的数字,并将其用作纹理坐标:

    for (i = 0; i <= 2 * PI; i += resolution)
    {
        const float tc = ( i / (float)( 2 * PI ) );
        glTexCoord2f( tc, 0.0 );
        glVertex3f(radius * cos(i), 0, radius * sin(i));
        glTexCoord2f( tc, 1.0 );
        glVertex3f(radius * cos(i), height, radius * sin(i));
    }
    
    /* close the loop back to zero degrees */
    glTexCoord2f( 0.0, 0.0 );
    glVertex3f(radius, 0, 0);
    glTexCoord2f( 0.0, 1.0 );
    glVertex3f(radius, height, 0);
    

    对于端盖:您可以直接使用sin()/cos() 输出(几乎):

    glTexCoord2f( 0.5, 0.5 );
    glVertex3f(0, height, 0);  /* center */
    for (i = 0; i <= 2 * PI; i += resolution)
    {
        // scale sin/cos range (-1 to 1) by 0.5 to get -0.5 to 0.5
        // then shift that range up/right by 0.5 to get 0 to 1:
        glTexCoord2f( 0.5f * cos(i) + 0.5f, 0.5f * sin(i) + 0.5f );
        glVertex3f(radius * cos(i), height, radius * sin(i));
    }
    

    完整示例:

    #include <GL/glew.h>
    #include <GL/glut.h>
    #include <cmath>
    
    void cylinder()
    {
        const double PI = 3.14159;
    
        /* top triangle */
        double i, resolution  = 0.1;
        double height = 1;
        double radius = 0.5;
    
        glPushMatrix();
        glTranslatef(0, -0.5, 0);
    
        glBegin(GL_TRIANGLE_FAN);
            glTexCoord2f( 0.5, 0.5 );
            glVertex3f(0, height, 0);  /* center */
            for (i = 2 * PI; i >= 0; i -= resolution)
    
            {
                glTexCoord2f( 0.5f * cos(i) + 0.5f, 0.5f * sin(i) + 0.5f );
                glVertex3f(radius * cos(i), height, radius * sin(i));
            }
            /* close the loop back to 0 degrees */
            glTexCoord2f( 0.5, 0.5 );
            glVertex3f(radius, height, 0);
        glEnd();
    
        /* bottom triangle: note: for is in reverse order */
        glBegin(GL_TRIANGLE_FAN);
            glTexCoord2f( 0.5, 0.5 );
            glVertex3f(0, 0, 0);  /* center */
            for (i = 0; i <= 2 * PI; i += resolution)
            {
                glTexCoord2f( 0.5f * cos(i) + 0.5f, 0.5f * sin(i) + 0.5f );
                glVertex3f(radius * cos(i), 0, radius * sin(i));
            }
        glEnd();
    
        /* middle tube */
        glBegin(GL_QUAD_STRIP);
            for (i = 0; i <= 2 * PI; i += resolution)
            {
                const float tc = ( i / (float)( 2 * PI ) );
                glTexCoord2f( tc, 0.0 );
                glVertex3f(radius * cos(i), 0, radius * sin(i));
                glTexCoord2f( tc, 1.0 );
                glVertex3f(radius * cos(i), height, radius * sin(i));
            }
            /* close the loop back to zero degrees */
            glTexCoord2f( 0.0, 0.0 );
            glVertex3f(radius, 0, 0);
            glTexCoord2f( 0.0, 1.0 );
            glVertex3f(radius, height, 0);
        glEnd();
    
        glPopMatrix();
    }
    
    GLuint tex;
    void init()
    {
        unsigned char data[] =
        {
            128, 128, 128, 255,
            255, 0, 0, 255,
            0, 255, 0, 255,
            0, 0, 255, 255,
        };
    
        glGenTextures( 1, &tex );
        glBindTexture( GL_TEXTURE_2D, tex );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
        glTexImage2D( GL_TEXTURE_2D, 0,GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
    }
    
    float angle = 0;
    void timer( int value )
    {
        angle += 6;
        glutPostRedisplay();
        glutTimerFunc( 16, timer, 0 );
    }
    
    void display()
    {
        glClearColor( 0, 0, 0, 1 );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective( 60, 1.0, 0.1, 100.0 );
    
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
        glTranslatef( 0, 0, -5 );
    
        glEnable( GL_CULL_FACE );
        glEnable( GL_DEPTH_TEST );
    
        glRotatef( angle, 0.2, 0.3, 0.1 );
    
        glEnable( GL_TEXTURE_2D );
        glBindTexture( GL_TEXTURE_2D, tex );
        cylinder();
    
        glutSwapBuffers();
    }
    
    int main(int argc, char **argv)
    {
        glutInit( &argc, argv );
        glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
        glutInitWindowSize( 600, 600 );
        glutCreateWindow( "GLUT" );
        init();
        glutDisplayFunc( display );
        glutTimerFunc( 0, timer, 0 );
        glutMainLoop();
        return 0;
    }
    

    【讨论】:

    • 这样的? glTexCoord2f(i-1/2*PI, i/2*PI);
    • 同理,它只出现在一个象限中。
    • Here's 使用当前版本的样子。我认为它将其中一个顶点映射到中心。
    • 我搞砸了中心点,它们应该在 (0.5, 0.5),而不是 (0,0)。已编辑。
    【解决方案2】:

    看起来您有一个实用的解决方案。但是与原始代码相比,我认为还有一些值得改进的地方,所以我仍然想提供一个替代方案。它解决了:

    • 非常低效,使用大量的sin/cos 调用以及除法,这些都相当昂贵。下面的代码使用了一个sin、一个cos,以及几个可以在编译时评估的分区。剩下的就是所有加法和乘法,它们的运算速度要快得多。
    • 原始代码使用浮点值进行循环控制。由于浮点值本质上是不精确的,因此这或多或少会“随机”导致一次或多或少的迭代,具体取决于四舍五入的结果。
    • 原始代码混合使用了floatdoubleint 类型。 float 适用于坐标,无需使用较慢的双精度,还需要将昂贵的类型转换回 float

    代码(未经测试,但希望足够接近以传达建议的方法):

    // Number of segments a circle is divided into.
    const unsigned DIV_COUNT = 32;
    
    // Calculate angle increment from point to point, and its cos/sin.
    float angInc = 2.0f * M_PI / static_cast<float>(DIV_COUNT);
    float cosInc = cos(angInc);
    float sinInc = sin(angInc);
    
    // Draw top cap.
    glBegin(GL_TRIANGLE_FAN);
    
    glTexCoord2f(0.5f, 0.5f);
    glVertex3f(0.0f, height, 0.0f);
    
    glTexCoord2f(1.0f, 0.5f);
    glVertex3f(radius, height, 0.0f);
    
    float xc = 1.0f;
    float yc = 0.0f;
    for (unsigned iDiv = 1; iDiv < DIV_COUNT; ++iDiv) {
        float xcNew = cosInc * xc - sinInc * yc;
        yc = sinInc * xc + cosInc * yc;
        xc = xcNew;
        glTexCoord2f(0.5f + 0.5f * xc, 0.5f + 0.5f * yc);
        glVertex3f(radius * xc, height, -radius * yc);
    }
    
    glTexCoord2f(1.0f, 0.5f);
    glVertex3f(radius, height, 0.0f);
    
    glEnd();
    
    // Draw bottom cap.
    glBegin(GL_TRIANGLE_FAN);
    
    glTexCoord2f(0.5f, 0.5f);
    glVertex3f(0.0f, 0.0f, 0.0f);
    
    glTexCoord2f(1.0f, 0.5f);
    glVertex3f(radius, 0.0f, 0.0f);
    
    xc = 1.0f;
    yc = 0.0f;
    for (unsigned iDiv = 1; iDiv < DIV_COUNT; ++iDiv) {
        float xcNew = cosInc * xc - sinInc * yc;
        yc = sinInc * xc + cosInc * yc;
        xc = xcNew;
        glTexCoord2f(0.5f + 0.5f * xc, 0.5f + 0.5f * yc);
        glVertex3f(radius * xc, 0.0f, radius * yc);
    }
    
    glTexCoord2f(1.0f, 0.5f);
    glVertex3f(radius, 0.0f, 0.0f);
    
    glEnd();
    
    float texInc = 1.0f / static_cast<float>(DIV_COUNT);
    
    // Draw cylinder.
    glBegin(GL_TRIANGLE_STRIP);
    
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(radius, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(radius, height, 0.0f);
    
    xc = 1.0f;
    yc = 0.0f;
    float texCoord = 0.0f;
    for (unsigned iDiv = 1; iDiv < DIV_COUNT; ++iDiv) {
        float xcNew = cosInc * xc - sinInc * yc;
        yc = sinInc * xc + cosInc * yc;
        xc = xcNew;
        texCoord += texInc;
        glTexCoord2f(texCoord, 0.0f);
        glVertex3f(radius * xc, 0.0f, radius * yc);
        glTexCoord2f(texCoord, 1.0f);
        glVertex3f(radius * xc, height, radius * yc);
    }
    
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(radius, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(radius, height, 0.0f);
    
    glEnd();
    

    如果您有兴趣转向现代 OpenGL,我对这个问题的回答显示了如何使用 VBO 绘制圆:How to draw a circle using VBO in ES2.0

    【讨论】:

      猜你喜欢
      • 2021-02-14
      • 1970-01-01
      • 2012-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多