Engineering Navi - ARToolKit 3. simpleTest.c 설명 (2/2)

 ** 이글은 http://kougaku-navi.net/ARToolKit.html 을 번역, 의역하였습니다.

 

--------------------------------------------------------------------------------------

 

 3-3. 메인루프 함수 mainLoop()

 

static void mainLoop(void)
{
    ARUint8         *dataPtr;      // 영상 데이터
    ARMarkerInfo    *marker_info;  // 검출된 마커의 정보
    int             marker_num;    // 검출된 마커의 수
    int             j, k;

    /* 카메라 영상 취득 */
    if( (dataPtr = (ARUint8 *)arVideoGetImage()) == NULL ) {
        arUtilSleep(2);   // 영상이 찍히지 않았다면,  2밀리 세컨드를 기다려 함수를 벗어난다.

        return;
    }
    if( count == 0 ) arUtilTimerReset();  // 타이머 리셋
    count++;                              // 처리 프레임 수 카운트
                                          // ↑ 이것은 나중에 FPS를 계산할 때, 사용함

    /* 캡쳐한 영상 표시 */
    argDrawMode2D();
    argDispImage( dataPtr, 0,0 );

    /* 카메라 영상으로 부터 마커 검출 */
    if( arDetectMarker(dataPtr, thresh, &marker_info, &marker_num) < 0 ) {
        cleanup(); // 에러가 발생하면 종료처리를 해서 프로그램을 종료함
        exit(0);
    }

    arVideoCapNext();  // 다음 카메라 영상을 취득한다.

    /* 가장 일치도가 높은 마커를 찾는다. */
    k = -1;
    for( j = 0; j < marker_num; j++ ) {
        if( patt_id == marker_info[j].id ) {
            if( k == -1 ) k = j;
            else if( marker_info[k].cf < marker_info[j].cf ) k = j;
        }
    }
    /* 여기서, k번째 마커가 가장 일치도가 높은 마커가 된다. */

    if( k == -1 ) {        // 일치하는 마커를 찾지 못한다면,
        argSwapBuffers();  // 버퍼 내용을 화면에 따라 그리는 것만 한다.
        return;
    }

    /* 마커의 3차원 위치, 자세(마커, 카메라 간의 좌표 변환 행렬)을 구한다. */
    arGetTransMat(&marker_info[k], patt_center, patt_width, patt_trans);

    draw();  // 출력 처리(여기에서 3D 오브젝트의 출력을 하고 있다.)

    argSwapBuffers();  // 버퍼의 내용을 화면에 표시함
}

 

메인루프 함수는 종료처리가 실행될 때까지 계소해서 반복되어 실행됩니다. 마커의 검출을 실행하는

함수 arDetecMarker()는 검출된 마커의 식별ID와 꼭지점, 일치도 등의 정보를 포함한 구조체와

검출된 마커의 수를 반환하지만, 검출된 마커 중에서 가장 높은 일치도를 가진 모양을 찾는 처리는

스스로 코딩하지 않으면 안되는 것에 주의합시다. 여기에서,

 

   marker_info[j].id  // j번째에 검출된 마커에 대응하는 마커 ID
 marker_info[j].cf  // 거기에 얼마나 가까이에 있는지 표시하는 일치도(0.0~1.0의 수치)

 

로 됩니다.

 ARToolKit의 최대의 장치가 arTransMat()라는 함수입니다. 이 함수에는 검출된 마커의 정보에서 부터,

카메라의 시점에 대한 마커의 3차원 위치자세(위치자세라는 단어가 계속 나오는데, 적당한 한국말은

무엇일까요?)를 계산하고 있습니다. 수학적으로 꽤나 엄청난 일을 하고 있기 때문에, 흥미가 있는

분은 공식사이트에서 공개된 논문과 함께 프로그램의 소스를 확인하시는 것도 추천합니다.(꽤 공부가

되니까요.) 이 함수로 부터 얻어진 patt_trans가 좌표변환행렬입니다.

 

 

 

다음으로 이 메인 루프 함수 안에서 호출되고 있는 draw() 함수의 내부를 확인해 봅시다.

 

3-4. 그래픽 출력처리함수 draw()

 

static void draw( void )
{
    double    gl_para[16];
    GLfloat   mat_ambient[]     = {0.0, 0.0, 1.0, 1.0};
    GLfloat   mat_flash[]       = {0.0, 0.0, 1.0, 1.0};
    GLfloat   mat_flash_shiny[] = {50.0};
    GLfloat   light_position[]  = {100.0,-200.0,200.0,0.0};
    GLfloat   ambi[]            = {0.1, 0.1, 0.1, 0.1};
    GLfloat   lightZeroColor[]  = {0.9, 0.9, 0.9, 0.1};
   
    // 3D 공간을 그려내기 위한 설정.
    argDrawMode3D();
    argDraw3dCamera( 0, 0 );


    // 숨겨진 면 처리에 관한 기술
    glClearDepth( 1.0 );
    glClear(GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
   
    /* 좌표변환행렬 로드 */
    argConvGlpara(patt_trans, gl_para);
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixd( gl_para );


    // 라이팅(조명)에 관한 기술
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambi);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_flash);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_flash_shiny);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);

    // 실질적으로 3D 오브젝트를 만들기 위한 것은 이 3줄임!!
    glMatrixMode(GL_MODELVIEW);
    glTranslatef( 0.0, 0.0, 25.0 );  // 마커 위에 올리기 위해 z방면에 25.0mm 띄움
    glutSolidCube(50.0);             // 50.0mm의 정사각형을 그림

    glDisable( GL_LIGHTING );
    glDisable( GL_DEPTH_TEST );
}

  여기가 가장 프로그래머의 손길이 필요한 부분이라고 생각합니다. 이름이 'gl'로 시작하는 함수는 OpenGL

