Skip to content

Commit

Permalink
Complete Tutorial 5
Browse files Browse the repository at this point in the history
  • Loading branch information
ffdd270 authored and Calvin1602 committed Nov 2, 2018
1 parent 209be6e commit 72d9efb
Showing 1 changed file with 37 additions and 62 deletions.
99 changes: 37 additions & 62 deletions kr/beginners-tutorials/tutorial-7-model-loading/index.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,15 @@ tags: []
language: kr
---

지금까지, 우리는 소스코드에 큐브를 하드코딩했어요. 그게 꽤 끔찍하고, 그리 괜찮은 일이 아니라는 것이라는 걸 여러분도 이해했으리라 믿어요.
Until now, we hardcoded our cube directly in the source code. I'm sure you will agree that this was cumbersome and not very handy.
지금까지, 우리는 소스코드에 큐브를 하드코딩했어요. 그게 꽤 끔찍하고, 그리 괜찮은 일이 아니라는 것이라는 걸 여러분도 느꼈다고 믿고 있어요.

이번 튜토리얼에서는 파일에서 3D 메쉬를 불러오는 법을 배울거에요. 텍스처 로딩에서 했던 것처럼 : 아주 제한적이고, 아주 작은 로더를 만들 거고. 실제 라이브러리들은 우리보다 괜찮을 거라고 확신할게요. 그러니까 나중에는 그 라이브러리 쓰는 방법도 알려줄게요.
In this tutorial we will learn how to load 3D meshes from files. We will do this just like we did for the textures : we will write a tiny, very limited loader, and I'll give you some pointers to actual libraries that can do this better that us.
이번 튜토리얼에서는 파일에서 3D 메쉬를 불러오는 법을 배울거에요. 텍스처 로딩에서 했던 것처럼 : 아주 제한적이고, 아주 작은 로더를 만들 거고- 그래서 실제 라이브러리들은 우리보다 괜찮을 거라고 확신해요. 그러니까 나중에는 라이브러리들 소개도 시켜드릴게요.

이 튜토리얼을 가능한 한 심플하게 만들기 위해, 우리는 OBJ 파일 포맷을 사용할 거에요. 아주 간단하고 일반적인 포멧이죠. 그리고 마찬가지 이유로. 우리는 버텍스당 UV 좌표 하나, normal 좌표 하나만을 가지는 OBJ 파일만 불러올 거에요. (normal에 대해선 아직 알 필요가 없어요.)
To keep this tutorial as simple as possible, we'll use the OBJ file format, which is both very simple and very common. And once again, to keep things simple, we will only deal with OBJ files with 1 UV coordinate and 1 normal per vertex (you don't have to know what a normal is right now).

# OBJ 로딩하기

우리 함수는, 코드는 common/objloader.cpp에. 정의는 common/objloader.hpp에 있을거에요 - 아래 시그네쳐에요. :
Our function, located in common/objloader.cpp and declared in common/objloader.hpp, will have the following signature :

``` cpp
bool loadOBJ(
Expand All @@ -34,16 +30,13 @@ bool loadOBJ(
)
```

우리는 loadOBJ가 파일 "path(경로)" 를 읽기 원하고, 그 경로에서 out_vertices/out_uvs/out_normals등의 데이터를 쓰기를 원할거고. 뭐가 잘못되었으면 false 값을 돌려줬으면 좋겠어요. std::vector 는 C++에 있는, 언제든지 크기를 수정할수 있는 배열이고. 템플릿이라는 것을 통해 지금은 glm::vec3 자료형을 보관중이에요. : 아. 이거는 수학적인 벡터가 아니에요. 그냥 배열이라니까요? 진짜로요. 마지막으로 알아볼건 & 인데. 이거는 '참조'라고 std::vector를 수정할 수 있다는 말이에요.
우리는 loadOBJ가 파일 "path(경로)" 를 읽기 원하고, 그 경로에서 out_vertices/out_uvs/out_normals등의 데이터를 쓰기를 원할거고. 뭐가 잘못되었으면 false 값을 돌려줬으면 좋겠어요. std::vector 는 C++에 있는 언제든지 크기를 수정할수 있는 배열이고. 템플릿이라는 것을 통해 지금은 glm::vec3 자료형을 보관중이에요. (아. 이거는 수학적인 벡터가 아니에요. 그냥 배열이라니까요? 진짜로요.) 마지막으로 알아볼건 & 인데. 이거는 '참조'라고 std::vector를 수정할 수 있다는 뜻이에요.

