[OpenGL Tutorial] Texture Mapping

사용자 삽입 이미지

이번 장에서는 컴퓨터 그래픽의 꽃이라고 할수있는 텍스쳐 맵핑에 대해서 알아보도록하자. 컴퓨터 그래픽에 있어서 가장 발전한 기술이 바로 이 텍스쳐 맵핑이라고 한다. 텍스쳐 맵핑이 무엇인지 잠깐 설명하도록 하겠다. 둠이라는 게임을 해 보았는가? 벽으로 막힌 건물 안에서 사악한 주인공이 조용이 살고 싶어하는 착한 괴물을 죽이고 다니는 아주 불건전한 겜이다. 여기서 둠에 나오는 벽을 생각해 보자. 그 벽은 원래 그냥 폴리곤(면)에 불과 했다. 그런데 그 면위에 그림을 입혀 마치 진짜 벽처럼 보이게 한 것이다. 바로 그림을 폴리곤에 입히는 것을 텍스쳐 맵핑이라고 한다. 그 둠이라는 게임에서 사용한 텍스쳐 맵핑 기술은 아주 원시적인 기술이다. 둠의 텍스쳐 맵핑은 단지 평평한 면에만 그림을 입힐수있다. 하지만 OpenGL에서는 곡면에도 그림을 입힐수있다.

대강 텍스쳐 맵핑에 대한 소개는 이쯤에서 접고 OpenGL에서 텍스쳐 맵핑 가운데 2차원 텍스쳐 맵핑에 대해서 알아보도록 하자. 2차원 텍스쳐 맵핑을 설명하기 위해 필자가 준비한 실습 예제는 우리가 지난장에서 만든 정육면체의 각면들에 세가지 그림을 입히는 것, 즉 그림(텍스쳐)를 맵핑하는 것으로 하겠다. 시작에 앞서 그 결과를 먼저 보여주겠다.

사용자 삽입 이미지

그 결과를 살며 보았으면 이제 어떻게 이런 결과를 얻었는지의 그 과정을 설명하기 위해 5장에서 사용한 소스를 기본으로 해서 제작해도록 하겠다. 자! 5장의 소스를 비주얼씨++로 열어 보기 바란다.

텍스쳐 맵핑을 하기 위해 준비해야될 것들은 무엇인가? 바로 텍스쳐, 즉 그림이다. 이 장에서는 3개의 그림이 필요하겠다. OpenGL에서는 간단하게 그림 파일(BMP)을 바로 텍스쳐 맵핑 소스로 사용할 수 없다. 이 그림 파일을 텍스쳐 맵핑 소스로 변환해 주는 루틴이 필요한다. 다음이 바로 그 루틴이다.

AUX_RGBImageRec *LoadBMPFile(char *filename)
{
    FILE *hFile = NULL;
    if(!filename) return NULL;
   
    hFile = fopen(filename, "r");
    if(hFile) {
        fclose(hFile);
        return auxDIBImageLoad(filename);
    }
   
    return NULL;
}

AUX_RGBImageRec 구조체는 AUX 라이브러리의 함수로 BMP 파일의 크기와 그림 데이타 포인터를 가지고 있다. 위의 함수는 파일 이름 인자를 가지는데 그 인자에 해당하는 것이 BMP 파일이다. 중요한 함수는 auxDIBImageLoad 함수이고 나머지 모든 루틴은 파일이 존재하는지을 참조하기 위함이다. 모든 오류에 있어서 NULL 값을 반환한다.

이제 그림 파일에서 맵핑 소스를 읽어 들이는 루틴이 완성되었다. 그렇다면 텍스쳐 맵핑을 OpenGL에서 사용할수있도록 Setting을 해줘야 하는데 그에 해당되는 코드들을 살펴보자.

AUX_RGBImageRec *texRec[3]; //<1>
memset(texRec, 0, sizeof(void *)*3); //<2>

if((texRec[0]=LoadBMPFile("img.bmp")) && //<3>
    (texRec[1]=LoadBMPFile("img2.bmp")) &&
    (texRec[2]=LoadBMPFile("img3.bmp"))) {
    glGenTextures(3, &tex[0]); //<4>
    for(int i=0; i<3; i++) { //<5>
        glBindTexture(GL_TEXTURE_2D, tex[i]); //<6>
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //<7>
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //<8>
        glTexImage2D(GL_TEXTURE_2D, 0, 3, texRec[i]->sizeX, texRec[i]->sizeY, 0, 
                 GL_RGB, GL_UNSIGNED_BYTE,texRec[i]->data); //<9>
    }
} else return FALSE;