함수입니다. 위의 코드 중에 빨간 색으로된 부분에 주목해 주세요. argDrawMode3D()는 OpenGL의 모델
View 행렬을 초기화하는 함수입니다. argDraw3dCamera()는 3D 공간의 시야범위를 설정하는 함수입니다.

 우선 "3D공간을 출력하기 전에 이 2개를 호출한다"는 이해를 해주시기 바랍니다.

argConvGlpara()라는 함수는 조금 전에 구한 좌표변환행렬 patt_trans를 OpenGL형식(4X4 행렬의 1차원 배열)

으로 변환하고 있습니다.

 

 

 

< argConvGlpara()로 변환된 OpenGL 형식의 변환행렬 >

 

 최초, 원점(0,0,0)은 렌즈의 위치(= 카메라의 위치)에 있지만, glLoadMatrixd()에 따라 좌표변환이 실행되어,

이 함수 이후는 마커의 중심을 원점으로 하는 좌표계로의 작업이 됩니다. 최대 포인트는 OpenGL 위에 길이

단위를 미리미터 단위로 지정할 수 있는가 입니다! 예를 들어, 폭 50.0mm의 정사각형을 출력하고 싶다면,

 그대로 glutSolidCube(50.0) 라고 지정하면 되겠습니다. 다만, 이것을 실현하기 위해서는 마커의 크기가

확실히 설정되어 있어야 하므로, 주의해 주시기 바랍니다.

 

    double          patt_width     = 80.0// 마커의 한 변의 길이[mm]


 

※ patt_width는 소스 상부에 전역으로 선언되어 있습니다.

 

 ! ARToolKit의 그래픽 처리

 

   ARToolKit에 따르면, 카메라 영상을 표시하거나, 사영기하의 설정을 실행하는 처리계에는 'gsub'와

'gsub_lite'가 있습니다. 함수명 이름 앞이 'arg'로 시작하는 것이 gsub의 함수로, 'argl'로 시작하는 함수는

gsub_lite의  함수입니다.

 

- gsub의 함수 레퍼런스(영문))
- gsub_lite의 함수 레퍼런스(영문)

 

 gsub에는 GLUT에 따른 윈도우 처리, 콜백 함수 관리가 포함되어있지만, gsub_lite에는 포함되어 있지

않습니다. 단적으로 말하면, gsub_lite의 경우는 윈도우와 콜백 함수의 관리를 스스로 실장(実装:어떤

기능을 구현하기위해 구성요소를 구체화하는 일(구현하는 작업))하는 것이 가능해지지만, 이미 OpenGL/

GLUT에 익숙해진 독자분은 gsub_lite의 함수를 사용해 코딩하는 쪽이 적합할지도 모르겠습니다. 특히

기존의 OpenGL의 코드를 AR화 하려는 경우는 gsub_lite 함수로 코딩하는 쪽이 하기 쉽습니다.

 

 gsub을 사용하는 경우는 gsub.h를 include합니다. 한편, gsub_lite를 사용하는 경우는 gsub_lite.h를

include합니다. 여기서 설명하고 있는 simpleTest.c는 gsub를 이용하고 있습니다. gsub_lite를 사용한

샘플은 simpleLite.c 입니다. gsub_lite를 사용하는 프로그램 쪽이 느낌상(雰囲気的に:분위기적으로를 의역했

습니다.) OpenGL의 코드에 가깝습니다.(OpenGL의 코드 위에 ARToolKit의 코드를 실고 있는 듯한 느낌임.)

 

 gsub와 gsub_lite의 차이에 관한 내용은 공식 사이트인 아래 페이지에 설명이 있습니다. 요약하자면

"GLUT에 따른 처리가 들어있느냐, 아니냐"라고 해도 틀리지 않을 것 입니다.

 

- ARToolKit Documentation (ARToolKit Framework)

 

 3-5. 키 이벤트 함수 keyEvent()

 

static void   keyEvent( unsigned char key, int x, int y)
{
    /* ESC키가 눌리면 종료한다. */
    if( key == 0x1b ) {
        printf("*** %f (frame/sec)\n", (double)count/arUtilTimer()); // FPS 표시
        cleanup(); // 종료처리
        exit(0);
    }
}

 키 이벤트 함수는 프로그램의 초기화 시에 argMainLoop()에서 설정합니다. 그렇게 하면, 실행주에 키 입력이

있을 때에 이 함수가 호출되는 것이 가능해집니다.

 변수 key에는 눌러진 키의 키 코드가 들어가 있습니다. 이 프로그램에는 esc키가 입력되면, FPS를 표시하는

프로그램을 종료하도록 되어 있습니다. 변수 count에는 처리한 프레임 수가 들어가 있습니다. 이것을 시간(

arUtilTimer())에 나누면 1초당 처리 프레임 수(FPS)가 구해집니다.

 다음은 여기서 사용되는 cleanoup() 함수의 내부를 봅시다.

 

 3-6. 종료처리함수 cleanup()

 

 아주 심플합니다. "캡쳐를 멈추면 종료" 그것 뿐인가 봅니다. 프로그램의 처리 도중에 에러가 발생하는 경우는

이 함수를 실행한 후에 종료(exit(0) 한다)하도록 해주세요.

 

 이 샘플 프로그램의 구조가 이해된다면, 스스로 개인만의 AR 어플리케이션을 만들 수 있을 것 입니다.

 

-------------------------------------------------------------------------------------------------