OpenGL

[OpenGL-9] object viewer 구현하기 (1)

zamezzz 2017. 4. 6. 00:39

Object Viewer 구현하기 (1)



안녕하세요.


오늘은 지난시간까지 배운 내용을 바탕으로 간단한 viewer를 함께 구현해봅시다 ㅎㅎ


obj viewer라고 하니 뭔가 어려워보일수도 있는데요.


전혀 새로운 것이 아닌 매우 간단한 프로그램입니다.


object파일 형식으로 된 obj파일을 입력받아 view해주는 간단한 프로그램이에요.


이번 포스팅은 2번에 나뉘어 진행하고자 합니다.


첫 번째는 obj란 무엇인지와 프로그램의 목차, 그리고 view하는 것에 대한 것이고요.


두 번째는 viewer에 띄운 오브젝트를 좀 더 이쁘게(?) 만드는 것에 대한 내용입니다


그럼 함께 시작해보겠습니다.


1. Obj File Format이란


obj는 가장 오래되고, 기본적으로 사용되는 3차원 모델 표현 파일입니다.


이는 3차원 오브젝트의 좌표를 기록하고, 각 점의 정보를 가지고 있습니다.


앞서 우리가 코드 내에 직접 입력했던 좌표 및 정보를 가지고 있는 하나의


파일이라고 생각하시면됩니다.


유명하고 잘 만들어진 obj파일은 인터넷에서 쉽게 구할 수 있어요.


그렇기때문에 obj파일을 이용한다면 여러 객체에 우리가 만든 효과를 적용시켜 볼 수


있다는 장점이 있습니다.


특히, 3D Max등으로 직접 제작한 객체를 obj file로 export할 수도 있어요.


그렇기 때문에 obj viewer가 있다면, 이러한 모든 객체를 볼 수가 있죠


예를 들어 cube.obj파일을 한 번 볼까요. cube.obj는 다음과 같이 생겼습니다.



여기서 V는 vertex를 의미합니다. 각 v의 순서대로 x,y,z 좌표 값을 뜻합니다.


f는 face(면 = 삼각형)을 의미합니다. 각 순서대로 삼각형을 구성하는 인덱스입니다.


즉, f 1 7 5 는 1번째, 7번째, 5번째 vertex로 구성된 삼각형을 의미합니다.


마찬가지로 다른 obj객체들도 이러한 방식으로 구성되어 있습니다.


2. Obj File 읽기


그렇다면 이러한 Obj 파일을 어떻게 읽어서 우리 프로그램에 보이게 할 수 있을까요


간단한 단 3가지 단계를 거치면, 우리의 화면에서 원하는 객체를 볼 수 있습니다.


1. 전체 vertex 갯수 및 삼각형 갯수를 Count

2. 해당 Count만큼 메모리 할당

3. 할당된 메모리에 각 vertex 및 face정보 입력


매우 간단한 3단계이죠? 이렇게 하면 바로 view가 완성됩니다.


그럼 각 단계별로 간단하게 코드를 살펴보겠습니다.


1. 전체 vertex 갯수 및 삼각형 갯수를 count

char count[100];

int vertexNum = 0;

int faceNum = 0;


while (!feof (s)) {

fscanf (s, "%s", count);  

if (count[0] == 'v' && count[1] =='\0')

vertexNum += 1;

else if (count[0] == 'f' && count[1] =='\0')

faceNum += 1;

memset(count, '\0', sizeof (count));

}


간단하죠? 파일이 끝날때까지 while문을 돌면서 한 줄씩 scanf합니다.


그리고 맨 앞이 v일 경우 vertexNum, f일 경우 faceNumcount합니다.


그리고 다음줄로 넘어갈 때 배열은 매번 초기화를 합니다.


한 번의 while문이 모두 수행된 후에는 두 번째 단계로 넘어갑니다.



2. 해당 Count만큼 메모리 할당

vec4 *vertex;

vec4 *face;


vertex = (vec4 *)malloc (sizeof (vec4) * vertexNum);

face = (vec4 *)malloc (sizeof (vec4) * faceNum);


첫 번째 과정보다 더 간단합니다.


첫 번째 과정에서 count한 만큼 메모리를 동적할당 해줍니다.


3. 할당된 메모리에 각 vertex 및 face정보 입력