for(int i=0; i<3; i++) { //<10>
    if(texRec[i]) {
        if(texRec[i]->data) free(texRec[i]->data);
        free(texRec[i]);
    } else return FALSE;
}

glEnable(GL_TEXTURE_2D); //<11>
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //<12>

자, 하나 하나 살펴 보도록하자.

<1> 코드는 우리가 3개의 텍스쳐 맵핑 소스를 만들어야 하기 때문에 3개의 Cell을 갖는 배열을 선언한 것이다. AUX_RGBImageRec 구조체 타입은 이미 앞에서 살펴보았는데 바로 그림 파일에서 맵핑 소스를 얻기 위해 사용할 함수의 반환값을 저장할 목적으로 쓰인다.

<2> 코드는 메모리 상의 변수를 0으로 만드는 코드인데 굳이 필요치 않는 코드이나 깔끔을 떠는 프로그래머들이 즐겨 사용하는 스타일로 어떤 변수를 사용하기에 앞서 0으로 초기화를 시켜본 경험이 있는 독자도 있을 것이다. 바로 그것이다.

<3> 코드는 IF 문인데 앞에서 살펴본 그림파일에서 텍스쳐 맵핑 소스를 생성하는 함수를 사용해서 각각의 3가지 그림 파일을 실제 읽어 들이는 루틴이다. 오류(파일이 없다)가 발생하지 않는 다면 실행점이 IF문의 TRUE 블럭으로 진입한다.

<4> 코드에서 새로운 함수가 등장했다. 앞으로 OpenGL의 새로운 함수가 많이 등장할 것이다. glGenTextures 함수는 2개의 인자를 갖는데 첫째는 우리가 몇개의 텍스쳐 맵핑 소스를 생성할 것인지를 지정하는 것이고 두번째는 그중 가장 첫번째 텍스쳐 맵핑 소스의 식별자의 주소값이다. 여기서 텍스쳐 맵핑 소스의 식별자라 함은 일단 텍스쳐 맵핑 소스가 생성되었으면 나중에 사용하기 위해서 그 맵핑 소스에 이름을 붙이게 되는데 그 이름으로써 정수값을 사용하며 그 정수값을 식별자라 부른다. 그런데 뜸금없이 tex라는 변수가 튀어 나왔는데 이 변수는 전역 변수 선언부에 다음과 같이 얌전하게 선언되어 있다.

GLuint tex[3];

3개의 텍스쳐를 사용할 것이므로 역시 3개의 Cell을 갖는 배열을 선언하였다.

<5>번 코드는 FOR 문인데 3번 반복하게 되어 있는데 한번의 반복당 하나의 텍스쳐 맵핑 소스에 대한 특성을 지정하게 된다.

<6>코드에 나오는 함수인 glBindTexture는 앞으로 설정할 텍스쳐의 식별자를 지정하는 것으로 첫번째 인자는 텍스쳐가 1차원(GL_TEXTURE_1D)인지, 2차원(GL_TEXTURE_2D)인지를 지정한다. 두번째 인자는 다음에 설정할 텍스쳐 맵핑의 식별자가 온다. 이 함수 이후로 이루어지는 텍스쳐 맵핑의 특성에 대한 설정은 모두 앞서 지정된 식별자에 해당하는 텍스쳐 맵핑에 대한 것이다.

<7>, <8>번 코드를 설명하기 전에 먼저 알아야 할 것이 있다. 실제 텍스쳐 맵핑의 크기와 맵핑되어질 물체의 크기가 똑같다면 간단하게 맵핑을 시키면 되겠으니 작거나 크다면 어떤 식으로 텍스쳐 맵핑 소스를 물체의 크기에 맞게 줄이거나 키울 것인가? 바로 그 어떻게 줄이거나 키울 것에 대한 방법으로 대표적으로 2가지가 있는데 하나는 보간법(GL_LINEAR)과 최근점법(GL_NEAREST)가 있다. 이해가 되었다면 glTexParameteri에 대해서 살펴보자. 세개의 인자를 취하는데 첫번째는 1차원인지, 2차원인지를 지정하는 것이고 두번째 인자는 만약 텍스쳐 맵핑 소스가 맵핑되어질 물체보다 클 경우에 어떤 식으로 축소시켜야 하는지를 지정하는 GL_TEXTURE_MIN_FILTER와 텍스쳐 맵핑 소스가 맵핑되어질 물체보다 작을 경우 어떤 식으로 확대시켜야 하는지를 지정하는 GL_TEXTURE_MAG_FILTER가 온다. 세번째 인자는 실제 그 축소, 확대시키는 방법을 기술하는데 대표적으로 GL_LINEAR와 GL_NEAREST가 온다. GL_LENEAR이 GL_NEAREST보다 나은 장면을 얻을 수 있으나 속도가 느리다. 위의 코드에서는 나은 장면을 얻기위해 모든 텍스쳐 맵핑 소스에 대해서 보간법을 사용하였다.