(역주 : JAVA등을 하던 사람은 이상하게 생각하겠지만, C++은 기본적으로 '모든' 변수를 '값'만 전달합니다. )

We want loadOBJ to read the file "path", write the data in out_vertices/out_uvs/out_normals, and return false if something went wrong. std::vector is the C++ way to declare an array of glm::vec3 which size can be modified at will: it has nothing to do with a mathematical vector. Just an array, really. And finally, the & means that function will be able to modify the std::vectors.
(역주 : JAVA등을 하던 사람은 이상하게 생각하겠지만, C++은 기본적으로 *모든* 변수를 ****만 전달합니다. 참조형만이 순수한 call-by-reference입니다. 다만 참조형은 compile 시점에 결정되기에, 가리키는 대상을 바꿀 수 없음을 유의하세요. 바꾸고 싶으시다면, 포인터를 사용하세요.)

## 예제 OBJ 파일

OBJ 파일은 대체로 이것보다 길거나 - 더 짧아요. :
An OBJ file looks more or less like this :

```
# Blender3D v249 OBJ File: untitled.blend
Expand Down Expand Up @@ -96,24 +89,17 @@ f 1/2/8 3/13/8 4/14/8
```

그래서, 뭔 뜻일까요?:

So :

* '#' 은 그냥 주석이에요. C++로 치면 '//' 같은거요.
* `#` 은 그냥 주석이에요. C++로 치면 '//' 같은거요.
* usemtl 과 mtllib는 모델이 어떻게 보여지는지 묘사하지만. 이번 튜토리얼에서는 안 쓸거에요.
* v은 정점이고.
* vt은 정점의 텍스쳐 좌표.
* vn은 정점의 normal 좌표고요.
* f는 면이에요.

* `#` is a comment, just like // in C++
* usemtl and mtllib describe the look of the model. We won't use this in this tutorial.
* v is a vertex
* vt is the texture coordinate of one vertex
* vn is the normal of one vertex
* f is a face

v, vt 그리고 vn은 쉽게 이해할 수 있겠지만. f는 조금 더 까다로워요. 그러니까 예제를 들어봐요! 만약 f 8/11/7 7/12/7 6/10/7이 있다고 해봅시다.
v, vt and vn are simple to understand. f is more tricky. So, for f 8/11/7 7/12/7 6/10/7 :

* 8/11/7은 삼각형의 첫 번째 정점을 표현한거에요.
* 7/12/7은 삼각형의 두 번째 정점을 표현한거에요.
Expand All @@ -122,33 +108,20 @@ v, vt and vn are simple to understand. f is more tricky. So, for f 8/11/7 7/12/7
* 11은 사용한 텍스쳐 좌표의 위치에요. 그러면 0.748355 0.998230가 되겠네요.
* 7은 사용할 normal 좌표의 위치에요. 그러면 0.000000 1.000000 -0.000000이겠네요.

* 8/11/7 describes the first vertex of the triangle
* 7/12/7 describes the second vertex of the triangle
* 6/10/7 describes the third vertex of the triangle (duh)
* For the first vertex, 8 says which vertex to use. So in this case, -1.000000 1.000000 -1.000000 (index start to 1, not to 0 like in C++)
* 11 says which texture coordinate to use. So in this case, 0.748355 0.998230
* 7 says which normal to use. So in this case, 0.000000 1.000000 -0.000000

이 숫자들은 'indices(인덱스들)' 라고 불려요. 이 방식은 꽤 똑똑한 방식인데 - 만약 같은 위치에 있는 정점을 공유하려면. 그냥 파일에서 v 하나를 척은 다음에 여러 번 돌려쓰면 되는 거죠. 메모리도 아끼고요.

These numbers are called indices. It's handy because if several vertices share the same position, you just have to write one "v" in the file, and use it several times. This saves memory.

나쁜 소식은 텍스처에는 다른 인덱스를 쓰라하고, normal에는 다른 인덱스를 쓰라하고, position에는 다른 인덱스를 쓰라고 할 수 없다는거죠. 그래서 이 튜토리얼에서는 인덱스 안된 메쉬를 사용할게요. 인덱싱은 나중에 - 튜토리얼 9에서 해요. 그때는 어떻게 돌아가는 지 알려드릴게요.

The bad news is that OpenGL can't be told to use one index for the position, another for the texture, and another for the normal. So the approach I took for this tutorial is to make a standard, non-indexed mesh, and deal with indexing later, in Tutorial 9, which will explain how to work around this.