while (!feof (s)) {

  fscanf (s, "%s", bind);

  if (bind[0] == 'v' && bind[1] == '\0') {

    fscanf (s, "%f %f %f", 

      &vertex[vertIndex].x, &vertex[vertIndex].y, &vertex[vertIndex].z);


    if(vertex[vertIndex].x > maxX) maxX=vertex[vertIndex].x;

    if(vertex[vertIndex].y > maxY) maxY=vertex[vertIndex].y;

    if(vertex[vertIndex].z > maxZ) maxZ=vertex[vertIndex].z;


    if(vertex[vertIndex].x < minX) minX=vertex[vertIndex].x;

    if(vertex[vertIndex].y < minY) minY=vertex[vertIndex].y;

    if(vertex[vertIndex].z < minZ) minZ=vertex[vertIndex].z;


    sumX += vertex[vertIndex].x;

     sumY += vertex[vertIndex].y;

    sumZ += vertex[vertIndex].z;


    vertIndex ++; 

  }

  else if (bind[0] == 'f' && bind[1] == '\0') {

    fscanf (s, "%f %f %f", 

      &face[faceIndex].x, &face[faceIndex].y, &face[faceIndex].z);

    faceIndex ++;                   

  } 

  avgX = sumX / vertexNum;

  avgY = sumY / vertexNum;

  avgZ = sumZ / vertexNum;

  

  scaleX = (1.0-maxX)*10 + 1;

  scaleY = (1.0-maxY)*10 + 1;

  scaleZ = (1.0-maxZ)*10 + 1;

  if(scaleX > scaleY) {

    if(scaleY > scaleZ)

      scaleAll = scaleZ;

    else

      scaleAll = scaleY;

  }

  else if(scaleX <scaleY) {

    if(scaleX < scaleZ)

      scaleAll = scaleX;

    else

      scaleAll = scaleZ;

  }

}


세 번째 과정은 좀 복잡해보이나요?


하지만 핵심은 검정색으로 굵게 표기된 부분입니다.


나머지는 좀 더 이쁘게하기 위한 작업들이죠!


색깔별로 설명해보겠습니다.


검정색


먼저 1, 2단계를 통해 메모리 할당까지 완료하였습니다.


이제 단순히 할당된 메모리에 값을 입력하는 것입니다. 단순히 조건문을 통해 값을 입력!


v이면 vertexf이면 face에 해당 x,y,z 값을 fscanf를 해줍니다.


■ 초록색


초록색 부분은 뒤에 파란색, 노란색 부분의 코드를 처리하기 위한 부분이라고 생각하세요


간단하게 최대, 최소를 구하고 전체 x,y,z의 합을 구하는 부분입니다.


어떻게 사용되는지는 아래에서 설명할게요.


■ 파란색


파란색 부분은 위에서 구한 sumX, sumY, sumZ를 바탕으로 각 평균값을 구합니다.


이 평균 값은 어디서 사용할까요? 바로 객체의 Position을 정하는데 사용됩니다


obj파일을 통해 읽어오는 각 객체는 크기도 다르고 모양도 다르고 다 다릅니다.


그렇기에 객체를 최대한 화면의 중앙에 위치시키기 위해서 위의 값을 사용합니다.


mat4 translateCenter = Translate(-avgX, -avgY, -avgZ);


위와 같은 형식으로 사용됩니다.


■ 노란색


마지막 노란색 부분입니다. 노란색 부분은 객체의 크기를 조절하는데 사용됩니다.


위에서 설명한 바와 같이 어떤 객체는 너무 크고, 어떤 객체는 너무 작아요.


그렇기 때문에 모든 객체가 적당히 한 화면에 뿌려지도록 스케일링을 해줍니다.


scaleX, scaleY, scaleZ 값을 각각 구하고, 가장 작은 값으로 모두 scale해줍니다.



이렇게 obj파일의 로드가 모두 끝났습니다.


위에서 로드한 정보를 활용해 단순히 view하면 모든 객체를 볼 수 있습니다.


다음 포스팅에서는 이렇게 띄운 객체를 더욱 이쁘게 바꾸어 보겠습니다.


지금까지 포스팅한 내용을 바탕으로 조면 및 surface 등등을 조정해볼까합니다.


그럼 이만 오늘은 포스팅을 마칠게요. 긴 글 읽어주셔서 감사합니다. 

반응형

'OpenGL' 카테고리의 다른 글

[OpenGL-11] object Viewer 구현하기 (3)  (0) 2017.05.27
[OpenGL-10] object viewer 구현하기 (2)  (0) 2017.05.17
[OpenGL-8] Lighting  (0) 2017.03.16
[OpenGL-7] Projection Matix  (0) 2016.12.31
[OpenGL-6] Model View Matrix  (2) 2016.11.13