<9>번 코드는 꽤 많은 인자를 요구하는 glTexImage2D라고 하는 놈이다. 모두 9개의 인자를 갖는다. 첫번째는 텍스쳐 이미지가 1차원인지 2차원인지를 지정하는 것이고 두번째 인자는 항상 0으로 지정하게 된다. 사실 이 인자는 Detail Level을 나타내는 놈으로 보다 나은 Midmap 텍스쳐 맵핑을 구현할때 사용하는 것이다. 이장에서는 사용법에 대해 언급하지 않겠지만 간략하게 소개하자면 앞에서 우리는 텍스쳐 맵핑 소스가 물체보다 크거나 작을때 맵핑 소스를 줄이거나 키운다고 했다. 하지만 Midmap 텍스쳐 맵핑은 처음부터 미리 큰 맵핑 소스와 그리고 중간 맵핑 소스, 작은 맵핑 소스등 많은 맵핑 소스를 준비해서 보다 나은 장면을 얻을 수 있는데 이런 Midmap 맵핑 소스 이미지들은 프로그래머가 처음부터 그림 파일로 만들어 놓고 지정하는 경우와 컴퓨터가 자동으로 생성하는 경우가 있다. 이제 계속 인자에 대해 알아보자. 세번째 인자는 생상 구성 요소의 갯수인데 BMP 파일은 RGB의 세가지 색상요소가 있으므로 3이 온다. 네번재와 다섯번째는 이미지의 가로 길이와 세로 길이를 나타내게 되고 여섯번째는 이미지의 경계에 대한 픽셀두깨를 지정하게 된다. 이 예제에서 사용하는 그림들은 모두 불필요한 경계가 없으므로 0이 된다. 일곱번째는 텍스쳐 맵핑 소스의 구성 요소를 분명하게 지정하는 것으로 GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_RGB 등 여러 값이 올수있으나 여기서는 GL_RGB로 이미 우리는 그림파일에서 텍스쳐 맵핑을 준비하는 것이고 그 그림파일은 RGB값을 갖는 구조이기 때문이다. 여덜번째는 색상 구성 요소들이 갖는 값의 타입을 나타내는데 모든 BMP파일에 있어서 RGB의 세가지 구성요소들은 부호 없는 Byte형이므로 GL_UNSIGNED_BYTE가 온다. 마지막으로 아홉번째는 텍스쳐 맵핑 소스의 실제 데이타값(그림 데이타)에 대한 포인터가 온다.

여기까지가 텍스쳐 맵핑 소스를 생성하고 그 맵핑 소스에 대한 특성을 설정하는 것들이다. 이후로 이제 불필요한 할당된 메모리를 해제하고 텍스쳐 맵핑을 사용한다고 지정하는 것들이 남았다.

<10>번 코드가 바로 이제는 더이상 필요치 않는 할당된 메모리를 해제하는 코드이다.

<11>은 2차원 텍스쳐 맵핑을 사용하겠노라고 OpenGL에게 알린다.

<12>에 나오는 함수인 glTexEnvi는 세개의 인자를 갖는데 중요한 것은 세번째 인자로써 올수있는 값은 GL_MODULATE와 GL_BLEND와 GL_DECAL이 올수있다. GL_MODULATE는 화면상의 픽셀 색과 텍스춰의 픽셀 색을 서로 곱하게 된다. <12>번 코드를 생략하면 자동으로 GL_MODULATE로 지정된다. GL_DECAL은 화면상의 픽셀 색을 텍스춰의 픽셀 색으로 대체한다. 이 경우 빛에 대해서 전혀 텍스쳐가 영향을 받지 않는다. GL_BLEND는 화면상의 픽셀 색을 텍스쳐의 픽색 색과 섞고 특정한 색과 결합시키게 된다.