## Blender에서 OBJ 파일 만들기


우리의 작은 로더는 심각하게 기능이 제한되어 있어서, Blender에서 파일을 뽑을때 정확한 옵션인지 특별히 주의를 기울이셔야 해요. 여기, 어떻게 Blender에서 뽑는지 보일거에요 :
Since our toy loader will be severely limited, we have to be extra careful to set the right options when exporting the file. Here's how it should look in Blender :

![]({{site.baseurl}}/assets/images/tuto-7-model-loading/Blender.png)

## 파일 읽기

## Reading the file

Ok, down with the actual code. We need some temporary variables in which we will store the contents of the .obj :
좋아요, 진짜 코드로 돌아가봅시다. .obj파일의 내용물을 저장하려면 임시 변수 몇몇개가 필요할 거에요 :

``` cpp
std::vector< unsigned int > vertexIndices, uvIndices, normalIndices;
Expand All @@ -157,7 +130,8 @@ std::vector< glm::vec2 > temp_uvs;
std::vector< glm::vec3 > temp_normals;
```

Since Tutorial 5 : A Textured Cube, you know how to open a file :
튜토리얼 5 : A Textured Cube에서 어떻게 파일을 여는지 배우셨죠? :


``` cpp
FILE * file = fopen(path, "r");
Expand All @@ -167,7 +141,7 @@ if( file == NULL ){
}
```

Let's read this file until the end :
파일의 끝까지 읽어봐요. :

``` cpp
while( 1 ){
Expand All @@ -181,9 +155,9 @@ while( 1 ){
// else : parse lineHeader
```
(notice that we assume that the first word of a line won't be longer than 128, which is a very silly assumption. But for a toy parser, it's all right)
(지금 우리는 첫 줄을 128바이트보다 더 못 읽는다는 걸 명심하세요. 썩 좋지 않은 조건이지만. 어차피 실제로는 쓰지 않을거니, 괜찮아요.)
Let's deal with the vertices first :
그러면 정점들을 먼저 읽어볼까요? :
``` cpp
if ( strcmp( lineHeader, "v" ) == 0 ){
Expand All @@ -192,7 +166,7 @@ if ( strcmp( lineHeader, "v" ) == 0 ){
temp_vertices.push_back(vertex);
```

i.e : If the first word of the line is "v", then the rest has to be 3 floats, so create a glm::vec3 out of them, and add it to the vector.
즉, 만약 첫 단어가 "v" 라면, 3개의 float들이 뒤에 있을거에요. 그러니 glm::vec3를 하나 만들고, vector에 추가해줍시다.

``` cpp
}else if ( strcmp( lineHeader, "vt" ) == 0 ){
Expand All @@ -201,9 +175,9 @@ i.e : If the first word of the line is "v", then the rest has to be 3 floats, so
temp_uvs.push_back(uv);
```
i.e if it's not a "v" but a "vt", then the rest has to be 2 floats, so create a glm::vec2 and add it to the vector.
즉, 만약 "v" 가 아니라 "vt"라면, 2개의 float들이 뒤에 있을거에요. 그러니 glm::vec2를 하나 만들고, vector에 추가해줍시다.
same thing for the normals :
normals도 똑같이 해주세요 :
``` cpp
}else if ( strcmp( lineHeader, "vn" ) == 0 ){
Expand All @@ -212,7 +186,7 @@ same thing for the normals :
temp_normals.push_back(normal);
```

And now the "f", which is more difficult :
마지막은 "f"인데. 이건 꽤 어려워요:

