Как текстурировать цилиндр в OpenGL, созданный с помощью треугольника_полосы?

Вот цилиндр, который я создал:

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. Я знаю, что он существует.


РЕДАКТИРОВАТЬ: Это нормально, если я могу только текстурировать диски. Квадратная полоса не нуждается в текстурировании.


person williGo    schedule 23.10.2014    source источник


Ответы (2)


Для центра: разделите i на 2 * PI, чтобы получить число, которое варьируется от 0.0 до 1.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;
}
person genpfault    schedule 23.10.2014
comment
что-то вроде этого? glTexCoord2f(i-1/2*PI, i/2*PI); - person williGo; 24.10.2014
comment
Например, он появляется только в одном квадранте. - person williGo; 24.10.2014
comment
Вот как это выглядит в текущей версии. Я думаю, что это отображение одной из вершин в центр. - person williGo; 24.10.2014
comment
Я испортил центральные точки, они должны быть на (0,5, 0,5), а не (0,0). Отредактировано. - person genpfault; 24.10.2014

Похоже, у вас есть функциональное решение. Но есть еще несколько вещей, которые, по моему мнению, стоит улучшить по сравнению с исходным кодом, поэтому я все же хочу предоставить альтернативу. Он касается:

  • Это очень неэффективно, так как используется много вызовов sin/cos, а также делений, которые довольно дороги. В приведенном ниже коде используется один sin, один cos и пара делений, которые можно оценить во время компиляции. Остальное — все сложения и умножения, которые выполняются намного быстрее.
  • Исходный код использует значения с плавающей запятой для управления циклом. Поскольку значения с плавающей запятой по своей природе неточны, это более или менее «случайно» приведет к одной более или менее итерации, в зависимости от того, как происходит округление.
  • В исходном коде используется дикая смесь типов float, double и int. 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: Как нарисовать круг с помощью VBO в ES2.0.

person Reto Koradi    schedule 24.10.2014