자!! 이렇게 해서 텍스쳐 맵핑 소스를 만드는 것에 대한 것들을 알아 보았다. 그렇다면 이 코드들은 어디에 위치해야 하는가? 간단하게 OpenGL을 초기화는 곳에 위치하도록 하자. 즉 void InitGL(GLvoid) 함수에 위치 시키자.

이제 우리는 이 텍스쳐 맵핑 소스를 물체에 적용하는 방법에 대해 알아볼 차례이다. 적용은 실제 물체를 그리는 코드에서 이루어진다. 다음의 DrawGLScene 함수를들어다 보자.

int DrawGLScene(GLvoid)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
   
    glTranslatef(0.0f, 0.0f, -7.0f);
   
    glColor3f(0.8f, 0.8f, 0.8f);
   
    glRotatef(rot, 1.0f, 1.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, tex[0]); //<1>
    glBegin(GL_QUADS);
    glNormal3f(0.0f, 0.0f, 1.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f); 
    glEnd();
   
    glBindTexture(GL_TEXTURE_2D, tex[1]); //<1>
    glBegin(GL_QUADS);
    glNormal3f(1.0f, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -1.0f);
    glEnd();
   
            .
            .
            .
   
    glBindTexture(GL_TEXTURE_2D, tex[0]); //<1>
    glBegin(GL_QUADS);
    glNormal3f(0.0f, 1.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -1.0f);
    glEnd();
   
    rot+=1.0f;
    if(rot>359.0f) rot=0.0f;
   
    return TRUE;
}

5장에서의 정육면체를 그릴때의 방법을 약간 변형했는데 5장에서는 한번의 glBegin과 glEnd를 사용하여 모든 면을 그려주었다. 하지만 이 장에서는 각각의 면에 대해서 glBegin와 glEnd를 사용하여 그려주었다. 그 이유는 각 면에 대해서 서로 다른 텍스쳐 맵핑을 시켜야 하기 때문이다. 살펴 볼것은 면에 대해서 어떤 텍스쳐 맵핑을 사용할 것인지는 앞서 언급한 텍스쳐 맵핑 식별자를 사용하는데 <1>번 코드가 바로 그것이다. 그리고 실제 텍스쳐 맵핑을 지정하는 것에 대한 것인데 위의 코드를 주의 깊게 살펴보면 면에 대한 각 점들을 지정해 줄때 glTexCoord2f라는 함수를 통해서 텍스쳐 맵핑 좌표를 같이 지정해 주었다는 점이다. 이것은 무엇을 의미하는가? 텍스쳐 맵핑의 소스는 크기에 상관없는 S-T 자표계를 사용하는데 S-T좌표계라는 시작점은 0으로 하고 끝점은 1로 한다는 점이다. 아래의 그림과 같이 말이다.

사용자 삽입 이미지

즉 glTexCoord2f를 이용하여 glVertex3f로 지정한 점에 대해 텍스쳐 맵핑 좌표를 설정해 줄때 glVertex3f로 지정한 위치에 대해 glTexCoord2f 좌표가 서로 일치시켜 그림을 맵핑하게 되는 것이다.

각각의 면들에 대해서 동일한 방법으로 텍스쳐 맵핑 좌표를 설정하였다.

이로써 텍스쳐 맵핑에 대한 것들을 마칠까 한다.