``` cpp
}else if ( strcmp( lineHeader, "f" ) == 0 ){
Expand All @@ -234,66 +208,67 @@ And now the "f", which is more difficult :
normalIndices.push_back(normalIndex[2]);
```
This code is in fact very similar to the previous one, except that there is more data to read.
이 코드는 읽을 데이터가 꽤 많다는 건 빼곤, 예전에 파일을 읽는 코드들과 꽤 비슷하죠?
## Processing the data
## 데이터 처리하기
So what we did there was simply to change the "shape" of the data. We had a string, we now have a set of std::vectors. But it's not enough, we have to put this into a form that OpenGL likes. Namely, removing the indexes and have plain glm::vec3 instead. This operation is called indexing.
자. 이제 우리는 간단하게나마 데이터의 "모양"을 바꿨어요. 아까까지는 문자열이었지만, 지금은 std::vector들에 있죠. 하지만 이걸로는 충분하지 않아요. 우리는 이걸 OpenGL이 좋아할 만한 형태로 바꿔야해요. 그러니까 indexes들을 날리고, glm::vec3를 대신 넣어야하죠. 이 작업을 indexing(인덱싱)이라고 해요.
We go through each vertex ( each v/vt/vn ) of each triangle ( each line with a "f" ) :
각 삼각형의 정점(v/vt/vn 같은 것들이요.) 들을 한번씩 돌아볼거에요. 루프를 통해서 말이죠. ( 각 라인의 "f" 마다. ) :
``` cpp
// For each vertex of each triangle
// For each vertex of each triangle (각 삼각형의 각 꼭지점을 순회합니다. )
for( unsigned int i=0; i<vertexIndices.size(); i++ ){
```

the index to the vertex position is vertexIndices[i] :
정점 좌표의 index는 vertexIndices[i]에 있었죠. :

``` cpp
unsigned int vertexIndex = vertexIndices[i];
```

so the position is temp_vertices[ vertexIndex-1 ] (there is a -1 because C++ indexing starts at 0 and OBJ indexing starts at 1, remember ?) :
그래서 좌표를 tmpe_vertices[vertexIndex - 1]에 담을거에요. ( -1을 빼는 이유는 C++은 index가 0부터 시작하지만, OBJ는 1부터 시작하기 때문이에요. 기억나요?)

``` cpp
glm::vec3 vertex = temp_vertices[ vertexIndex-1 ];
```

And this makes the position of our new vertex
이걸로 우리의 새로운 정점에게 좌표값을 줄 수 있죠.

``` cpp
out_vertices.push_back(vertex);
```

The same is applied for UVs and normals, and we're done !
그리고 이 일을 UV 좌표들과, normal 좌표들에게도 적용하면 끝이에요!

# Using the loaded data
# 읽어온 데이터 활용하기

Once we've got this, almost nothing changes. Instead of declaring our usual static const GLfloat g_vertex_buffer_data[] = {...}, you declare a std::vector vertices instead (same thing for UVS and normals). You call loadOBJ with the right parameters :
여기까지 해냈는데, 아직 바뀐 건 하나도 없어요. 이제 바꿔봐요. static const GLfloat g_vertex_buffer_data[] = {...} 를 선언하는 대신에 std::vector 정점들(텍스쳐 좌표와, normal 좌표도 똑같이요.)을 선언합시다. 그리고 loadOBJ 함수를 정확한 파라미터로 호출해봐요. :

``` cpp
// Read our .obj file
// 우리의 .obj 파일을 읽습니다.
std::vector< glm::vec3 > vertices;
std::vector< glm::vec2 > uvs;
std::vector< glm::vec3 > normals; // Won't be used at the moment.
std::vector< glm::vec3 > normals; // 지금은 안쓸거에요.
bool res = loadOBJ("cube.obj", vertices, uvs, normals);
```

and give your vectors to OpenGL instead of your arrays :
그리고 OpenGL에게 배열 대신에 vector들을 줘보세요. :

``` cpp
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
```
And that's it !
진짜 끝!
# Results
# 결과
Sorry for the lame texture, I'm NOT a good artist :( Any contribution welcome !
구린 텍스쳐에 미리 사과 드릴게요. 저는 **절대** 좋은 그림쟁이는 아니에요 :( 그러니까 기여는 환영할게요!
![]({{site.baseurl}}/assets/images/tuto-7-model-loading/ModelLoading.png)
# Other formats/loaders
# 다른 포맷들 / 로더들
우리가 만든 작은 로더는 초심자인 우리에게는 적합하지만, 실무에서 쓰기에는 전혀 아니죠. 한번 [유용한 링크 & 도구들](http://www.opengl-tutorial.org/miscellaneous/useful-tools-links/) 페이지를 살펴보면서 사용할 만한 도구가 있는지 확인해보세요. 아. 그렇지만 *진짜로* 쓰기 전에. 튜토리얼 9까지는 참아주세요.
This tiny loader should give you enough to get started, but won't want to use this in real life. Have a look at our [Useful Links & Tools](http://www.opengl-tutorial.org/miscellaneous/useful-tools-links/) page for some tools you can use. Note, however, that you'd better wait for tutorial 9 before *actually *trying to use them.

0 comments on commit 72d9efb

Please sign in to comment.