“[OpenGL Tutorial] Texture Mapping”에 대한 51개의 댓글

  1. 정말 많은걸 배우고 갑니다. 덕분에 살았습니다. 그런데 개념이 너무 안잡히는게 있어서 메일 한통 보냈는데…도움 좀 부탁드립니다.ㅠㅠ

  2. 안녕하세요.
    관련 자료를 검색하다 방문하게되었습니다.
    질문하나만 드릴께요.
    위와 같이 정육면체에 텍스처를 입힌 후 회전(rotate)를 할 경우
    각 면의 대각선 방향으로 늘어짐 현상이 발생하는 것으로 알고있습니다.
    이문제를 해결하기 위한 방법이 있을까요??
    고맙습니다.

    1. 저는 Opengl ES 를 사용하여 구현중이며,
      텍스처 맵핑은
      glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
      를 사용하였습니다.

  3. ABC님, 텍스쳐의 필터링 문제가 아닐까 조심스레 생각이 되는데요. 기본값이 GL_NEAREST인데… 이를 선형 방식(GL_LINEAR)로 변경해 보시길 권해 드립니다. OpenGL에서는 glTexParameteri 함수를 이용해 지정할 수 있습니다. 그리고 혹시 OpenGL ES에서는 삼각형 스트립 원시타입만을 지원하게 되는데, 이때 사각형을 만들기 위해 인접한 두 삼각형의 경계에서 깨진 발생할 수도 있겠다는 추측이 듭니다. 이때 경계부분에 대한 wrapping 방식을 다르게 지정해 보시길 권해드립니다. 역시 glTexParameteri로 지정하고, 두번째 인자를 GL_TEXTURE_WRAP_S, T와 세번째 인자에 대해서 GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_CLAMP 등으로 테스트해 보시기 바랍니다. 아시겠지만, OpenGL에서 원하는 결과를 얻기 위해서는 많은 실험정신을 필요로 하는지라.. 그런 건투를 빕니다.

  4. 저 그림이 깨지는경우는 무슨경우인가요?다른그림은 안깨지는데 이그림만 깨지네요 이런경우는 무슨경우인지좀 알려주세요

    1. 텍스쳐 이미지가 깨지는 경우는 대부분 이미지의 크기값이 2의 자승이 아닌 경우에 그렇습니다. 이미지의 크기를 확인해보세요~

    1. 글 마다 댓글을 남겨주셔서 정말 감사드립니다. 하나 하나 답변해 드려야하지만.. 이 글을 대표로 해서 감사의 댓글을 대신합니다~

  5. 안녕하세요. 좋은 자료 잘 보고있습니다. 아이폰에서 3D 메뉴를 구현해보려고 하는데 질문이 있습니다. 같은 크기의 네모 여러개를 3D공간에서 원형으로 배치하려면 어떤식으로 해야되는지 힌트좀 얻을 수 있을까요? 혹시 도움될만한 글이나 아이디어를 좀 알려주시면 감사하겠습니다.

    1. 안녕하세요~ ^^
      일단 원하는 위치에 오브젝트를 놓는 것에 대해 알아야 합니다. 이건 http://www.gisdeveloper.co.kr/437 를 통해 살펴보시구요. 그 다음에는 원형 배치라고 했으니 원형 배치에 대한 각 위치를 계산해야 합니다. 이 것은 간단히 cos/sin 함수와 각도 0~360까지를 사용해 얻을 수 있습니다. 그럼 건투를 빕니다.

  6. OpenGL 처음 시작하는데 정말 많은 도움되고 있습니다! 좋은자료 감사합니다 ^^ 새해복 많이받으세요!

  7. 텍스쳐 매핑에 대해 어렴풋하게만 알고 있었는데, 이번 글을 통해 확실히 알고 갑니다. 감사합니다. 🙂

    1. 네, 질문에 신경쓰고 답변하다가 정작 중요한 감사의 댓글을 이제서야 달게 되었습니다. 감사합니다!

  8. 정말 잘보고 갑니다..기본 텍스쳐에 정리가 잘 되어 있네요 ~ 헌데 제가 원기둥을 cylinder 함수를 이용해서 그렸는데 그 경우 텍스쳐를 입힐려면 어떻게 해야 할까요 ㅠ. 찾아봐도 잘 나오질 않네요..

    1. 원기둥을 직접 Triangle로 직접 구성하는 경우라면.. 텍스쳐 맵핑 좌표 역시 직접 구성해 주셔야 합니다. 삼각형으로 직접 구성할 정도라면 텍스쳐 맵핑 좌표 역시 직접 지정해 주실 정도의 스킬은 되시리라 생각됩니다. 직접 구성하기가 어렵다면… 오픈GL에서 제공하는 Quadrics를 이용해 보시기 바랍니다.. 간단하게 API를 이용해 실린더를 구성할 수 있고 텍스쳐 좌표까지 지정할 수 있습니다.

  9. 증강현실을 ARToolkit 없이 만들어보려는대요
    OpenCV로
    IplImage* frame = cvCaptureFromCAM(0);
    으로 받아온 실시간 카메라 영상을 이 예제의 OpenGL 배경으로 사용할 수는 없나요???
    몇날 몇일을 시도해봐도…
    지식이 부족해서 해결이 되질 않네요 T^T
    답변에 미리 감사합니다

    1. IplImage를 통해 텍스쳐 맵핑을 하시면됩니다.
      전체 화면을 덮는 사각형 매쉬를 올려두고..
      이 매쉬에 IplImage의 이미지를 맵핑하시면 원하시는 결과를 얻으실 수 있습니다.

  10. 답변 감사합니다.
    그런데 지식이 매우 얕아서… 잘 되질 않네요 ^^;;;;;;;;;;;;;…….
    저는 강의에 나와 있는 부분을 활용하여 대충 이런 식으로 바꾸었었습니다.

    GLuint *texture; // 전역변수
    int InitGL(GLvoid)// 이 부분을 바꿔보았습니다. 하지만.. 실패했습니다.. T^T
    {
    ///////// New Codes ///////////////////////////////////////////////
    CvCapture* capture = cvCaptureFromCAM(0); // cam 영상을 받아온 후
    IplImage *image = NULL;
    while(1)
    {
    image = cvQueryFrame(capture);
    glGenTextures(1, text);
    glBindTexture(GL_TEXTURE_2D, *text);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, image->imageData);

    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    char chKey = cvWaitKey(10);

    if(chKey == 27 ) // ESC를 눌렀는지 확인하고 ESC이면 종료하는 부분
    {
    break;
    }
    }
    ////////////////////////////////////////////////////////////////////
    ….
    return TRUE; // Initialization Went OK
    }

    int DrawGLScene(GLvoid)
    {
    glTranslatef(0.0f, 0.0f, -5.0f);
    glColor3f(0.8f, 0.8f, 0.8f);

    glBindTexture(GL_TEXTURE_2D, *texture);
    glBegin(GL_QUADS);
    glNormal3f(0.0f, 0.0f, 1.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -2.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -2.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -2.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -2.0f);
    glEnd();

    return TRUE;
    }

    제가 생각하는 문제점은
    영상은 while(1)인 무한 loop 안에서 계속 해서 IplImage *image에 저장되고 있는데, 제가 작성한 형식은 이 무한 loop를 빠져나가지 않는 이상 OpenGL로 물체를 그릴 수 없다는 것입니다.
    ….
    죄송하고, 무리한 부탁인 줄은 알지만
    혹시 가능하시다면 OpenCV로 받아온 IplImage *image 영상을 OpenGL window의 배경으로, 아니면 texture mapping 하는 코드 좀 작성해주시면 안되겠습니까?
    개인적으로 가르쳐 주시는 게 뭣 하다면 강의로라도… T^T
    제가 code를 바꾸는 걸 보시면 아시겠지만 완전 초보자라서요…
    답변에 미리 감사합니다.

  11. 아, 혹시 메일로 보내주실 수 있으시면… ㅎㅎ;;
    nightskysj@naver.com
    입니다.
    제발 T^T… 염치 없지만…부탁드립니다.
    인터넷을 정말 열심히 뒤져보아도 이 곳 만한 곳이 없어서요 T^T
    감사합니다.

  12. 넵 ㅎㅎ
    저, 김형우님의 강의를 바탕으로 이래저래 해서 cam에서 받아온 영상을 실시간으로 띄우는 건 했습니다. ㅎㅎ 감사합니다.
    그런데 한 가지 문제가 있습니다. T^T
    메모리를 해제해주는 문제인데요.
    메모리를 제가 하는 방식으로 해제를 하려니까 메모리 해제가 반복해서 일어난다며 heap이 손상되었다며 에러메시지가 뜨더라구요.
    이 부분에 대해서 조언 좀 해주시면 안 될까요?? ㅎㅎ;;
    다음은 제가 삽입한 코드입니다.
    int DrawGLScene(GLvoid) // Here’s Where We Do All The Drawing
    {
    ////////// New Codes ///////////////////////////////////////////////
    IplImage *image = 0;
    image = (IplImage*)calloc(1, sizeof(IplImage)); // 동적메모리를 할당한 부분입니다. 이렇게 안 하니까 에러가 또 나더라고요. T^T
    texture = (GLuint*)calloc(1, sizeof(GLuint)); // 여기 또한 마찬가지 입니다.

    if(image = cvQueryFrame(capture))
    {
    glGenTextures(1, texture);
    glBindTexture(GL_TEXTURE_2D, *texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D,
    0,
    GL_RGB, // IplImage를 띄우기 위해서는 이렇게 선언해야 된다더군요. R과 B의 순서가 바뀌어 있어서 그렇다더군요.
    image->width,
    image->height,
    0,
    GL_BGR_EXT, // 여기도 마찬가지 입니다.
    GL_UNSIGNED_BYTE,
    image->imageData);
    } else return FALSE;

    /* if(image) // 여기가 메모리를 해제하려는 부분인데요 이 주석만 풀면 에러가 나더라고요…
    {
    if(image->imageData) free(image->imageData);
    free(image);
    } else return FALSE;
    cvReleaseFrame(&capture);
    free(&texture);
    */

    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    ////////////////////////////////////////////////////////////////////

    }

    부디… 이 부분 좀 해결해주시면 ㅎㅎㅎ;; 안 될까요??? ㅎㅎ
    미리미리 감사합니다.

  13. 안녕하세요?
    자세한 설명, 정말 감사합니다.

    저는 GL_TEXTURE_1D 모드를 사용하고 싶은데요,
    등고선 등을 표현하기에 1D 모드가 좋다고 책에서 봤습니다.

    다음에 시간이 되시면, 1D의 사용예도 함께 포스팅 부탁드리겠습니다.

  14. 안녕하세요 자주 보러오는데 댓글은 처음 남겨보네요…
    죄송하지만 구 텍스처 입힐 때 자동으로 매핑하는 방법말고 다른방법이없을까요?
    볼링핀을 구현하려고하는데 텍스쳐 입혀놓은것이 좌표가 바뀌면서 일그러지는 현상이 일어나더라구요.. 구를 glutSolidSphere로 만들지말고 아예 만들어서 좌표를 설정해야하는 건가요??
    답변좀 부탁드릴게요 ㅜㅜ

    1. 텍스쳐 좌표를 자동으로 지정하도록 하지 않는다면 직접 하나 하나 좌표를 계산하여 지정해줘야 합니다.

  15. loadBMPfile 함수에서
    return auxdib ~ () 문장. () 안에 char* 형태가 안들어간다고 하여
    프로젝트 속성을 유니코드 집합에서 멀티바이트 집합으로 바꾸어 실행해보았으나
    실행은 되는데 왜 읽어온 파일명을 fopen 하지 못하는지… 모르겠습니다 ..

    도와주세요 ㅜㅜ

    1. 파일을 찾지 못하는 문제같은데요.. 파일 경로를 확인해 보시기 바랍니다. 특히, C언어에서 문자열로 파일 경로를 지정할때 “c:\a.bmp” 는 “c:\\a.bmp”처럼 해주셔야 합니다.

  16. visual studio 2017에서 실행했을 때 코드의 if((texRec[0]=LoadBMPFile(“img.bmp”)) &&
    (texRec[1]=LoadBMPFile(“img2.bmp”)) &&
    (texRec[2]=LoadBMPFile(“img3.bmp”))) 에서 const char * 형식의 인수가 char * 형식의 매개변수와 호환되지 않는다며 오류가 뜨는데 어떻게 해결할 수 있을까요?

    1. 안녕하세요, 김형준입니다.
      OpenGL 쪽에서 AUX 라이브러리가 더 이상 지원되지 않는것으로 알고 있습니다.
      대신 GLUT(The OpenGL Utility Toolkit) 라이브러리에서 비슷한 함수가 있을겁니다.
      오히려 AUX 보다는 GLUT 라이브러리로 접근하시는것이 생각 이상으로 훨씬 더 많고 유용한 자료가 될것입니다.

  17. 너무 원초적인 질문인것 같은데 ㅠㅠ제가 이걸로 애를 먹고있어요 ㅠㅠㅠ img.bmp 파일들을 어디에 저장해놓고 불러오는건지를 모르겠어요…ㅠㅠ 프로젝트 폴더 내의 res 폴더 안에 넣은 후에 (저는 tga 파일이라) load(“img.tga”)이런식으로 한 후에 사용해주었는데 맵핑이 안되네요 ㅠㅠㅠㅠ

    1. 안녕하세요, img.bmp 파일의 위치는 일반적인 파일 위치입니다.
      load 함수 호출시에 이 img.bmp에 대한 적절한 위치로 설정하시면 됩니다.
      예를들어, c:/images라는 디렉토리에 img.bmp 파일이 존재할 경우..
      load(“c://images//img.bmp”); 라고 호출하시면 됩니다.

이영호에 답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다