From ff6a5bae41380db8ff8e0fc6c9f796936e54f143 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Tue, 30 Oct 2018 20:55:35 +0900 Subject: [PATCH 01/18] tutorial 5 to Korean Korean translation --- .../tutorial-5-a-textured-cube/index.markdown | 263 +++++++++--------- 1 file changed, 133 insertions(+), 130 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown b/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown index e48a50d2f..886a81c99 100644 --- a/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown +++ b/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown @@ -10,29 +10,34 @@ order: 50 tags: [] language: kr --- +이 튜토리얼에서는 이걸 배울거에요! : -In this tutorial, you will learn : +* UV 좌표계가 뭘까요? +* 어떻게 텍스처를 로딩할까요? +* 그리고 그것들을 OpenGL에서 어떻게 쓸까요? +* 필터링과 밉맵핑이 뭔지, 그리고 그걸 어떻게 쓸까요? +* 어떻게 GLFW를 이용해서 텍스처를 더 편하게 로드할 수 있을까요? +* 알파 채널이 도대체 뭘까요? -* What are UV coordinates -* How to load textures yourself -* How to use them in OpenGL -* What is filtering and mipmapping, and how to use them -* How to load texture more robustly with GLFW -* What the alpha channel is -# About UV coordinates -When texturing a mesh, you need a way to tell to OpenGL which part of the image has to be used for each triangle. This is done with UV coordinates. +# UV 좌표계에 대해서 +Mesh에 텍스쳐링(텍스처를 붙이는 작업)하려면, 여러분은 각 삼각형(폴리곤)에 사용할 이미지의 일부를 OpenGL에게 전해줘야 하는 방법이 필요할거에요. 그 때 필요한 것이 UV 좌표계이랍니다. -Each vertex can have, on top of its position, a couple of floats, U and V. These coordinates are used to access the texture, in the following way : +각각의 정점은 위치의 제일 위에서 부터 2개의 float을 가질 수 있어요. 바로 U와 V죠. 이 좌표들은 텍스처에 접근할때 필요한데, 한번 보시죠! ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/UVintro.png) -Notice how the texture is distorted on the triangle. +삼각형에서 텍스처를 씌우면, 어떻게 왜곡되는지 살펴보세요. + +# 혼자서 .BMP 이미지들 로딩해보기 # Loading .BMP images yourself -Knowing the BMP file format is not crucial : plenty of libraries can load BMP files for you. But it's very simple and can help you understand how things work under the hood. So we'll write a BMP file loader from scratch, so that you know how it works, and never use it again. + +사실 BMP 파일 형식을 아는 건 그리 중요한게 아니에요. 많은 라이브러리들은 BMP 파일을 끝장나게 로드해 줄 수 있거든요. 그래도 아주 간단하고, 새련된 인터페이스 밑에서 얼마나 추악한 짓이 일어나고 있는지 이해하는데 도움이 될거에요. 자. 그러면 BMP 파일 로더를 처음부터 만들어서. 작동 방식을 알고. 그다음에 영원히 코드를 잠재워버립시다. 이 세상에는 더 가치 있는 코드가 많으니까요! + +로딩 함수의 정의부터 살펴봐요!: Here is the declaration of the loading function : @@ -40,15 +45,14 @@ Here is the declaration of the loading function : GLuint loadBMP_custom(const char * imagepath); ``` -so it's used like this : +그러면, 이렇게 쓰겠죠? : ``` cpp GLuint image = loadBMP_custom("./my_texture.bmp"); ``` -Let's see how to read a BMP file, then. - -First, we'll need some data. These variable will be set when reading the file. +자. 그러면 어떻게 BMP 파일을 읽나 봅시다. +먼저, 자료가 필요할 건데. 이 변수들은 파일을 읅을 때 설정될거에요. ``` cpp // Data read from the header of the BMP file @@ -60,7 +64,7 @@ unsigned int imageSize; // = width*height*3 unsigned char * data; ``` -We now have to actually open the file +우리는 이제 '진짜로' 파일을 열고 있어요! ``` cpp // Open the file @@ -68,7 +72,8 @@ FILE * file = fopen(imagepath,"rb"); if (!file){printf("Image could not be opened\n"); return 0;} ``` -The first thing in the file is a 54-bytes header. It contains information such as "Is this file really a BMP file?", the size of the image, the number of bits per pixel, etc. So let's read this header : +파일의 처음 부분은 54-바이트 헤더일거에요. 여기엔 "이거, 진짜 BMP파일이야?"와 같은 정보가 저장되어 있어요. 그러니까 이미지의 크기, 픽셀당 비트수 같은 거 말이에요. 자. 한번 읽어볼까요?: + ``` cpp if ( fread(header, 1, 54, file)!=54 ){ // If not 54 bytes read : problem @@ -77,11 +82,11 @@ if ( fread(header, 1, 54, file)!=54 ){ // If not 54 bytes read : problem } ``` -The header always begins by BM. As a matter of fact, here's what you get when you open a .BMP file in a hexadecimal editor : +헤더는 언제나 BM으로 시작할 거에요. 진짜냐고요? 그럼 16진수 편집기로 한번 까보죠! : ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/hexbmp.png) -So we have to check that the two first bytes are really 'B' and 'M' : +자. 그래서 우리는 첫 두바이트가 진짜 'B'와 'M'인지 체크해야 해요. : ``` cpp if ( header[0]!='B' || header[1]!='M' ){ @@ -89,131 +94,126 @@ if ( header[0]!='B' || header[1]!='M' ){ return 0; } ``` - -Now we can read the size of the image, the location of the data in the file, etc : +그럼 이제 우리는 이미지의 크기를 읽을 수 있고, 파일의 위치나.. 그런 것들을 읽을 수 있어요. : ``` cpp -// Read ints from the byte array +// 바이트 배열에서 int 변수를 읽습니다. dataPos = *(int*)&(header[0x0A]); imageSize = *(int*)&(header[0x22]); width = *(int*)&(header[0x12]); height = *(int*)&(header[0x16]); ``` -We have to make up some info if it's missing : +몇몇 정보가 날아갔을 때도 대비해야죠! : ``` cpp +// 몇몇 BMP 파일들은 포맷이 잘못되었습니다. 정보가 누락됬는지 확인해봅니다. // Some BMP files are misformatted, guess missing information if (imageSize==0) imageSize=width*height*3; // 3 : one byte for each Red, Green and Blue component if (dataPos==0) dataPos=54; // The BMP header is done that way ``` -Now that we know the size of the image, we can allocate some memory to read the image into, and read : +자, 그러면 우리는 이제 이미지의 크기를 알고 있으니까 - 이미지를 저장할 메모리를 할당한 다음 - 파일을 읽을 수 있겠네요! : ``` cpp -// Create a buffer +// 버퍼 생성 data = new unsigned char [imageSize]; -// Read the actual data from the file into the buffer +// 파일에서 버퍼로 실제 데이터 넣기. fread(data,1,imageSize,file); +//이제 모두 메모리 안에 있으니까, 파일을 닫습니다. //Everything is in memory now, the file can be closed fclose(file); ``` +자. 드디어 '진짜' OpenGL 파트에 도착했어요. 텍스처 생성은 정점 버퍼 생성이랑 아주 비슷해요. 또 텍스처를 만들고, 또 바인딩 하고, 또 채우고, 또 구성하는거죠! -We arrive now at the real OpenGL part. Creating textures is very similar to creating vertex buffers : Create a texture, bind it, fill it, and configure it. - -In glTexImage2D, the GL_RGB indicates that we are talking about a 3-component color, and GL_BGR says how exactly it is represented in RAM. As a matter of fact, BMP does not store Red->Green->Blue but Blue->Green->Red, so we have to tell it to OpenGL. +glTexImage2D에선. GL_RGB는 3가지 색상이라고 알려주는거고요, 그리고 GL_BGR은 RAM에 정확히 어떻게 표현되는지 알려주는거에요. 실제론, BMP는 빨강->초록->파랑 순이 아니라 파랑->초록->빨강으로 저장해요. 그래서. 우리는 그걸 OpenGL에게 알려줘야해요. ``` cpp -// Create one OpenGL texture +// OpenGL Texture를 생성합니다. GLuint textureID; glGenTextures(1, &textureID); +// 새 텍스처에 "Bind" 합니다 : 이제 모든 텍스처 함수들은 이 텍스처를 수정합니다. // "Bind" the newly created texture : all future texture functions will modify this texture glBindTexture(GL_TEXTURE_2D, textureID); -// Give the image to OpenGL +// OpenGL에게 이미지를 넘겨줍니다. glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ``` -We'll explain those last two lines later. Meanwhile, on the C++-side, you can use your new function to load a texture : +마지막 두 줄은 나중에 설명할게요. 어쨌든, C++에선 이제 새 기능을 이용해 텍스처를 로드할 수 있어요! : ``` cpp GLuint Texture = loadBMP_custom("uvtemplate.bmp"); ``` -> Another very important point :** use power-of-two textures !** +> 아주 아주 중요한 점이에요! :** 2배수의 텍스처를 쓰세요! !** > -> * good : 128\*128, 256\*256, 1024\*1024, 2\*2... -> * bad : 127\*128, 3\*5, ... -> * okay but weird : 128\*256 +> * 좋음 : 128\*128, 256\*256, 1024\*1024, 2\*2... +> * 나쁨 : 127\*128, 3\*5, ... +> * 괜찮은데, 이상해요. : 128\*256 -# Using the texture in OpenGL +# OpenGL에서 텍스처 쓰기 -We'll have a look at the fragment shader first. Most of it is straightforward : +자. Frgament Shader를 먼저 봅시다. 간단해요! : ``` glsl #version 330 core -// Interpolated values from the vertex shaders +// 정점 셰이더에서 넘겨준 보간 값. in vec2 UV; -// Ouput data +// 출력 데이터 out vec3 color; -// Values that stay constant for the whole mesh. +// 한 메쉬를 그리는 동안 일정하게 유지되는 값. uniform sampler2D myTextureSampler; void main(){ - - // Output color = color of the texture at the specified UV + // Output color = 지정된 UV에서 텍스처의 색. color = texture( myTextureSampler, UV ).rgb; } ``` -{: .highlightglslfs } - -Three things : +자. 세가지가 필요합니다! : +* Fragment Shader는 UV 좌표가 필요한 것 같네요. 좋아요. +* 그리고, 접근할 텍스처를 확인하려면 "Sampler" 2D가 필요한 것 같고요. (동일한 셰이더로, 여러 텍스처에 접근할 수 있어요.) +* 마지막으론, texture() 함수를 통해 텍스쳐에 접근하면, (R,G,B,A) vec4를 돌려주네요. 곧 A에 대해 알아 볼거에요. -* The fragment shader needs UV coordinates. Seems fair. -* It also needs a "sampler2D" in order to know which texture to access (you can access several texture in the same shader) -* Finally, accessing a texture is done with texture(), which gives back a (R,G,B,A) vec4. We'll see about the A shortly. -The vertex shader is simple too, you just have to pass the UVs to the fragment shader : +Vertex Shader도 간단해요, 그냥 UV 좌표들을 Fragment Shader에 전해주기만 하면 되죠! : ``` glsl #version 330 core -// Input vertex data, different for all executions of this shader. +// 입력 정점 데이터, 이 셰이더가 실행할 때마다 달라집니다. (각 정점마다 셰이더가 한번씩 실행되요.) layout(location = 0) in vec3 vertexPosition_modelspace; layout(location = 1) in vec2 vertexUV; -// Output data ; will be interpolated for each fragment. +// 출력 데이터 ; 각 픽셀마다 알아서 보간될거에요. out vec2 UV; +// 이 변수는 '매쉬당' 상수에요. // Values that stay constant for the whole mesh. uniform mat4 MVP; void main(){ - - // Output position of the vertex, in clip space : MVP * position + // 정점의 출력 위치 = MVP(Model View Projection) * position; gl_Position = MVP * vec4(vertexPosition_modelspace,1); - - // UV of the vertex. No special space for this one. + + // 정점의 UV. 특별한 건 없음. UV = vertexUV; } ``` - -{: .highlightglslvs } - -Remember "layout(location = 1) in vec2 vertexUV" from Tutorial 4 ? Well, we'll have to do the exact same thing here, but instead of giving a buffer (R,G,B) triplets, we'll give a buffer of (U,V) pairs. +튜토리얼 4에 나왔던 "layout(location = 1) in vec2 vertexUV"를 기억하세요? 뭐, 여기서도 똑같이 해야겠지만. (R,G,B) 세 개를 주는 것보단 (U,V) 를 던져주죠! 그 편이 더 예쁠꺼니까요. ``` cpp -// Two UV coordinatesfor each vertex. They were created with Blender. You'll learn shortly how to do this yourself. +// 정점 당 두개의 UV 좌표. Blender에서 생성되었습니다. 곧 이걸 스스로 하는 법을 배울겁니다. static const GLfloat g_uv_buffer_data[] = { 0.000059f, 1.0f-0.000004f, 0.000103f, 1.0f-0.336048f, @@ -253,127 +253,129 @@ static const GLfloat g_uv_buffer_data[] = { 0.667979f, 1.0f-0.335851f }; ``` - -The UV coordinates above correspond to the following model : +위 UV 좌표는 다음 모델에 해당되요 : ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/uv_mapping_blender.png) -The rest is obvious. Generate the buffer, bind it, fill it, configure it, and draw the Vertex Buffer as usual. Just be careful to use 2 as the second parameter (size) of glVertexAttribPointer instead of 3. +자. 2번이나 했던 걸 또 해봐요. 버퍼 생성, 바인딩, 채우고, 구성하고, 그리고 평소대로 Vertex Buffer를 그리는데. 조심할 점은 glVertexAttribPointer 함수에서 두 번째 매게변수(size, 크기.)는 3이 아니라 2에요! 중요하니 다시 말할께요. glVertexAttribPointer 함수에서 두 번째 매게변수(size, 크기.)를 3에서 2로 바꿔요! -This is the result : + +이제 결과를 볼까요? : ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/nearfiltering.png) -and a zoomed-in version : +그리고 - 줌인 버전! : ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/nearfiltering_zoom.png) -# What is filtering and mipmapping, and how to use them +# 밉맵과 필터링은 무엇이고, 어떻게 쓸까 -As you can see in the screenshot above, the texture quality is not that great. This is because in loadBMP_custom, we wrote : +아래 스크린 샷에 볼 수 있듯. 텍스처 품질이 구리네요. 왜일까요? 우리가 loadBMP_custom에서 이렇게 써서 그래요. : ``` cpp glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ``` -This means that in our fragment shader, texture() takes the texel that is at the (U,V) coordinates, and continues happily. +그러니까, 우리의 Fragment Shader에서, texture() 함수가 대충 U,V 좌표에 있는 texel(역주: Tex + Pixel)을 취하고. 계속 대충 때려박는거죠. 왜냐면 화면은 아-주 크지만. 텍스처는 아-주 크지는 않으니. 한 Texel에서 계속 똑같은 색만 들고 오는 거에요. ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/nearest.png) -There are several things we can do to improve this. +자. 이대로 두고 볼수만은 없겠죠? -## Linear filtering +## 선형 필터링 -With linear filtering, texture() also looks at the other texels around, and mixes the colours according to the distance to each center. This avoids the hard edges seen above. +선형 필터링을 쓰면, texutre() 함수는 주변의 다른 텍셀을 보기 시작하고. 각 텍셀까지의 거리에 따라 색상을 섞어요. 그렇게 하면- 아까 보이던 도트게임 같던 비주얼은 사라지겠죠! -![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/linear1.png) -This is much better, and this is used a lot, but if you want very high quality you can also use anisotropic filtering, which is a bit slower. +![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/linear1.png) -## Anisotropic filtering +어휴, 굉장한 진전이기는 한데, 그치만 여전히 구려보인다면. 이것보단 조금 더 느린 비등방성 필터링이라는 걸 쓸 수 있어요. -This one approximates the part of the image that is really seen through the fragment. For instance, if the following texture is seen from the side, and a little bit rotated, anisotropic filtering will compute the colour contained in the blue rectangle by taking a fixed number of samples (the "anisotropic level") along its main direction. +## 비등방성 필터링 +이 부분은 조각을 통해 실제로 보이는 이미지 부분을 근사해요. 예를 들어, 다음 텍스처가 측면에서 보이고 조금 회전한다면, 이방성 필터링이 주 방향에 따라 고정된 수의 샘플("비등방성 레벨")을 취해 파란색 직사각형에 포함된 색깔을 계산해요. ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/aniso.png) -## Mipmaps +## 밉맵 -Both linear and anisotropic filtering have a problem. If the texture is seen from far away, mixing only 4 texels won't be enough. Actually, if your 3D model is so far away than it takes only 1 fragment on screen, ALL the texels of the image should be averaged to produce the final color. This is obviously not done for performance reasons. Instead, we introduce MipMaps : +선형, 비등방성 필터링은 둘 다 문제를 가지고 있어요. 만약에 텍스처가 멀리서도 잘 보이려면, 4개의 텍셀을 섞는 것만으론 충분하지 않겠죠. 실제로, 만약 우리의 3D 모델이 화면의 한 픽셀(fragment를 Pixel로 번역했습니다.) 만 차지 한다면. 이미지의 *모든* 텍셀들의 평균을 내서 최종 색깔을 결정해야해요. 점 하나 그리는데 말이에요! 그걸 누가 해요?! 아무도 안하죠! 똑똑한 사람들은 모두 MipMaps를 쓰거든요! ![](http://upload.wikimedia.org/wikipedia/commons/5/5c/MipMap_Example_STS101.jpg) -* At initialisation time, you scale down your image by 2, successively, until you only have a 1x1 image (which effectively is the average of all the texels in the image) -* When you draw a mesh, you select which mipmap is the more appropriate to use given how big the texel should be. -* You sample this mipmap with either nearest, linear or anisotropic filtering -* For additional quality, you can also sample two mipmaps and blend the results. +* 초기화 때, 이미지를 2배씩 계속 축소면서 1x1 이미지(사실상 이미지 내 모든 Texel의 평균)까지 생성해요. +* 메쉬를 그릴 때, 텍셀이 얼마나 커야된지 생각하며 어느 밉맵을 쓸지 정해요. +* 가장 가까운 밉맵을 선형이나 이방성 필터링을 써서 샘플링해요. +* 더 괜찮은 결과를 얻으려면, 두개의 밉맵을 샘플링하고 섞어서 결과를 얻을 수도 있겠네요! -Luckily, all this is very simple to do, OpenGL does everything for us provided that you ask him nicely : +다행히도, 위 과정은 아주 간단하게 할 수 있어요! OpenGL은 우리들을 잘 돌봐주고 있거든요. : ``` cpp -// When MAGnifying the image (no bigger mipmap available), use LINEAR filtering +// 이미지를 확대할땐(영어로 확대는 MAGnifying. 앞글자 MAG를 따왔네요.), 선형 필터링을 사용합니다. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -// When MINifying the image, use a LINEAR blend of two mipmaps, each filtered LINEARLY too +// 이미지를 축소할땐(영어로 축소는 MINifying. 앞글자 MIN을 따왔네요.), 두개의 밉맵을 선형으로 블랜드하고, 선형으로 필터링합니다. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); -// Generate mipmaps, by the way. +// 그리고- 밉맵 생성. glGenerateMipmap(GL_TEXTURE_2D); ``` -# How to load texture with GLFW +# GLFW로 텍스처 로드하기 -Our loadBMP_custom function is great because we made it ourselves, but using a dedicated library is better. GLFW2 can do that too (but only for TGA files, and this feature has been removed in GLFW3, that we now use) : +우리의 loadBMP_custom 함수는 괜찮아요. 물론 우리가 스스로 만든 건 치곤요. 하지만 역시 전용 라이브러리를 쓰는 게 더 좋아요. GLFW2는 훌륭한 역활을 해줄거에요!(하지만 TGA 파일 밖에 안되고. 게다가 GLFW3에선 그 기능마저 사라졌어요. 우리가 '지금' 쓰는 버전 말이에요. ) (역주: 그럼 도대체 왜 이게 있는거지;) : ``` cpp GLuint loadTGA_glfw(const char * imagepath){ - // Create one OpenGL texture + // OpenGL 텍스처 생성! GLuint textureID; glGenTextures(1, &textureID); - - // "Bind" the newly created texture : all future texture functions will modify this texture + + // 새롭게 생성된 텍스처를 "Bind"합니다. : 이제 앞으로 모든 Texutre 관련 함수는 이 친구를 건듭니다. glBindTexture(GL_TEXTURE_2D, textureID); - // Read the file, call glTexImage2D with the right parameters + // 파일을 읽고, 매개 변수로 glTexImage2D를 호출해요. glfwLoadTexture2D(imagepath, 0); - // Nice trilinear filtering. + // 괜찮은 3중 필터링. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmap(GL_TEXTURE_2D); - // Return the ID of the texture we just created + // 우리가 만든 Texture의 ID를 돌려줍니다! return textureID; } ``` -# Compressed Textures +# 압축 텍스쳐 -At this point, you're probably wondering how to load JPEG files instead of TGA. +자. 이제 TGA나, BMP처럼 멸종당한 생명체보단 JPEG 파일을 로드하는 게 궁금 할 거에요. -Short answer : don't. GPUs can't understand JPEG. So you'll compress your original image in JPEG, and decompress it so that the GPU can understand it. You're back to raw images, but you lost image quality while compressing to JPEG. +짧은 답을 드리죠 : 하지마요. GPU들은 JPEG를 이해하지 못해요. 그래서 원본 이미지를 JPEG로 압축하고, 또 압축을 풀고 GPU가 이해할 수 있도록 해야하는데요. 그럼 결국 원시 이미지로 돌아왔지만. JEPG로 돌아오는 동안 화질은 떨어졌죠. 램은 그대로 먹고요. -There's a better option. -## Creating compressed textures +더 괜찮은 방법이 있어요. +## 압축 택스쳐 만들기 -* Download [The Compressonator](http://gpuopen.com/gaming-product/compressonator/), an AMD tool -* Load a Power-Of-Two texture in it -* Generate mipmaps so that you won't have to do it on runtime -* Compress it in DXT1, DXT3 or in DXT5 (more about the differences between the various formats on [Wikipedia](http://en.wikipedia.org/wiki/S3_Texture_Compression)) : + +* [The Compressonator](http://gpuopen.com/gaming-product/compressonator/)를 다운로드 받으세요, AMD 툴입니다. +* 2의 제곱 텍스처를 넣어요! +* Runtime에 밉맵들을 만들고 싶지 않으면, 지금 만들 수 있어요. +* DXT1, DXT3나 DXT5로 압축하세요.(더 자세한 정보는 [Wikipedia](http://en.wikipedia.org/wiki/S3_Texture_Compression)) : ![]({{site.baseurl}}/assets/images/tuto-5-textured-cube/TheCompressonator.png) +* .DDS 파일로 뽑습니다. * Export it as a .DDS file. -At this point, your image is compressed in a format that is directly compatible with the GPU. Whenever calling texture() in a shader, it will uncompress it on-the-fly. This can seem slow, but since it takes a LOT less memory, less data needs to be transferred. But memory transfers are expensive; and texture decompression is free (there is dedicated hardware for that). Typically, using texture compression yields a 20% increase in performance. So you save on performance and memory, at the expense of reduced quality. +이 시점에서, 우리의 이미지는 GPU에 바로 박을 수 있는 포맷으로 압축되었어요. shader 안에서 texutre() 함수를 부를 때, 즉석해서 압축을 풀거에요. 압축 푸는게 느려보일 수 있지만 - 엄청나게 적은 메모리를 먹어서 적은 양의 데이터만 전송하면 되기에 - 오히려 더 빠른데요. 왜냐면 텍스처 압축해제는 무료지만(전용 하드웨어가 있어요.), 데이터 전송이 비싸기 때문이죠! 일반적으로, 텍스처 압축을 사용하면 성능이 무려 20%나 증가해요! 성능과 메모리를 절약할 수 있죠. 조금 텍스처가 구려보이겠지만. -## Using the compressed texture -Let's see how to load the image. It's very similar to the BMP code, except that the header is organized differently : +## 압축 텍스처 사용하기 +그럼 이미지를 어떻게 불러오는 지 보자고요! BMP 로드 코드랑 유사하지만, 헤더가 다르다는게 차이점이에요. : ``` cpp GLuint loadDDS(const char * imagepath){ @@ -381,12 +383,12 @@ GLuint loadDDS(const char * imagepath){ FILE *fp; - /* try to open the file */ + /* 파일 열기 시도 */ fp = fopen(imagepath, "rb"); if (fp == NULL) return 0; - /* verify the type of file */ + /* 파일 타입 체크 */ char filecode[4]; fread(filecode, 1, 4, fp); if (strncmp(filecode, "DDS ", 4) != 0) { @@ -394,7 +396,7 @@ GLuint loadDDS(const char * imagepath){ return 0; } - /* get the surface desc */ + /* 이미지의 정보를 긁어옵니다. */ fread(&header, 124, 1, fp); unsigned int height = *(unsigned int*)&(header[8 ]); @@ -404,22 +406,20 @@ GLuint loadDDS(const char * imagepath){ unsigned int fourCC = *(unsigned int*)&(header[80]); ``` -After the header is the actual data : all the mipmap levels, successively. We can read them all in one batch : - - +헤더 뒤에 진짜 데이터가 있는데요. 모든 밉맵 레벨들이 포함되어 있어요! 그리고 우린 이걸 한번에 읽을 수 있고요 : ``` cpp unsigned char * buffer; unsigned int bufsize; - /* how big is it going to be including all mipmaps? */ + /* 모든 밉맵을 포함하면 얼마나 크나요? */ bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize; buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); fread(buffer, 1, bufsize, fp); - /* close the file pointer */ + /* 파일 포인터 닫기 */ fclose(fp); ``` -Here we'll deal with 3 different formats : DXT1, DXT3 and DXT5. We need to convert the "fourCC" flag into a value that OpenGL understands. +우리는 - 머리가 복잡해지기 싫으니 - 3가지 포맷들만 다룰게요 : DXT1, DXT3, 그리고 DXT5. 위에서 얻은 "fourCC" 플래그는 OpenGL이 이해할 수 없으니. OpenGL이 이해할 수 있는 값으로 바꿀게요. ``` cpp unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4; @@ -441,18 +441,18 @@ Here we'll deal with 3 different formats : DXT1, DXT3 and DXT5. We need to conve } ``` -Creating the texture is done as usual : +텍스처 생성은 평소 같이! : ``` cpp // Create one OpenGL texture GLuint textureID; glGenTextures(1, &textureID); - // "Bind" the newly created texture : all future texture functions will modify this texture + // 새롭게 생성된 텍스처를 "Bind"합니다. : 이제 앞으로 모든 Texutre 관련 함수는 이 친구를 건듭니다. glBindTexture(GL_TEXTURE_2D, textureID); ``` -And now, we just have to fill each mipmap one after another : +그리고, 우리는 이제 각 밉맵을 하나씩 채워 넣기만 하면 되죠. : ``` cpp unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16; @@ -474,25 +474,28 @@ And now, we just have to fill each mipmap one after another : return textureID; ``` -## Inversing the UVs +## UV 좌표 반전하기 -DXT compression comes from the DirectX world, where the V texture coordinate is inversed compared to OpenGL. So if you use compressed textures, you'll have to use ( coord.u, 1.0-coord.v) to fetch the correct texel. You can do this whenever you want : in your export script, in your loader, in your shader... +DXT 압축은 OpenGL과 V 좌표계가 반대인 DirectX에서 건너왔어요. 그래서 압축된 텍스처를 사용할 때엔 올바른 Texel을 가져 오기 위해 이 공식을 써야해요.( coord.u, 1.0 - coord.v ) 뭐, 언제 하든 상관 없어요. : 로더에서 하든, 쉐이더에서 하든... 방법은 많겠죠. 그냥 까먹지나 마요. -# Conclusion +# 결론 -You just learnt to create, load and use textures in OpenGL. +박수! 이제 OpenGL 텍스처를 만들고, 불러오고 사용하는 법을 배우셨어요! -In general, you should only use compressed textures, since they are smaller to store, almost instantaneous to load, and faster to use; the main drawback it that you have to convert your images through The Compressonator (or any similar tool) +일반적으로 압축 된 텍스처가 저장하기에도 작고, 불러오는 데도 엄청 빠르고, 사용하기도 엄청 빨라서 사용해야해요. 뭐. 단점이 있다면 Compressonator를 이용해서 변환해야 한다는 걸까요? 귀찮죠. (아니면 다른 툴을 쓰던가요.) -# Exercices +# 연습문제들 +* DDS 로더는 소스코드에 구현은 되어있는데, 텍스처 좌표는 수정하지 않아요. 적절하게 코드를 변경해서 큐브를 멋지게 출력해봐요! +* 다양한 DDS 형식으로 실험해보세요. 다른 결과를 주던가요? 다른 압축 비율이라던가? +* Compressonator에서 밉맵을 생성하지 마세요. 어떻게 되던가요? 터졌나요? 터졌으면 해결하는 3가지 다른 방법을 만들어봐요! -* The DDS loader is implemented in the source code, but not the texture coordinate modification. Change the code at the appropriate place to display the cube correctly. -* Experiment with the various DDS formats. Do they give different result ? Different compression ratios ? -* Try not to generate mipmaps in The Compressonator. What is the result ? Give 3 different ways to fix this. - -# References +# 참고문헌들 * [Using texture compression in OpenGL](http://www.oldunreal.com/editing/s3tc/ARB_texture_compression.pdf) , Sébastien Domine, NVIDIA + + +# 참고 +역주 : 알파 채널 내용은 원본에서도 안 나와있습니다. From cc263189652705d79a386afe2db1cabfa49ec9a8 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Tue, 30 Oct 2018 20:56:03 +0900 Subject: [PATCH 02/18] Update index.markdown --- .../tutorial-5-a-textured-cube/index.markdown | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown b/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown index 886a81c99..5aa70fc02 100644 --- a/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown +++ b/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown @@ -32,15 +32,11 @@ Mesh에 텍스쳐링(텍스처를 붙이는 작업)하려면, 여러분은 각 # 혼자서 .BMP 이미지들 로딩해보기 -# Loading .BMP images yourself - 사실 BMP 파일 형식을 아는 건 그리 중요한게 아니에요. 많은 라이브러리들은 BMP 파일을 끝장나게 로드해 줄 수 있거든요. 그래도 아주 간단하고, 새련된 인터페이스 밑에서 얼마나 추악한 짓이 일어나고 있는지 이해하는데 도움이 될거에요. 자. 그러면 BMP 파일 로더를 처음부터 만들어서. 작동 방식을 알고. 그다음에 영원히 코드를 잠재워버립시다. 이 세상에는 더 가치 있는 코드가 많으니까요! 로딩 함수의 정의부터 살펴봐요!: -Here is the declaration of the loading function : - ``` cpp GLuint loadBMP_custom(const char * imagepath); ``` From 720a5b11e2e05d0170abb90b88ec6fcd67d2ee40 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Thu, 1 Nov 2018 12:10:51 +0900 Subject: [PATCH 03/18] Update index.markdown --- .../index.markdown | 164 +++++++++--------- 1 file changed, 81 insertions(+), 83 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown index e3cefe6cc..9049bd744 100644 --- a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown +++ b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown @@ -10,16 +10,15 @@ order: 60 tags: [] language: kr --- - +6번째 튜토리얼에 오신 걸 환영합니다! Welcome for our 6th tutorial ! +오늘은 FPS 처럼 마우스와 키보드를 이용해 카매라를 어떻게 움직이는 지 알아볼거에요! -We will now learn how to use the mouse and the keyboard to move the camera just like in a FPS. - -# The interface +# 인터페이스 -Since this code will be re-used throughout the tutorials, we will put the code in a separate file : common/controls.cpp, and declare the functions in common/controls.hpp so that tutorial06.cpp knows about them. +이 코드는 이제 튜토리얼을 하는 동안 계속 우려먹을 거니까, 파일 분할을 합시다 : common/controls.cpp에 코드를 추가하고, common/controls.hpp에 함수들을 선언해서 tutorial06.cpp가 코드를 인식하게 합시다. -The code of tutorial06.cpp doesn't change much from the previous tutorial. The major modification is that instead of computing the MVP matrix once, we now have to do it every frame. So let's move this code inside the main loop : +tutorial06.cpp은 저번 튜토리얼과 변경점이 크게 없어요. 주 변경점은 이제 MVP 메트릭스를 한번만 계산하는 대신에, 이제 매 프레임 계산하는 거에요. 그래서, 이 코드를 메인-루프에 추가합시다 ! : ``` cpp do{ @@ -36,98 +35,95 @@ do{ // ... } ``` +이 코드는 3가지 새로운 함수가 필요합니다 : -This code needs 3 new functions : - -* computeMatricesFromInputs() reads the keyboard and mouse and computes the Projection and View matrices. This is where all the magic happens. -* getProjectionMatrix() just returns the computed Projection matrix. -* getViewMatrix() just returns the computed View matrix. +* computeMatricesFromInputs()는 키보드와 마우스, 그리고 Projection 행렬과 View 행력의 계산을 담당합니다. 여기서 모든 일이 일어날거에요! +* getProjectionMatrix()는, 계산한 Projection 행렬을 돌려주기만 합니다. +* getViewMatrix()도, 계산한 View 행렬을 돌려주기만 합니다. -This is just one way to do it, of course. If you don't like these functions, go ahead and change them. +물론, 이건 수많은 방법 중에 한 개일 뿐이에요. 이 함수들이 마음에 안드신다면, 계속해서 변경해보세요! -Let's see what's inside controls.cpp. +자. 그러면 controls.cpp안으로 가봅시다! -# The actual code +# 진-짜 코드 -We'll need a few variables. +몇몇 변수가 필요할거에요. ``` cpp -// position +// 좌표 glm::vec3 position = glm::vec3( 0, 0, 5 ); -// horizontal angle : toward -Z +// 수평각 : -Z 방향으로 float horizontalAngle = 3.14f; -// vertical angle : 0, look at the horizon +// 수직각 : 0, 수직을 바라봅니다. float verticalAngle = 0.0f; -// Initial Field of View +// 초기 FOV값 float initialFoV = 45.0f; -float speed = 3.0f; // 3 units / second +float speed = 3.0f; // 초당 3 unitsl float mouseSpeed = 0.005f; ``` -FoV is the level of zoom. 80° = very wide angle, huge deformations. 60° - 45° : standard. 20° : big zoom. +FoV는 줌의 단계에요. 80° = 아주 넓은 각도, 큰 변형. 60° - 45° : 표준. 20° : 큰 줌. -We will first recompute position, horizontalAngle, verticalAngle and FoV according to the inputs, and then compute the View and Projection matrices from position, horizontalAngle, verticalAngle and FoV. +우린 이제 입력에 따라 position, horizontalAngle, verticalAngle, 그리고 Fov를 수정하고, View, Projection 행렬을 position, horizontalAngle, verticalAngle, 그리고 Fov에 따라 계산할 거에요. -## Orientation +## 방향 결정 -Reading the mouse position is easy : +마우스 좌표를 읽는 건 쉽습니다. : ``` cpp -// Get mouse position +// 마우스 좌표 받기 int xpos, ypos; glfwGetMousePos(&xpos, &ypos); ``` - -but we have to take care to put the cursor back to the center of the screen, or it will soon go outside the window and you won't be able to move anymore. +하지만, 커서를 화면의 중앙에 되돌려 놓지 않으면 커서가 화면 바깥을 나가게 되버립니다. 그러면 게임을 할 수 없겠죠? ``` cpp -// Reset mouse position for next frame +// 매 프레임마다, 화면의 중앙으로 마우스 커서를 움직입니다. glfwSetMousePos(1024/2, 768/2); ``` -Notice that this code assumes that the window is 1024*768, which of course is not necessarily the case. You can use glfwGetWindowSize if you want, too. - -We can now compute our viewing angles : +아. 이 코드는 1024*768에 맞춰저 있는어요. 화면 크기가 다르시다고요? glfwGetWindowSize를 이용하면 현재 화면 크기를 받아 오실 수 있어요. +자. 이제 각도를 계산할 수 있겠네요. : ``` cpp -// Compute new orientation +// 새로운 방향 계산 horizontalAngle += mouseSpeed * deltaTime * float(1024/2 - xpos ); verticalAngle += mouseSpeed * deltaTime * float( 768/2 - ypos ); ``` +그러면, 오른쪽에서 부터 왼쪽으로 읽어볼까요? : Let's read this from right to left : -* 1024/2 - xpos means : how far is the mouse from the center of the window ? The bigger this value, the more we want to turn. -* float(...) converts it to a floating-point number so that the multiplication goes well. -* mouseSpeed is just there to speed up or slow down the rotations. Fine-tune this at will, or let the user choose it. -* += : If you didn't move the mouse, 1024/2-xpos will be 0, and horizontalAngle+=0 doesn't change horizontalAngle. If you had a "=" instead, you would be forced back to your original orientation each frame, which isn't good. +* 1024/2 - xpos 는 : 얼마나 마우스가 윈도우에 중앙에서 부터 멀어졌는가? 에요. 더 돌고 싶으면, 이 값이 크면 되요. +* float(...)는 곱샘이 잘 되도록 정수형에서 실수형으로 바꿔줘요. +* mouseSpeed는 회전 속도를 높이거나, 낮출 수 있어요. 괜찮게 튜닝하거나, 그냥 유저가 정하게 내버려둬요. +* += : 만약 마우스를 움직이지 않으면, 1024/2-xpos는 0이 될거고, horizontalAngle+=0이 되어 horizontalAngle이 바뀌지 않겠죠. 만약에 "+="이 아니라, "="을 대신 사용한다면 매 프레임마다 원 상태로 복귀할건데. 그거. 별로 보기 안 좋아요. -We can now compute a vector that represents, in World Space, the direction in which we're looking +이제 우리는 바라보고 있는 방향을 가지고 있는 벡터를 계산할 수 있어요! ``` cpp -// Direction : Spherical coordinates to Cartesian coordinates conversion +// 방향 : 구면 좌표를 데카르트 좌표로 변환 glm::vec3 direction( cos(verticalAngle) * sin(horizontalAngle), sin(verticalAngle), cos(verticalAngle) * cos(horizontalAngle) ); ``` - -This is a standard computation, but if you don't know about cosine and sinus, here's a short explanation : +이게 계산이기는 한데. 코사인과 사인에 대해서 잘 모르시는 분도 있을거에요. 그런 분을 위해 간단한 설명을 드릴게요. : -The formula above is just the generalisation to 3D. +위 공식은 3D에 대한 일반화에요. -Now we want to compute the "up" vector reliably. Notice that "up" isn't always towards +Y : if you look down, for instance, the "up" vector will be in fact horizontal. Here is an example of to cameras with the same position, the same target, but a different up: +이제 "up"벡터를 계산해봐요. "Up"은 항상 +Y 방향을 향하는 건 아닌데 - 가령, 아래를 바라본다면. "Up" 벡터가 수평이 되겠네요. - 아래는 동일 위치, 동일 타겟, 그렇지만 다른 "UP" 벡터를 가지는 두개의 카메라를 보여주고 있어요. : ![]({{site.baseurl}}/assets/images/tuto-6-mouse-keyboard/CameraUp.png) -In our case, the only constant is that the vector goes to the right of the camera is always horizontal. You can check this by putting your arm horizontal, and looking up, down, in any direction. So let's define the "right" vector : its Y coordinate is 0 since it's horizontal, and its X and Z coordinates are just like in the figure above, but with the angles rotated by 90°, or Pi/2 radians. +우리의 경우엔, 유일한 상수는 카메라의 오른쪽으로 가는 벡터가 항상 수평이라는 점이에요. 팔을 수평으로 놓고, 어떤 방향으로든 아래를 내려다보시거나- 올려다 보시면 알 수 있을거에요. 그러면. "오른쪽" 벡터를 정의해봅시다. Y 좌표는 수평이니 0이고, X 와 Z 좌표는 위의 그림과 같지만 각도는 90°나 Pi/2 라디안으로 회전할거에요. ``` cpp -// Right vector +// 오른쪽 벡터 glm::vec3 right = glm::vec3( sin(horizontalAngle - 3.14f/2.0f), 0, @@ -135,103 +131,105 @@ glm::vec3 right = glm::vec3( ); ``` -We have a "right" vector and a "direction", or "front" vector. The "up" vector is a vector that is perpendicular to these two. A useful mathematical tool makes this very easy : the cross product. +"Right"벡터와 "Direction" 벡터, "Front"벡터가 있어요. "Up"벡터는 이 둘에 대해 수직인 벡터에요. 유용한 수학 함수를 사용하면이 작업을 매우 간단하게 처리 할 수 있어요. : 바로. 외적이에요! + ``` cpp -// Up vector : perpendicular to both direction and right +// Up vector : Direction 과 Right에 대해 직각 glm::vec3 up = glm::cross( right, direction ); ``` -To remember what the cross product does, it's very simple. Just recall the Right Hand Rule from Tutorial 3. The first vector is the thumb; the second is the index; and the result is the middle finger. It's very handy. - -## Position +외적이 기억이 안나신다고요? 간단해요. 튜토리얼 3에서 보셨던 오른손 법칙을 생각해보세요. 첫 번쨰 벡터는 엄지 손가락, 두 번째 손가락은 검지 손가락. 그리고 답은 가운데 손가락이에요. 어때요. 참 쉽죠? -The code is pretty straightforward. By the way, I used the up/down/right/left keys instead of the awsd because on my azerty keyboard, awsd is actually zqsd. And it's also different with qwerZ keyboards, let alone korean keyboards. I don't even know what layout korean people have, but I guess it's also different. +## 위치 +코드는 간단해요. 그런데, 저는 WASD 대신 Up/Down/Right/Left 키들을 사용했는데. 왜냐하면- 저는 qwerty가 아니라 azerty 키보드를 쓰고 있고, awsd는 저에겐 zqsd로 보여요. 그리고 qwerZ 키보드들에게는 또 다르게 보일거고. 한글 키보드도 똑같을 거에요. 전 한국인 여러분들이 어떤 키보드 레이아웃을 쓰는지는 모르겠지만. 다를 것이라 생각해요. ``` cpp -// Move forward +// 앞으로 if (glfwGetKey( GLFW_KEY_UP ) == GLFW_PRESS){ position += direction * deltaTime * speed; } -// Move backward +// 뒤로 if (glfwGetKey( GLFW_KEY_DOWN ) == GLFW_PRESS){ position -= direction * deltaTime * speed; } -// Strafe right +// 오른쪽 if (glfwGetKey( GLFW_KEY_RIGHT ) == GLFW_PRESS){ position += right * deltaTime * speed; } -// Strafe left +// 왼쪽 if (glfwGetKey( GLFW_KEY_LEFT ) == GLFW_PRESS){ position -= right * deltaTime * speed; } ``` -The only special thing here is the deltaTime. You don't want to move from 1 unit each frame for a simple reason : +오. 다 괜찮은데.. deltaTime은 처음보네요. 이걸 왜 썼을까요? 용도를 알기전에 우선 간단한 예로 특별한 방법 없이는 일정하게 움직이는게 불가능하다는 걸 설명드릴게요 : -* If you have a fast computer, and you run at 60 fps, you'd move of 60*speed units in 1 second -* If you have a slow computer, and you run at 20 fps, you'd move of 20*speed units in 1 second +* 좋은 컴퓨터를 가지고 게시면, 60프레임으로 실행시키실 거고, 그러면 1초에 60*speed를 이동합니다. +* 느린 컴퓨터를 가지고 계시면, 20프레임으로 실행시키실 거고, 그러면 1초에 20*speed를 이동합니다. -Since having a better computer is not an excuse for going faster, you have to scale the distance by the "time since the last frame", or "deltaTime". +더 좋은 컴퓨터를 사용한다고 더 빨라지면 안되죠! 그래서 일정하게 움직이려면 "마지막 프레임 이후의 시간" - 즉. "deltaTime" 만큼 거리를 조정해주는거에요! -* If you have a fast computer, and you run at 60 fps, you'd move of 1/60 * speed units in 1 frame, so 1*speed in 1 second. -* If you have a slow computer, and you run at 20 fps, you'd move of 1/20 * speed units in 1 second, so 1*speed in 1 second. +* 좋은 컴퓨터를 가지고 게시면, 60프레임으로 실행시키실 거고, 그러면 1 프레임당 1/ 60 * speed 을 움직일 거고, 그래서 1 * speed를 1초에 움직입니다. +* 느린 컴퓨터를 가지고 게시면, 20프레임으로 실행시키실 거고, 그러면 1 프레임당 1/ 20 * speed 을 움직일 거고, 그래서 1 * speed를 1초에 움직입니다. -which is much better. deltaTime is very simple to compute : +뭐가 더 괜찮은진 명백해 보이네요. deltaTime은 계산하기도 아주 간단해요. ``` cpp double currentTime = glfwGetTime(); float deltaTime = float(currentTime - lastTime); ``` -## Field Of View +## Field Of View (FOV 값, 시야각) -For fun, we can also bind the wheel of the mouse to the Field Of View, so that we can have a cheap zoom : +심심하시면, 마우슬 휠을 Fov에 바인딩해서 아주 쉽게 확대 / 축소를 할 수 있습니다. ``` cpp float FoV = initialFoV - 5 * glfwGetMouseWheel(); ``` -## Computing the matrices - -Computing the matrices is now straightforward. We use the exact same functions than before, but with our new parameters. +## 행렬 계산하기 +행렬을 게산하는 건 이제 간단하죠. 예전이랑 똑같은 함수를 쓰지만, 새로운 매게변수를 넣을거에요. ``` cpp -// Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units +// Projection 행렬 : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units ProjectionMatrix = glm::perspective(glm::radians(FoV), 4.0f / 3.0f, 0.1f, 100.0f); -// Camera matrix +// 카메라 행렬 ViewMatrix = glm::lookAt( - position, // Camera is here - position+direction, // and looks here : at the same position, plus "direction" - up // Head is up (set to 0,-1,0 to look upside-down) + position, // 카메라는 여기 있고, + position+direction, // 보는 좌표도 여기 있어요. : 같은 위치에서, "direction"만 더해주세요. + up // 머리는 여기있고요. (set to 0,-1,0 to look upside-down) ); ``` -# Results +# 결과 ![]({{site.baseurl}}/assets/images/tuto-6-mouse-keyboard/moveanim.gif) -## Backface Culling +## 후면 컬링(날리기) -Now that you can freely move around, you'll notice that if you go inside the cube, polygons are still displayed. This can seem obvious, but this remark actually opens an opportunity for optimisation. As a matter of fact, in a usual application, you are never _inside_ a cube. +이제 자유롭게 이동할 수 있는데, 큐브 안에 들어가도 폴리곤들이 계속 그려지네요. 지금은 볼 수 있긴 한데. 게임중에 큐브 '안'으로 들어가는 경우가 어디 있겠어요? 평소에도 사람 안으로 들어가는 일이 없다시피한데요. 그러니까 안은 그리지 맙시다. 최적화를 해요. 그 성능으로 더 많은 일을 할 수 있을거에요. -The idea is to let the GPU check if the camera is behind, or in front of, the triangle. If it's in front, display the triangle; if it's behind, *and* the mesh is closed, *and* we're not inside the mesh, *then* there will be another triangle in front of it, and nobody will notice anything, except that everything will be faster : 2 times less triangles on average ! +아이디어는 간단해요. 카메라가 삼각형의 뒤에 있는지, 아니면 앞에 있는지 GPU가 확인하는 거에요. 앞에 있으면 삼각형을 그리는 거고. 뒤에 있고 - 매쉬가 닫혀있고, 그리고 우리가 매쉬안에 없고, 그리고 다른 삼각형이 앞에 있어서 아무도 볼 수 없다면 그걸 그리지 않는 거죠. 그러면 엄청나게 빨라질 거에요! : 평균적으로, 2배나 그릴 일이 없어요! -The best thing is that it's very easy to check this. The GPU computes the normal of the triangle (using the cross product, remember ?) and checks whether this normal is oriented towards the camera or not. +게다가. 가장 좋은 점은 그걸 확인 하는 것도 아주 쉽다는 거에요. GPU는 삼각형의 법선을 계산하고(외적 쓰는 법, 기억하시죠?), 이 법선이 카메라를 향하고 있는지 확인하는 거에요. -This comes at a cost, unfortunately : the orientation of the triangle is implicit. This means that is you invert two vertices in your buffer, you'll probably end up with a hole. But it's generally worth the little additional work. Often, you just have to click "invert normals" in your 3D modeler (which will, in fact, invert vertices, and thus normals) and everything is just fine. +하지만 비용은 발생해요. 삼각형의 방향은 암시적이죠. 즉, 버퍼에서 두 개의 정점을 뒤집는다면 구멍이 생길 거에요. 하지만 못할 만한 일은 아니죠. 문제가 생긴다면 3D 모델러에서 "반전 법선(invert normals)"을 클릭한다면 (실제로 정점을 뒤집어서 법선도 뒤집어요. ) 다 잘 될거에요. + + +컬링을 활성화 하는 건 아주 쉬워요! : -Enabling backface culling is a breeze : ``` cpp -// Cull triangles which normal is not towards the camera +// 카메라를 향하지 않은 삼각형을 렌더링 하지 않습니다. glEnable(GL_CULL_FACE); ``` -# Exercices +# 연습문제 + +* verticalAngle을 뒤집지 못하도록 제한하세요. +* 오브젝트 주변을 도는 카메라를 만들어보세요. ( position = ObjectCenter + ( radius * cos(time), height, radius * sin(time) ) ); 회전 넓이나, 높이나, 시간을 키보드/마우스에 바인드 해보시고요. 음. 더 괜찮은 아이디어가 있으면 더 좋죠. +* 즐겨 보세요! -* Restrict verticalAngle so that you can't go upside-down -* Create a camera that rotates around the object ( position = ObjectCenter + ( radius * cos(time), height, radius * sin(time) ) ); bind the radius/height/time to the keyboard/mouse, or whatever -* Have fun ! From cae769e9aa05f39b460cf9ca36c1fc79e6831171 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Thu, 1 Nov 2018 12:11:07 +0900 Subject: [PATCH 04/18] Update index.markdown --- .../tutorial-6-keyboard-and-mouse/index.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown index 9049bd744..1c831f1d6 100644 --- a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown +++ b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown @@ -11,7 +11,7 @@ tags: [] language: kr --- 6번째 튜토리얼에 오신 걸 환영합니다! -Welcome for our 6th tutorial ! + 오늘은 FPS 처럼 마우스와 키보드를 이용해 카매라를 어떻게 움직이는 지 알아볼거에요! # 인터페이스 From 5f0c1fca4e0b1b9265de4b9c1d9a4eba15e0b78b Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Thu, 1 Nov 2018 12:16:20 +0900 Subject: [PATCH 05/18] Update index.markdown --- .../index.markdown | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown index 1c831f1d6..902122166 100644 --- a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown +++ b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown @@ -16,9 +16,9 @@ language: kr # 인터페이스 -이 코드는 이제 튜토리얼을 하는 동안 계속 우려먹을 거니까, 파일 분할을 합시다 : common/controls.cpp에 코드를 추가하고, common/controls.hpp에 함수들을 선언해서 tutorial06.cpp가 코드를 인식하게 합시다. +이 코드는 이제 튜토리얼을 하는 동안 계속 우려먹을 거니까, 파일 분할을 해봐요. : common/controls.cpp에 코드를 추가하고, common/controls.hpp에 함수들을 선언해서 tutorial06.cpp가 코드를 인식하게 하세요. -tutorial06.cpp은 저번 튜토리얼과 변경점이 크게 없어요. 주 변경점은 이제 MVP 메트릭스를 한번만 계산하는 대신에, 이제 매 프레임 계산하는 거에요. 그래서, 이 코드를 메인-루프에 추가합시다 ! : +tutorial06.cpp은 저번 튜토리얼과 변경점이 크게 없어요. 주 변경점은 이제 MVP 메트릭스를 한번만 계산하는 대신에, 이제 매 프레임 계산하는 거에요. 그래서, 이 코드를 메인-루프에 추가해봐요 ! : ``` cpp do{ @@ -35,9 +35,9 @@ do{ // ... } ``` -이 코드는 3가지 새로운 함수가 필요합니다 : +이 코드는 3가지 새로운 함수가 필요해요 : -* computeMatricesFromInputs()는 키보드와 마우스, 그리고 Projection 행렬과 View 행력의 계산을 담당합니다. 여기서 모든 일이 일어날거에요! +* computeMatricesFromInputs()는 키보드와 마우스, 그리고 Projection 행렬과 View 행력의 계산을 담당하고 있어요. 여기서 모든 일이 일어날거에요! * getProjectionMatrix()는, 계산한 Projection 행렬을 돌려주기만 합니다. * getViewMatrix()도, 계산한 View 행렬을 돌려주기만 합니다. @@ -63,27 +63,27 @@ float speed = 3.0f; // 초당 3 unitsl float mouseSpeed = 0.005f; ``` -FoV는 줌의 단계에요. 80° = 아주 넓은 각도, 큰 변형. 60° - 45° : 표준. 20° : 큰 줌. +FoV는 줌의 단계에요. 80° = 아주 넓은 각도고, 변형이 심해요. 60° - 45° : 표준이고. 20° : 크게 확대되요. 우린 이제 입력에 따라 position, horizontalAngle, verticalAngle, 그리고 Fov를 수정하고, View, Projection 행렬을 position, horizontalAngle, verticalAngle, 그리고 Fov에 따라 계산할 거에요. ## 방향 결정 -마우스 좌표를 읽는 건 쉽습니다. : +마우스 좌표를 읽는 건 쉬워요. : ``` cpp // 마우스 좌표 받기 int xpos, ypos; glfwGetMousePos(&xpos, &ypos); ``` -하지만, 커서를 화면의 중앙에 되돌려 놓지 않으면 커서가 화면 바깥을 나가게 되버립니다. 그러면 게임을 할 수 없겠죠? +하지만, 커서를 화면의 중앙에 되돌려 놓지 않으면 커서가 화면 바깥을 나가게 되버리는데. 그러면 게임을 할 수 없겠죠? 다시 돌려놓읍시다! ``` cpp // 매 프레임마다, 화면의 중앙으로 마우스 커서를 움직입니다. glfwSetMousePos(1024/2, 768/2); ``` -아. 이 코드는 1024*768에 맞춰저 있는어요. 화면 크기가 다르시다고요? glfwGetWindowSize를 이용하면 현재 화면 크기를 받아 오실 수 있어요. +아. 이 코드는 1024*768에 맞춰져 있어요. 화면 크기가 다르시다고요? glfwGetWindowSize를 이용하면 현재 화면 크기를 받아 오실 수 있어요. 자. 이제 각도를 계산할 수 있겠네요. : ``` cpp @@ -93,12 +93,11 @@ verticalAngle += mouseSpeed * deltaTime * float( 768/2 - ypos ); ``` 그러면, 오른쪽에서 부터 왼쪽으로 읽어볼까요? : -Let's read this from right to left : * 1024/2 - xpos 는 : 얼마나 마우스가 윈도우에 중앙에서 부터 멀어졌는가? 에요. 더 돌고 싶으면, 이 값이 크면 되요. * float(...)는 곱샘이 잘 되도록 정수형에서 실수형으로 바꿔줘요. * mouseSpeed는 회전 속도를 높이거나, 낮출 수 있어요. 괜찮게 튜닝하거나, 그냥 유저가 정하게 내버려둬요. -* += : 만약 마우스를 움직이지 않으면, 1024/2-xpos는 0이 될거고, horizontalAngle+=0이 되어 horizontalAngle이 바뀌지 않겠죠. 만약에 "+="이 아니라, "="을 대신 사용한다면 매 프레임마다 원 상태로 복귀할건데. 그거. 별로 보기 안 좋아요. +* += : 만약 마우스를 움직이지 않으면, 1024/2-xpos는 0이 될거고, horizontalAngle+=0이 되어 horizontalAngle이 바뀌지 않겠죠. 만약에 "+="이 아니라, "="을 대신 사용한다면 매 프레임마다 원 상태로 복귀할건데. 그거, 별로 보기 안 좋아요. 이제 우리는 바라보고 있는 방향을 가지고 있는 벡터를 계산할 수 있어요! @@ -116,11 +115,13 @@ glm::vec3 direction( 위 공식은 3D에 대한 일반화에요. -이제 "up"벡터를 계산해봐요. "Up"은 항상 +Y 방향을 향하는 건 아닌데 - 가령, 아래를 바라본다면. "Up" 벡터가 수평이 되겠네요. - 아래는 동일 위치, 동일 타겟, 그렇지만 다른 "UP" 벡터를 가지는 두개의 카메라를 보여주고 있어요. : +이제 "Up"벡터를 계산해봐요. "Up"은 항상 +Y 방향을 향하는 건 아닌데 - 가령, 아래를 바라본다면. "Up" 벡터가 수평이 되겠네요. - 아래는 동일 위치, 동일 타겟, 그렇지만 다른 "Up" 벡터를 가지는 두개의 카메라를 보여주고 있어요. : ![]({{site.baseurl}}/assets/images/tuto-6-mouse-keyboard/CameraUp.png) -우리의 경우엔, 유일한 상수는 카메라의 오른쪽으로 가는 벡터가 항상 수평이라는 점이에요. 팔을 수평으로 놓고, 어떤 방향으로든 아래를 내려다보시거나- 올려다 보시면 알 수 있을거에요. 그러면. "오른쪽" 벡터를 정의해봅시다. Y 좌표는 수평이니 0이고, X 와 Z 좌표는 위의 그림과 같지만 각도는 90°나 Pi/2 라디안으로 회전할거에요. +지금 우리의 유일한 상수는 카메라의 오른쪽으로 가는 벡터에요. 수평으로 설정되어 있는데... 팔을 수평으로 놓고, 어떤 방향으로든 아래를 내려다보시거나- 올려다 보시면 알 수 있을거에요. 오른쪽 벡터는 바뀌지 않겠죠? 하지만 몸을 돌리면 이야기가 달라지죠. + +이제 한번 "오른쪽" 벡터를 정의해봐요. Y 좌표는 수평이니 0이고, X 와 Z 좌표는 위의 그림과 같지만 각도는 90°나 Pi/2 라디안으로 회전할거에요. ``` cpp // 오른쪽 벡터 From a6be2ecf42f138e14d52045573d7e20dca671853 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Thu, 1 Nov 2018 21:21:20 +0900 Subject: [PATCH 06/18] tutorial 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 수정중. --- .../tutorial-7-model-loading/index.markdown | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown b/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown index 2ec7de12f..502807efa 100644 --- a/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown +++ b/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown @@ -11,14 +11,18 @@ 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. +이 튜토리얼을 가능한 한 심플하게 만들기 위해, 우리는 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). -# Loading the OBJ +# 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 @@ -30,10 +34,15 @@ bool loadOBJ( ) ``` +우리는 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. -## Example OBJ file +## 예제 OBJ 파일 +OBJ 파일은 대체로 이것보다 길거나 - 더 짧아요. : An OBJ file looks more or less like this : ``` @@ -86,8 +95,16 @@ f 1/2/8 2/9/8 3/13/8 f 1/2/8 3/13/8 4/14/8 ``` +그래서, 뭔 뜻일까요?: So : +* '#' 은 그냥 주석이에요. 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 @@ -95,8 +112,16 @@ So : * 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은 삼각형의 두 번째 정점을 표현한거에요. +* 6/10/7은 삼각형의 세 번째 정점을 표현한거에요. +* 첫 번째 정점에서, 8은 사용할 정점의 위치에요. 그러니까 위에 나열된 정점들 중에서 8번째라는 말인데요. 8번째면 -1.000000 1.000000 -1.000000 이겠네요. (C++과 다르게 순서는 0이 아니라 1부터 시작해요.) +* 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) @@ -104,12 +129,18 @@ v, vt and vn are simple to understand. f is more tricky. So, for f 8/11/7 7/12/7 * 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. -## Creating an OBJ file in Blender +## 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) From 2852a4303f130423a59cef7194641c2cdcaee55d Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Fri, 2 Nov 2018 09:13:39 +0900 Subject: [PATCH 07/18] Complete Tutorial 5 --- .../tutorial-7-model-loading/index.markdown | 99 +++++++------------ 1 file changed, 37 insertions(+), 62 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown b/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown index 502807efa..3fbf4fad9 100644 --- a/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown +++ b/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown @@ -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( @@ -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 @@ -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은 삼각형의 두 번째 정점을 표현한거에요. @@ -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; @@ -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"); @@ -167,7 +141,7 @@ if( file == NULL ){ } ``` -Let's read this file until the end : +파일의 끝까지 읽어봐요. : ``` cpp while( 1 ){ @@ -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 ){ @@ -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 ){ @@ -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 ){ @@ -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 ){ @@ -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 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. From d2d1159e0adc10c571e80eac057c5b3412b9292d Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 14:15:18 +0900 Subject: [PATCH 08/18] Tutorial 3 Updated - All Translation. All Translation. --- .../tutorial-3-matrices/index.markdown | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-3-matrices/index.markdown b/kr/beginners-tutorials/tutorial-3-matrices/index.markdown index 304da24ef..99a7a1afc 100644 --- a/kr/beginners-tutorials/tutorial-3-matrices/index.markdown +++ b/kr/beginners-tutorials/tutorial-3-matrices/index.markdown @@ -161,7 +161,7 @@ glm::vec3 myRotationAxis( ??, ??, ??); glm::rotate( angle_in_degrees, myRotationAxis ); ``` -## Cumulating transformations +## 누적 변환 이제 우리는 벡터들을 어떻게 회전하고, 평행이동하고, 스케일 하는지 알게 되었습니다. 이들 변환들을 합칠수 있다면 굉장하겠죠. 이는 매트릭스들을 함께 곱함으로서 이루어집니다. 예를 들어 : @@ -169,43 +169,49 @@ glm::rotate( angle_in_degrees, myRotationAxis ); TransformedVector = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalVector; ``` -**!!! 주의사항 !!!** 이 라인은 실제로는 스케일리을 먼.저. 하고나서 그 다.음.에 회전하고, 그 다.음.에 평행이동 합니다. 이것이 매트릭스 곱이 동작하는 법이에요. +**!!! 주의사항 !!!** 이 라인은 실제로는 **스케일링**을 먼저 하고나서 그 다음에 회전하고, 그 다음에 평행이동 합니다. 이것이 매트릭스 곱이 동작하는 법이에요. 연산을 다른 순서로 작성하는 것은 같은 결과를 내주지 않을 겁니다. 스스로 해보세요 : -- make one step ahead ( beware of your computer ) and turn left; +- 한 걸음 간 다음에 (아, 컴퓨터 조심하세요.) 왼쪽으로 돌아보세요. -- turn left, and make one step ahead +- 왼쪽으로 돈 다음에, 한 걸음 가보세요. + +실제로, 위 순서는 게임 캐릭터나 다른 것들에게 꼭 필요한 것들이에요. : 확대가 필요하다면, 먼저 한 다음. 방향을 전하고, 이동하는 거에요. 예를 들어 볼까요? 배를 하나 가져다 놓을게요. As a matter of fact, the order above is what you will usually need for game characters and other items : Scale it first if needed; then set its direction, then translate it. For instance, given a ship model (rotations have been removed for simplification) : -* The wrong way : - - You translate the ship by (10,0,0). Its center is now at 10 units of the origin. - - You scale your ship by 2\. Every coordinate is multiplied by 2 _relative to the origin_, which is far away... So you end up with a big ship, but centered at 2*10 = 20. Which you don't want. -* The right way : - - You scale your ship by 2\. You get a big ship, centered on the origin. - - You translate your ship. It's still the same size, and at the right distance. +* 틀린 방법 : + - 배를 (10, 0, 0) 으로 이동했어요. 이제 원점으로 부터 10 unit 떨어져 있네요. + - 그리고 배를 2배로 키웠어요. 모든 좌표는 원점으로부터 상대적으로 2배 더 커졌고요. 어유. 멀리도 갔네. 확대가 끝나면 큰 배는 가지겠지만. 이제 원점으로 부터 2*10 = 20이나 멀어졌네요. 원하지 않는 선물을 받아버렸어요. + +* 올바른 방법 : + - 배를 2배 키워요. 그러면 큰 배를 얻었고, 원점에 잘 있죠. + - 그리고 배를 이동시켜요. 여전히 같은 크기를 가지고 있고, 올바른 방향을 가지고 있죠. + + +행렬 - 행렬 곱은 행렬 - 벡터 곱가 아주 유사해요, 그래서 구체적인 내용을 생략하고. 대신 참고가 [될 만한 링크를(Matricesa and Quaternions FAQ)]({{site.baseurl}}/assets/faq_quaternions/index.html#Q11)걸어드릴게요. 만약에 행렬곱이 필요하다면. 지금은 그냥 컴퓨터에게 맡깁시다. : Matrix-matrix multiplication is very similar to matrix-vector multiplication, so I'll once again skip some details and redirect you the the [Matrices and Quaternions FAQ]({{site.baseurl}}/assets/faq_quaternions/index.html#Q11) if needed. For now, we'll simply ask the computer to do it : -**in C++, with GLM :** +**GLM과 함깨, C++에선. :** ``` cpp glm::mat4 myModelMatrix = myTranslationMatrix * myRotationMatrix * myScaleMatrix; glm::vec4 myTransformedVector = myModelMatrix * myOriginalVector; ``` -**in GLSL :** +**GLSL에선. :** ``` glsl mat4 transform = mat2 * mat1; vec4 out_vec = transform * in_vec; ``` -# The Model, View and Projection matrices +# 모델, 뷰, 프로젝션 행렬 -_튜토리얼의 남은 부분들에서는, 우리가 블랜더의 가장 사랑받는 3d 모델-키 수자네-을 그릴줄 이미 알고 있었다고 하고 진행합니다_ +*튜토리얼의 남은 부분들에서는, 우리가 블랜더의 가장 사랑받는 3d 모델-키 수자네-을 그릴줄 이미 알고 있었다고 하고 진행합니다* 모델과 뷰, 프로젝션 매트릭스는 변환들을 분명하게 구별하기 위한 좋은 도구 입니다. 이들을 안 쓸수도 있습니다 (여기까지 우리가 튜토리얼 1과 2에서 했던것 처럼). 하지만 써야만 합니다. 모든 사람이 이렇게 합니다. 왜냐면 이렇게 하는게 쉬운 길이니까요. @@ -284,7 +290,6 @@ glm::mat4 projectionMatrix = glm::perspective( ); ``` -One last time : 마지막으로 : _우리는 카메라 공간 (모든 버텍스들이 카메라 좌표에 상대적) 에서 호모니지어스 공간 (모든 버텍스들이 작은 큐브 안에 정의되고, 큐브안에 있는 모든 것들은 화면에 띄어집니다)으로 갔습니다._ @@ -313,7 +318,7 @@ _우리는 카메라 공간 (모든 버텍스들이 카메라 좌표에 상대 그리고 여기 실제로 랜더 되는 이미지가 있네요 ! -## 변환들 쌓기 : 모델뷰 매트릭스 +## 변환 쌓기 : 모델뷰 매트릭스 ... 당신이 이미 사랑했었던 일반적인 매트릭스 곱과 같습니다 ! @@ -336,7 +341,7 @@ transformed_vertex = MVP * in_vertex; // 프로젝션 매트릭스 : 45도 시야각, 4:3 비율, 시야 범위 : 0.1 유닛 <--> 100 유닛 glm::mat4 Projection = glm::perspective(glm::radians(45.0f), (float) width / (float)height, 0.1f, 100.0f); - // 혹은 ortho(직교) 카메라에선 : + //혹은 ortho(직교) 카메라에선 : //glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f); // 월드 좌표로 표현 // 카메라 매트릭스 @@ -352,46 +357,50 @@ transformed_vertex = MVP * in_vertex; glm::mat4 mvp = Projection * View * Model; // 기억하세요, 행렬곱은 계산은 반대순서로 이루어집니다 ``` -* Second step : give it to GLSL +* 두번째 : GLSL에게 줘버려요. ``` cpp - // Get a handle for our "MVP" uniform - // Only during the initialisation + // 우리의 "MVP" 행렬에 참조를 얻습니다. + // 아. 초기화때만 하셔야 해요. GLuint MatrixID = glGetUniformLocation(programID, "MVP"); - // Send our transformation to the currently bound shader, in the "MVP" uniform - // This is done in the main loop since each model will have a different MVP matrix (At least for the M part) + // 현재 바인딩된 쉐이더에게 변환한 메트릭스를 보냅시다. 방금 얻은 참조로요. + // 이건 각각의 모델마다 다른 MVP 행렬을 가지고 있을 것이니, 메인 루프에 해줍시다. (VP는 같을지 몰라도, M은 다를거에요.) glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &mvp[0][0]); ``` -* Third step : use it in GLSL to transform our vertices +* 세번쨰 : 우리가 GLSL에게 넘겨준 행렬을 정점에 적용시킵시다. ``` glsl - // Input vertex data, different for all executions of this shader. + // vertex 데이터 입력 값, 쉐이더의 실행때마다 값이 다릅니다. layout(location = 0) in vec3 vertexPosition_modelspace; - // Values that stay constant for the whole mesh. + // 이 값은 한 매쉬동안은 상수적입니다. uniform mat4 MVP; void main(){ + // 정점의 출력 좌표, clip space에선 : MVP * position // Output position of the vertex, in clip space : MVP * position gl_Position = MVP * vec4(vertexPosition_modelspace,1); } ``` + {: .highlightglslvs } -* Done ! Here is the same triangle as in tutorial 2, still at the origin (0,0,0), but viewed in perspective from point (4,3,3), heads up (0,1,0), with a 45° field of view. +* 끝났어요! 튜토리얼 2와 같은 삼각형이 있을거에요. 아직도 원점 (0,0,0)에 있는 거 말이죠. 하지만 perspective로 (4,3,3) 좌표에서, 상향 벡터는 (0,1,0)으로 줬으니 45도 각도로 보일거에요. ![]({{site.baseurl}}/assets/images/tuto-3-matrix/perspective_red_triangle.png) -In tutorial 6 you'll learn how to modify these values dynamically using the keyboard and the mouse to create a game-like camera, but first, we'll learn how to give our 3D models some colour (tutorial 4) and textures (tutorial 5). +튜토리얼 6에서는 키보드와 마우스를 이용해서 어떻게 우리가 오늘 썼던 값을 다이나믹하게 바꿀 수 있는지 배울거에요 - 마치 게임 카메라 같을걸요? - 하지만. 우선은 우리의 3D 모델에 어떻게 색상을 넣는지 (tutorial 4)와 텍스쳐를 넣을지부터 배울거에요. (tutorial 5) + + +# 연습문제 -# Exercises -* Try changing the glm::perspective -* Instead of using a perspective projection, use an orthographic projection (glm::ortho) -* Modify ModelMatrix to translate, rotate, then scale the triangle -* Do the same thing, but in different orders. What do you notice ? What is the "best" order that you would want to use for a character ? +* glm::perspective를 한번 바꿔보세요. +* perspective(원근법) projection(투영법)을 쓰는 대신, orthographic projection을 써보세요. (glm::ortho) +* ModelMatrix를 이동하고, 회전하고, 확대해서 삼각형을 수정해보세요.ㅗ +* 같은 걸 해보시는데, 한번 다른 순서로 해보시겠어요? 어떤게 최고의 방법이었나요? 어떤 순서로 해야 캐릭터가 예쁘게 보일 것 같았나요? _Addendum_ From 9f462c302ef031eaf57a4584da94b78c6cd21de4 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 17:29:46 +0900 Subject: [PATCH 09/18] Tutorial 8 Tranlation to korean first Complete. --- .../tutorial-8-basic-shading/index.markdown | 224 +++++++++--------- 1 file changed, 115 insertions(+), 109 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown b/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown index 1626c0816..40d03ecdc 100644 --- a/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown +++ b/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown @@ -1,4 +1,4 @@ ---- + --- layout: tutorial status: publish published: true @@ -11,36 +11,36 @@ tags: [] language: kr --- -In this 8th tutorial, we will learn how to do some basic shading. This includes : +8번째 튜토리얼에선, 이제 어떻게 기초적인 쉐이딩을 하는지 배울거에요. 아래 내용을 포함해요: -* Beeing more bright when closer to a light source -* Having highlights when looking in the reflection of a light (specular lighting) -* Beeing darker when light is not directly towards the model (diffuse lighting) -* Cheating a lot (ambient lighting) +* 광원에 가까워질 때 더 밝아지는 법. +* 빛의 반사로 하이라이트를 만드는 법. (Specular Lighting - 반사 조명) +* 빛이 모델을 직접 향하지 않아도 빛이 보이는 법. (Diffuse Lighting - 기본 조명) +* 개쩌는 속임수. (ambient Lighting) -This does NOT include : +아래는 포함하지 않아요 : -* Shadows. This is a broad topic that deserves its own tutorial(s) -* Mirror-like reflections (this includes water) -* Any sophisticated light-matter interaction like subsurface scattering (like wax) -* Anisotrophic materials (like brushed metal) -* Physically based shading, which tries to mimic the reality closely -* Ambient Occlusion (it's darker in a cave) -* Color Bleeding (a red carpet will make a white ceiling a litte bit red) -* Transparency -* Any kind of Global Illumination whatsoever (it's the name that regroups all previous ones) +* 그림자. 이건 따로 튜토리얼을 만들어야 할 정도로 광대한 주제에요. +* 거울 같은 반사 (물 포함해서.) +* 표면이 산란하는 것 같은 정됴한 빛 - 물질 상호 작용 (왁스같이 번쩍 번쩍.) +* 이방성 표면 (잘 닦은 금속과 같은. ) +* 물리 기반 셰이딩, 현실과 비슷한. +* 주변 폐색하기 (역주 : 빛이 안 들어오는 곳에 안 들어오게 하는 거.)(동굴에서 더 어두워 짐.) +* 색상 번지게 하기. (레드 카펫이 하얀 천장을 붉게 만듭니다. ) +* 투명도 +* 모든 종류의 글로벌 일루미네이션(GI) (더럽게 어려워!) -In a word : Basic. +한 마디로 : 기초죠. -# Normals +# 법선 벡터(normals) -During the last few tutorials you've been dealing with normal without really knowing what they were. +지금까지 튜로리얼에서 normal을 소개를 못 드렸어요. 드디어 오늘 소개 드릴게요! -## Triangle normals +## 삼각형 법선 -The normal of a plane is a vector of length 1 that is perpendicular to this plane. +평면의 법선이란, 길이가 1이고 평면에 수직인 벡터를 말해요. -The normal of a triangle is a vector of length 1 that is perpendicular to this triangle. It is easily computed by taking the cross product of two of its edges (the cross product of a and b produces a vector that is perpendicular to both a and b, remember ?), and normalized : its length is brought back to 1. In pseudo-code : +삼각형의 법선은 그럼 길이가 1이고, 삼각형에 수직인 백터를 말하겠죠? 이건 가장자리에 있는 두 벡터를 외적(외적은 a,b 두 백터의 수직 백터를 구하는 작업이었죠. 기억하시죠? )함으로써 쉽게 구할 수 있고, 일반화 시킬 수도 있죠 : 일반화는, 길이를 1로 만드는 작업을 말해요. 의사코드를 보죠!: ``` triangle ( v1, v2, v3 ) @@ -49,21 +49,22 @@ edge2 = v3-v1 triangle.normal = cross(edge1, edge2).normalize() ``` -Don't mix up normal and normalize(). Normalize() divides a vector (any vector, not necessarily a normal) by its length so that its new length is 1. normal is just the name for some vectors that happen to represent, well, a normal. +normal과 normalize()를 햇갈리지 마세요. Normailize()는 새 길이가 1이 되도록 벡터를 길이로 나누는 작업이에요. normal은 그 백터를 표현할 수 있는 이름이에요. 음. normal말이에요. -## Vertex normals +## 정점의 법선들 -By extension, we call the normal of a vertex the combination of the normals of the surroundings triangles. This is handy because in vertex shaders, we deal with vertices, not triangles, so it's better to have information on the vertex. And any way, we can't have information on triangles in OpenGL. In pseudo-code : -``` +확장하면, 정점의 법선은 주위 삼각형의 법선의 조합이라고 할 수 있어요. Vertex Shader는 앞서 말한 삼각형은 다루지 않고, 정점들을 다루니까 그렇게 말하는 게 더 편할거에요. 어차피 OpenGL에서는 삼각형들에 대한 정보를 얻을 길이 없으니까요. 의사 코드에요 : + +``` vertex v1, v2, v3, .... triangle tr1, tr2, tr3 // all share vertex v1 v1.normal = normalize( tr1.normal + tr2.normal + tr3.normal ) ``` -## Using vertex normals in OpenGL +## OpenGL에서 정점의 법선을 사용하기. -To use normals in OpenGL, it's very easy. A normal is an attribute of a vertex, just like its position, its color, its UV coordinates... so just do the usual stuff. Our loadOBJ function from Tutorial 7 already reads them from the OBJ file. +OpenGL에서 법선을 쓰는 건, 아주 쉬워요! 법선은 정점의 속성이에요. 마치 위치나, 색상이나, UV 좌표 같은 것들 말이에요. 그러니까 해온 대로 합시다. 이미 우리의 loadOBJ 파일에서는 OBJ 파일을 읽으면서 모든 준비를 맞춘 것 같네요. ``` cpp GLuint normalbuffer; @@ -75,7 +76,7 @@ GLuint normalbuffer; and ``` cpp - // 3rd attribute buffer : normals + // 3번째 버퍼의 속성 : 법선들 glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glVertexAttribPointer( @@ -88,64 +89,65 @@ and ); ``` -and this is enough to get us started. +자. 이제 시작하기는 충분해 보여요. -# The Diffuse part +# Diffuse 부분 -## The importance of the surface normal +## 표면 법선의 중요성 -When light hits an object, an important fraction of it is reflected in all directions. This is the "diffuse component". (We'll see what happens with the other fraction soon) +빛이 물체에 닿을 때, 중요한 점은 모든 방향으로 반사된다는 점이에요. 그걸 "확산 요소"라고 불러요. (곧 있으면 도대체 무슨 일이 벌어지는 지 알 수 있을거에요.) ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuseWhite1.png) -When a certain flux of light arrives at the surface, this surface is illuminated differently according to the angle at which the light arrives. +어떤 빛이 표면에 도달하면, 표면은 빛이 도달한 각도에 따라 다르게 조명이 되요. -If the light is perpendicular to the surface, it is concentrated on a small surface. If it arrives at a gazing angle, the same quantity of light spreads on a greater surface : +만약 빛이 표면으로부터 수직으로 들어오면, 작은 표명에 집중되겠죠. 지금 보고 있는(시야) 각도에 도달하면, 같은 빛이 더 넓은 표면에 확산될 거에요 : ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuseAngle.png) -This means that each point of the surface will look darker with gazing light (but remember, more points will be illuminated, so the total quantity of light will remain the same) +그러니까. 표면의 각 점은 수직보다는 응시각도일 때 더 어두워보이겠죠? 왜냐하면 상대적으로 더 적은 빛이 들어오니까요. (하지만 기억하세요, 응시각도일때 더 많은 빛이 조명될거니. 결국 최종적으로는 같을거에요. ) -This means that when we compute the colour of a pixel, the angle between the incoming light and the surface normal matters. We thus have : +각 점이라는 말은 . 우리는 각각의 픽셀의 색상을 계산할 때 들어오는 빛과 표면 간의 각도가 중요하다는 것이죠. 이렇게 해볼 수 있을 것 같네요 : ``` glsl -// Cosine of the angle between the normal and the light direction, -// clamped above 0 -// - light is at the vertical of the triangle -> 1 -// - light is perpendicular to the triangle -> 0 +// 빛과 법선사이의 각도에 대해 cosine을 구할게요. (실제로 cos함수를 쓰지는 않아요. 왜냐하면 연산량을 먹을 뿐더러, 두 벡터가 단위 벡터(길이가 1이라면)라는 가정하에서 내적은 두 벡터의 각도의 cos 과 같게 나오니까요. - 역주 ) +// 법선 백터와 빛 백터 사이의 코사인 각도. +// 0으로 clamp 됨. +// - 빛이 삼각형에 대해서 수직임. -> 1 +// - 빛이 삼각형에 대해서 수평임. -> 0 float cosTheta = dot( n,l ); color = LightColor * cosTheta; ``` {: .highlightglslfs } -In this code, n is the surface normal and l is the unit vector that goes from the surface to the light (and not the contrary, even if it's non inuitive. It makes the math easier). +이 코드에서는, n은 표면의 법선이고, l은 표면으로 가는 빛의 단위 벡터에요. (표현이 글러먹은 것 같지만. 이게 좀... 직관적일거에요. 수학을 쉽게 만드려고 했어요.) -## Beware of the sign +## 사인을 조심하세요. -Something is missing in the formula of our cosTheta. If the light is behind the triangle, n and l will be opposed, so n.l will be negative. This would mean that colour = someNegativeNumber, which doesn't mean much. So we have to clamp cosTheta to 0 : + +우리의 cosTheta 공시에 뭐가 빠진 것 같아요. 빛이 삼각형 뒤에 있으면, n과 l이 뒤에 있다는 말이니까... n과 l의 내적은 음수가 되겠네요. 그러면 color = 음수숫자 가 되버리는데. 그건 그리 좋지 않을 것 같아요. 그러니까 cosTheat를 0으로 clamp 합시다. : ``` glsl -// Cosine of the angle between the normal and the light direction, -// clamped above 0 -// - light is at the vertical of the triangle -> 1 -// - light is perpendicular to the triangle -> 0 -// - light is behind the triangle -> 0 +// 법선 백터와 빛 백터 사이의 코사인 각도. +// 0으로 clamp 됨. +// - 빛과 법선이 수직이면 -> 1 +// - 빛과 법선이 수평이면 -> 0 +// - 빛이 삼각형 뒤에 있으면 ->0 float cosTheta = clamp( dot( n,l ), 0,1 ); color = LightColor * cosTheta; ``` {: .highlightglslfs } -## Material Color - -Of course, the output colour also depends on the colour of the material. In this image, the white light is made out of green, red and blue light. When colliding with the red material, green and blue light is absorbed, and only the red remains. +## Material 색깔 +물론, 출력 색상도 재질의 색상에 따라 다르겠죠. 이 이미지에서 흰색 빛은 녹색, 빨간색과 파란색 빛으로 만들어져요. 적색으로 뒤덮인 물체랑 충돌하면, 녹색과 청색 빛은 흡수디고 적색만 남겠죠? ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuseRed.png) - +우리는 이걸 간단한 곱샘으로 표현할 수 있어요 : We can model this by a simple multiplication : ``` glsl @@ -153,93 +155,96 @@ color = MaterialDiffuseColor * LightColor * cosTheta; ``` {: .highlightglslfs } -## Modeling the light +## 빛을 모델링하기. -We will first assume that we have a punctual light that emits in all directions in space, like a candle. +자. 먼저 촛불이랑 비슷하게, 모든 방향으로 방출하는 빛을 가지고 있다고 가정해봐요. -With such a light, the luminous flux that our surface will receive will depend on its distance to the light source: the further away, the less light. In fact, the amount of light will diminish with the square of the distance : +그런 빛은 물질의 표면에 닿을 빛의 양이 광원까지의 거리에 따라 달라지겠죠? 멀리 떨어져 있을 수록 빚이 적죠. 실제로도, 빛의 양은 거리의 제곱으로 서서히 줄어들어요 : ``` glsl color = MaterialDiffuseColor * LightColor * cosTheta / (distance*distance); ``` {: .highlightglslfs } -Lastly, we need another parameter to control the power of the light. This could be encoded into LightColor (and we will in a later tutorial), but for now let's just have a color (e.g. white) and a power (e.g. 60 Watts). +마지막으로, 우리는 빛의 세기를 조정할 매개변수가 필요하겠네요. 그걸 LightColor라고 정해요. (나중에 설명할 예정이에요. ) 그럼 이제, 우리는 빛의 색깔이랑 (예 : 흰색), 세기로 나타낼 수 있겠네요. (예 : 60 와트) ``` glsl color = MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance); ``` {: .highlightglslfs } -## Putting it all together +## 다 넣어보기 -For this code to work, we need a handful of parameters (the various colours and powers) and some more code. +자. 위에서 설명한 코드가 돌아가려면 매개 변수 몇개랑(아까 말한 색상과 힘이요.) 더 많은 코드가 필요해요. -MaterialDiffuseColor is simply fetched from the texture. +MaterialDiffuseColor는 간단하게 텍스쳐에서 가져옵시다. -LightColor and LightPower are set in the shader through GLSL uniforms. +LightColor와 LightPower는 GLSL을 통해 셰이더로 보내면 될 것 같고요. -cosTheta depends on n and l. We can express them in any space provided it's the same for both. We choose the camera space because it's easy to compute the light's position in this space : +cosTheta는 n과 l에 의존하고 있죠. l은 어디서나 끌어오면 되는데. 카메라 공간에서 끌고 오는 게 제일 쉬워보이니까. 카메라 공간에서 끌어옵시다! : ``` glsl -// Normal of the computed fragment, in camera space + // fragment shader에서 계산된 법선, 카메라 공간 기준. vec3 n = normalize( Normal_cameraspace ); - // Direction of the light (from the fragment to the light) + // 빛의 방향. vec3 l = normalize( LightDirection_cameraspace ); ``` {: .highlightglslfs } -with Normal_cameraspace and LightDirection_cameraspace computed in the Vertex shader and passed to the fragment shader : +Normal_cameraspace와 LightDirection_cameraspace는 Vertex Shader에서 계산을 끝내고, fragment shader로 보내줄거에요 : ``` glsl -// Output position of the vertex, in clip space : MVP * position +// 정점의 최종 좌표, 화면 상에선 : MVP * position gl_Position = MVP * vec4(vertexPosition_modelspace,1); -// Position of the vertex, in worldspace : M * position +// 정점의 월드 좌표, 월드 공간에선 : M * position Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz; -// Vector that goes from the vertex to the camera, in camera space. -// In camera space, the camera is at the origin (0,0,0). +// 정점에서 카메라 공간을 향하는 벡터. +// 카메라 공간에선, 카메라는 원점에 있습니다. vec3 vertexPosition_cameraspace = ( V * M * vec4(vertexPosition_modelspace,1)).xyz; EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace; -// Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity. +// 카메라 공간에서 빛으로, 그리고 정점으로 가는 벡터. M은 단위 행렬 (숫자로 치면 1 - 역주) 이기에 생략했습니다. vec3 LightPosition_cameraspace = ( V * vec4(LightPosition_worldspace,1)).xyz; LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace; -// Normal of the the vertex, in camera space +// 카메라 공간에서 정점의 법선 벡터. Normal_cameraspace = ( V * M * vec4(vertexNormal_modelspace,0)).xyz; // Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not. ``` {: .highlightglslvs } -This code can seem impressive but it's nothing we didn't learn in Tutorial 3 : Matrices. I paid attention to write the name of the space in each vector's name, so that keeping track of what is happening is much easier. **You should do that, too.** +이 코드는 Tutorial 3 : 메트릭스랑 비슷하게 보이지만. 거기서 배웠던 거랑 달라요. 아. 유의할 점이 하나 있어요. 지금 코드에는 각 벡터의 이름에 어느 공간인지 표시해주고 있죠? (예를 들면, Normal_cameraspace처럼요. - 역주) 이렇게 하면 이 벡터를 잘못 쓸 일이 없을거에요! 잘못 쓸 일이 뭐냐고요? 예를 들면 월드 공간에 있는 걸 카메라 공간이랑 곱하는 일을 피할 수 있어요. 피트랑 미터법이 다른 것처럼. 그런 원천적인 실수를 봉인할 수 있죠. + +이렇게 길게 설명한 이유는 다들 아실거에요. **당연히 이걸 보는 여러분도 이렇게 하셔야 해요!** -M and V are the Model and View matrices, which are passed to the shader in the exact same way as MVP. +는 모델과 뷰 행렬을 말하는데요, MVP와 같은 방식으로 넘겨주시면 되요. -## Time for work +## 일할 시간! -You've got everything you need to code a diffuse lighting. Go ahead, and learn the hard way :) +자. 이제 Diffuse Lighting에 필요한 모든 코드가 준비 되었어요. 먼저 해보시고, 열심히 배워봐요! :) -## Result +## 결과 -With only the Diffuse component, we have the following result (sorry for the lame texture again) : +Diffuse만 사용하면, 이런 결과가 나올 거에요. (구린 텍스쳐에 다시 한 번 사과드려요) : ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuse_only.png) -It's better than before, but there is still much missing. In particular, the back of Suzanne is completely black since we used clamp(). +저번보다는 괜찮지만, 뭔가 많이 빠진 것 같네요. 특히. Clamp를 써서 그런가 모델링의 뒤는 완전히 검은색이에요. -# The Ambient component +# 주변광 -The Ambient component is the biggest cheat ever. +주변광은 엄청나게 멋져요. -We expect the back of Suzanne to be receive more light because in real life, the lamp would light the wall behind it, which would in turn (slightly less) light the back of the object. +봐요. 실제 상황에서는 렘프의 빛이 벽에 부딪치고, 그게 반사되고, 반사되고, 반사되서 결국 모델링의 뒤에 빛이 조금이라도 갈거에요. 그런데 지금은 어떻죠? 그냥 검정색이죠. -This is awfully expensive to compute. +그런데. 그걸 진짜로 다 계산하기에는 너무 시간이 많이 걸려요. -So the usual hack is to simply fake some light. In fact, is simply makes the 3D model *emit *light so that it doesn't appear completely black. +그래서, 일반적인 편법은 간단히 약간의 가짜 빛을 추가하는거죠. 그러니까. 3D 모델이 약간의 빛을 자체적으로 방출해서 완벽하게 검은 색으로 보이지 않게 하는거에요. -This can be done this way : + +그럼. 이렇게 짤 수 있겠죠? : ``` glsl vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor; @@ -248,65 +253,66 @@ vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor; ``` glsl color = - // Ambient : simulates indirect lighting + // 주변 광 : 간접광을 시뮬레이션 합니다. MaterialAmbientColor + - // Diffuse : "color" of the object + // 확산 광 : 오브젝트의 "색깔"입니다. MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) ; ``` {: .highlightglslfs } -Let's see what it gives +무슨 결과가 나오는 지 한번 봅시다! -## Results +## 결과 -Ok so that's a little bit better. You can adjust the (0.1, 0.1, 0.1) if you want better results. +오. 좀 더 괜찮아 보이네요. 만약에 더 괜찮은 결과를 보고 싶으시면, (0.1, 0.1, 0.1)로 조정해도 될 것 같아요. ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuse_ambiant.png) -# The Specular component +# 반사광 -The other part of light that is reflected is reflected mostly in the direction that is the reflection of the light on the surface. This is the specular component. +반사되는 빛의 대부분은, 표면에서 빛이 들어오는 방향으로 반사되요. 그게 반사광이죠. ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/specular.png) -As you can see in the image, it forms a kind of lobe. In extreme cases, the diffuse component can be null, the lobe can be very very very narrow (all the light is reflected in a single direction) and you get a mirror. +이미지에서 볼 수 있듯. 일종의 로브(망토처럼 퍼져 보이죠? - 역주)를 형성해요. 하지만 극단적일 경우엔 확산 요소가 없을 지도 모르니. 로브는 아주, 아주, 아주 작을 지도 모르죠. (모든 빛은 단일 방향으로 반사되니까요.) 그러면 - 거울이 되는 거에요. 모든 빛을 정반사하니까요. -(*we can indeed tweak the parameters to get a mirror, but in our case, the only thing we take into account in this mirror is the lamp. So this would make for a weird mirror)* +(*거울을 만들려고 매개 변수를 조정할 수는 있겠는데. 지금 경우에는 반사할 수 있는게 광원 밖에 없어요. 그건 거울이 아니라, 가짜 거울이죠.)* ``` glsl -// Eye vector (towards the camera) +// 눈 벡터 - 카메라 쪽. vec3 E = normalize(EyeDirection_cameraspace); -// Direction in which the triangle reflects the light +// 삼각형이 빛을 반사하는 방향. vec3 R = reflect(-l,n); -// Cosine of the angle between the Eye vector and the Reflect vector, -// clamped to 0 -// - Looking into the reflection -> 1 -// - Looking elsewhere -> < 1 +// 눈 벡터와 반사 벡터 사이의 코사인 각도, +// 0으로 clamp 됨. +// - 반사광을 들어다보고 있으면 -> 1 +// - 다른 곳을 보고 있으면 -> < 1 float cosAlpha = clamp( dot( E,R ), 0,1 ); color = - // Ambient : simulates indirect lighting + // 주변광 : 간접 조명 시뮬레이션 MaterialAmbientColor + - // Diffuse : "color" of the object - MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) ; - // Specular : reflective highlight, like a mirror + // 확산광 : 오브젝트의 "색깔" + MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) + + // 반사광 : 반사 하이라이트, 거울 같이. MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance*distance); ``` {: .highlightglslfs } -R is the direction in which the light reflects. E is the inverse direction of the eye (just like we did for "l"); If the angle between these two is little, it means we are looking straight into the reflection. +R은 빛의 반사 방향이에요. E는 눈(우리가"l"이라고 말한거 말이에요.)의 반대 방향이고요; 만약에 이 둘 사이에 각도가 작으면, 반사광을 똑바로 보고 있음을 말하는 거겠죠? -pow(cosAlpha,5) is used to control the width of the specular lobe. Increase 5 to get a thinner lobe. +pow(cosAlpha,5)는 반사광의 로브의 넓이를 조정하는 데 쓸 수 있어요. 5보다 더 증가하면 - 얇은 로브를 얻을 수 있죠. (아마, 지금 반사하는 곳을 '로브'같다고 해서 로브라고 하는 것 같아요 - 역주 ) -## Final result +## 최종 결과 ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuse_ambiant_specular.png) +코와 눈쪽에 반사 하이라이트가 있는 거, 보이시죠? + +이 쉐이딩 방법은 간단해서 수년 동안 사용되어 왔는데요. 하지만 여러가지 문제가 있어서, microfaect BRDF같은 물리 -기반-렌더링으로 대체되고 있어요. 그건 나중에 알아봐요. -Notice the specular highlights on the nose and on the eyebrows. -This shading model has been used for years due to its simplicity. It has a number of problems, so it is replaced by physically-based models like the microfacet BRDF, but we will see this later. +다음 튜토리얼에선, VBO라는 것을 이용해서 성능을 어떻게 높일지 알아볼거에요! 그게 첫 중급 튜토리얼이 될거에요! 그러니까. 초급 튜토리얼은 끝났어요! 여기까지 따라와주신 여러분들. 축하드려요! 그럼 힘내서 중급 튜토리얼까지 가봅시다! -In the next tutorial, we'll learn how to improve the performance of your VBO. This will be the first Intermediate tutorial ! From f94e32ede62d236b7062bf87dc2f0a343d8dd8b2 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 14:15:18 +0900 Subject: [PATCH 10/18] Tutorial 3 Updated - All Translation. All Translation. --- .../tutorial-3-matrices/index.markdown | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-3-matrices/index.markdown b/kr/beginners-tutorials/tutorial-3-matrices/index.markdown index 304da24ef..99a7a1afc 100644 --- a/kr/beginners-tutorials/tutorial-3-matrices/index.markdown +++ b/kr/beginners-tutorials/tutorial-3-matrices/index.markdown @@ -161,7 +161,7 @@ glm::vec3 myRotationAxis( ??, ??, ??); glm::rotate( angle_in_degrees, myRotationAxis ); ``` -## Cumulating transformations +## 누적 변환 이제 우리는 벡터들을 어떻게 회전하고, 평행이동하고, 스케일 하는지 알게 되었습니다. 이들 변환들을 합칠수 있다면 굉장하겠죠. 이는 매트릭스들을 함께 곱함으로서 이루어집니다. 예를 들어 : @@ -169,43 +169,49 @@ glm::rotate( angle_in_degrees, myRotationAxis ); TransformedVector = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalVector; ``` -**!!! 주의사항 !!!** 이 라인은 실제로는 스케일리을 먼.저. 하고나서 그 다.음.에 회전하고, 그 다.음.에 평행이동 합니다. 이것이 매트릭스 곱이 동작하는 법이에요. +**!!! 주의사항 !!!** 이 라인은 실제로는 **스케일링**을 먼저 하고나서 그 다음에 회전하고, 그 다음에 평행이동 합니다. 이것이 매트릭스 곱이 동작하는 법이에요. 연산을 다른 순서로 작성하는 것은 같은 결과를 내주지 않을 겁니다. 스스로 해보세요 : -- make one step ahead ( beware of your computer ) and turn left; +- 한 걸음 간 다음에 (아, 컴퓨터 조심하세요.) 왼쪽으로 돌아보세요. -- turn left, and make one step ahead +- 왼쪽으로 돈 다음에, 한 걸음 가보세요. + +실제로, 위 순서는 게임 캐릭터나 다른 것들에게 꼭 필요한 것들이에요. : 확대가 필요하다면, 먼저 한 다음. 방향을 전하고, 이동하는 거에요. 예를 들어 볼까요? 배를 하나 가져다 놓을게요. As a matter of fact, the order above is what you will usually need for game characters and other items : Scale it first if needed; then set its direction, then translate it. For instance, given a ship model (rotations have been removed for simplification) : -* The wrong way : - - You translate the ship by (10,0,0). Its center is now at 10 units of the origin. - - You scale your ship by 2\. Every coordinate is multiplied by 2 _relative to the origin_, which is far away... So you end up with a big ship, but centered at 2*10 = 20. Which you don't want. -* The right way : - - You scale your ship by 2\. You get a big ship, centered on the origin. - - You translate your ship. It's still the same size, and at the right distance. +* 틀린 방법 : + - 배를 (10, 0, 0) 으로 이동했어요. 이제 원점으로 부터 10 unit 떨어져 있네요. + - 그리고 배를 2배로 키웠어요. 모든 좌표는 원점으로부터 상대적으로 2배 더 커졌고요. 어유. 멀리도 갔네. 확대가 끝나면 큰 배는 가지겠지만. 이제 원점으로 부터 2*10 = 20이나 멀어졌네요. 원하지 않는 선물을 받아버렸어요. + +* 올바른 방법 : + - 배를 2배 키워요. 그러면 큰 배를 얻었고, 원점에 잘 있죠. + - 그리고 배를 이동시켜요. 여전히 같은 크기를 가지고 있고, 올바른 방향을 가지고 있죠. + + +행렬 - 행렬 곱은 행렬 - 벡터 곱가 아주 유사해요, 그래서 구체적인 내용을 생략하고. 대신 참고가 [될 만한 링크를(Matricesa and Quaternions FAQ)]({{site.baseurl}}/assets/faq_quaternions/index.html#Q11)걸어드릴게요. 만약에 행렬곱이 필요하다면. 지금은 그냥 컴퓨터에게 맡깁시다. : Matrix-matrix multiplication is very similar to matrix-vector multiplication, so I'll once again skip some details and redirect you the the [Matrices and Quaternions FAQ]({{site.baseurl}}/assets/faq_quaternions/index.html#Q11) if needed. For now, we'll simply ask the computer to do it : -**in C++, with GLM :** +**GLM과 함깨, C++에선. :** ``` cpp glm::mat4 myModelMatrix = myTranslationMatrix * myRotationMatrix * myScaleMatrix; glm::vec4 myTransformedVector = myModelMatrix * myOriginalVector; ``` -**in GLSL :** +**GLSL에선. :** ``` glsl mat4 transform = mat2 * mat1; vec4 out_vec = transform * in_vec; ``` -# The Model, View and Projection matrices +# 모델, 뷰, 프로젝션 행렬 -_튜토리얼의 남은 부분들에서는, 우리가 블랜더의 가장 사랑받는 3d 모델-키 수자네-을 그릴줄 이미 알고 있었다고 하고 진행합니다_ +*튜토리얼의 남은 부분들에서는, 우리가 블랜더의 가장 사랑받는 3d 모델-키 수자네-을 그릴줄 이미 알고 있었다고 하고 진행합니다* 모델과 뷰, 프로젝션 매트릭스는 변환들을 분명하게 구별하기 위한 좋은 도구 입니다. 이들을 안 쓸수도 있습니다 (여기까지 우리가 튜토리얼 1과 2에서 했던것 처럼). 하지만 써야만 합니다. 모든 사람이 이렇게 합니다. 왜냐면 이렇게 하는게 쉬운 길이니까요. @@ -284,7 +290,6 @@ glm::mat4 projectionMatrix = glm::perspective( ); ``` -One last time : 마지막으로 : _우리는 카메라 공간 (모든 버텍스들이 카메라 좌표에 상대적) 에서 호모니지어스 공간 (모든 버텍스들이 작은 큐브 안에 정의되고, 큐브안에 있는 모든 것들은 화면에 띄어집니다)으로 갔습니다._ @@ -313,7 +318,7 @@ _우리는 카메라 공간 (모든 버텍스들이 카메라 좌표에 상대 그리고 여기 실제로 랜더 되는 이미지가 있네요 ! -## 변환들 쌓기 : 모델뷰 매트릭스 +## 변환 쌓기 : 모델뷰 매트릭스 ... 당신이 이미 사랑했었던 일반적인 매트릭스 곱과 같습니다 ! @@ -336,7 +341,7 @@ transformed_vertex = MVP * in_vertex; // 프로젝션 매트릭스 : 45도 시야각, 4:3 비율, 시야 범위 : 0.1 유닛 <--> 100 유닛 glm::mat4 Projection = glm::perspective(glm::radians(45.0f), (float) width / (float)height, 0.1f, 100.0f); - // 혹은 ortho(직교) 카메라에선 : + //혹은 ortho(직교) 카메라에선 : //glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f); // 월드 좌표로 표현 // 카메라 매트릭스 @@ -352,46 +357,50 @@ transformed_vertex = MVP * in_vertex; glm::mat4 mvp = Projection * View * Model; // 기억하세요, 행렬곱은 계산은 반대순서로 이루어집니다 ``` -* Second step : give it to GLSL +* 두번째 : GLSL에게 줘버려요. ``` cpp - // Get a handle for our "MVP" uniform - // Only during the initialisation + // 우리의 "MVP" 행렬에 참조를 얻습니다. + // 아. 초기화때만 하셔야 해요. GLuint MatrixID = glGetUniformLocation(programID, "MVP"); - // Send our transformation to the currently bound shader, in the "MVP" uniform - // This is done in the main loop since each model will have a different MVP matrix (At least for the M part) + // 현재 바인딩된 쉐이더에게 변환한 메트릭스를 보냅시다. 방금 얻은 참조로요. + // 이건 각각의 모델마다 다른 MVP 행렬을 가지고 있을 것이니, 메인 루프에 해줍시다. (VP는 같을지 몰라도, M은 다를거에요.) glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &mvp[0][0]); ``` -* Third step : use it in GLSL to transform our vertices +* 세번쨰 : 우리가 GLSL에게 넘겨준 행렬을 정점에 적용시킵시다. ``` glsl - // Input vertex data, different for all executions of this shader. + // vertex 데이터 입력 값, 쉐이더의 실행때마다 값이 다릅니다. layout(location = 0) in vec3 vertexPosition_modelspace; - // Values that stay constant for the whole mesh. + // 이 값은 한 매쉬동안은 상수적입니다. uniform mat4 MVP; void main(){ + // 정점의 출력 좌표, clip space에선 : MVP * position // Output position of the vertex, in clip space : MVP * position gl_Position = MVP * vec4(vertexPosition_modelspace,1); } ``` + {: .highlightglslvs } -* Done ! Here is the same triangle as in tutorial 2, still at the origin (0,0,0), but viewed in perspective from point (4,3,3), heads up (0,1,0), with a 45° field of view. +* 끝났어요! 튜토리얼 2와 같은 삼각형이 있을거에요. 아직도 원점 (0,0,0)에 있는 거 말이죠. 하지만 perspective로 (4,3,3) 좌표에서, 상향 벡터는 (0,1,0)으로 줬으니 45도 각도로 보일거에요. ![]({{site.baseurl}}/assets/images/tuto-3-matrix/perspective_red_triangle.png) -In tutorial 6 you'll learn how to modify these values dynamically using the keyboard and the mouse to create a game-like camera, but first, we'll learn how to give our 3D models some colour (tutorial 4) and textures (tutorial 5). +튜토리얼 6에서는 키보드와 마우스를 이용해서 어떻게 우리가 오늘 썼던 값을 다이나믹하게 바꿀 수 있는지 배울거에요 - 마치 게임 카메라 같을걸요? - 하지만. 우선은 우리의 3D 모델에 어떻게 색상을 넣는지 (tutorial 4)와 텍스쳐를 넣을지부터 배울거에요. (tutorial 5) + + +# 연습문제 -# Exercises -* Try changing the glm::perspective -* Instead of using a perspective projection, use an orthographic projection (glm::ortho) -* Modify ModelMatrix to translate, rotate, then scale the triangle -* Do the same thing, but in different orders. What do you notice ? What is the "best" order that you would want to use for a character ? +* glm::perspective를 한번 바꿔보세요. +* perspective(원근법) projection(투영법)을 쓰는 대신, orthographic projection을 써보세요. (glm::ortho) +* ModelMatrix를 이동하고, 회전하고, 확대해서 삼각형을 수정해보세요.ㅗ +* 같은 걸 해보시는데, 한번 다른 순서로 해보시겠어요? 어떤게 최고의 방법이었나요? 어떤 순서로 해야 캐릭터가 예쁘게 보일 것 같았나요? _Addendum_ From c01a7bdefd718a4634014816e37e4a0ba217fda3 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 17:29:46 +0900 Subject: [PATCH 11/18] Tutorial 8 Tranlation to korean first Complete. --- .../tutorial-8-basic-shading/index.markdown | 224 +++++++++--------- 1 file changed, 115 insertions(+), 109 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown b/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown index 1626c0816..40d03ecdc 100644 --- a/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown +++ b/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown @@ -1,4 +1,4 @@ ---- + --- layout: tutorial status: publish published: true @@ -11,36 +11,36 @@ tags: [] language: kr --- -In this 8th tutorial, we will learn how to do some basic shading. This includes : +8번째 튜토리얼에선, 이제 어떻게 기초적인 쉐이딩을 하는지 배울거에요. 아래 내용을 포함해요: -* Beeing more bright when closer to a light source -* Having highlights when looking in the reflection of a light (specular lighting) -* Beeing darker when light is not directly towards the model (diffuse lighting) -* Cheating a lot (ambient lighting) +* 광원에 가까워질 때 더 밝아지는 법. +* 빛의 반사로 하이라이트를 만드는 법. (Specular Lighting - 반사 조명) +* 빛이 모델을 직접 향하지 않아도 빛이 보이는 법. (Diffuse Lighting - 기본 조명) +* 개쩌는 속임수. (ambient Lighting) -This does NOT include : +아래는 포함하지 않아요 : -* Shadows. This is a broad topic that deserves its own tutorial(s) -* Mirror-like reflections (this includes water) -* Any sophisticated light-matter interaction like subsurface scattering (like wax) -* Anisotrophic materials (like brushed metal) -* Physically based shading, which tries to mimic the reality closely -* Ambient Occlusion (it's darker in a cave) -* Color Bleeding (a red carpet will make a white ceiling a litte bit red) -* Transparency -* Any kind of Global Illumination whatsoever (it's the name that regroups all previous ones) +* 그림자. 이건 따로 튜토리얼을 만들어야 할 정도로 광대한 주제에요. +* 거울 같은 반사 (물 포함해서.) +* 표면이 산란하는 것 같은 정됴한 빛 - 물질 상호 작용 (왁스같이 번쩍 번쩍.) +* 이방성 표면 (잘 닦은 금속과 같은. ) +* 물리 기반 셰이딩, 현실과 비슷한. +* 주변 폐색하기 (역주 : 빛이 안 들어오는 곳에 안 들어오게 하는 거.)(동굴에서 더 어두워 짐.) +* 색상 번지게 하기. (레드 카펫이 하얀 천장을 붉게 만듭니다. ) +* 투명도 +* 모든 종류의 글로벌 일루미네이션(GI) (더럽게 어려워!) -In a word : Basic. +한 마디로 : 기초죠. -# Normals +# 법선 벡터(normals) -During the last few tutorials you've been dealing with normal without really knowing what they were. +지금까지 튜로리얼에서 normal을 소개를 못 드렸어요. 드디어 오늘 소개 드릴게요! -## Triangle normals +## 삼각형 법선 -The normal of a plane is a vector of length 1 that is perpendicular to this plane. +평면의 법선이란, 길이가 1이고 평면에 수직인 벡터를 말해요. -The normal of a triangle is a vector of length 1 that is perpendicular to this triangle. It is easily computed by taking the cross product of two of its edges (the cross product of a and b produces a vector that is perpendicular to both a and b, remember ?), and normalized : its length is brought back to 1. In pseudo-code : +삼각형의 법선은 그럼 길이가 1이고, 삼각형에 수직인 백터를 말하겠죠? 이건 가장자리에 있는 두 벡터를 외적(외적은 a,b 두 백터의 수직 백터를 구하는 작업이었죠. 기억하시죠? )함으로써 쉽게 구할 수 있고, 일반화 시킬 수도 있죠 : 일반화는, 길이를 1로 만드는 작업을 말해요. 의사코드를 보죠!: ``` triangle ( v1, v2, v3 ) @@ -49,21 +49,22 @@ edge2 = v3-v1 triangle.normal = cross(edge1, edge2).normalize() ``` -Don't mix up normal and normalize(). Normalize() divides a vector (any vector, not necessarily a normal) by its length so that its new length is 1. normal is just the name for some vectors that happen to represent, well, a normal. +normal과 normalize()를 햇갈리지 마세요. Normailize()는 새 길이가 1이 되도록 벡터를 길이로 나누는 작업이에요. normal은 그 백터를 표현할 수 있는 이름이에요. 음. normal말이에요. -## Vertex normals +## 정점의 법선들 -By extension, we call the normal of a vertex the combination of the normals of the surroundings triangles. This is handy because in vertex shaders, we deal with vertices, not triangles, so it's better to have information on the vertex. And any way, we can't have information on triangles in OpenGL. In pseudo-code : -``` +확장하면, 정점의 법선은 주위 삼각형의 법선의 조합이라고 할 수 있어요. Vertex Shader는 앞서 말한 삼각형은 다루지 않고, 정점들을 다루니까 그렇게 말하는 게 더 편할거에요. 어차피 OpenGL에서는 삼각형들에 대한 정보를 얻을 길이 없으니까요. 의사 코드에요 : + +``` vertex v1, v2, v3, .... triangle tr1, tr2, tr3 // all share vertex v1 v1.normal = normalize( tr1.normal + tr2.normal + tr3.normal ) ``` -## Using vertex normals in OpenGL +## OpenGL에서 정점의 법선을 사용하기. -To use normals in OpenGL, it's very easy. A normal is an attribute of a vertex, just like its position, its color, its UV coordinates... so just do the usual stuff. Our loadOBJ function from Tutorial 7 already reads them from the OBJ file. +OpenGL에서 법선을 쓰는 건, 아주 쉬워요! 법선은 정점의 속성이에요. 마치 위치나, 색상이나, UV 좌표 같은 것들 말이에요. 그러니까 해온 대로 합시다. 이미 우리의 loadOBJ 파일에서는 OBJ 파일을 읽으면서 모든 준비를 맞춘 것 같네요. ``` cpp GLuint normalbuffer; @@ -75,7 +76,7 @@ GLuint normalbuffer; and ``` cpp - // 3rd attribute buffer : normals + // 3번째 버퍼의 속성 : 법선들 glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glVertexAttribPointer( @@ -88,64 +89,65 @@ and ); ``` -and this is enough to get us started. +자. 이제 시작하기는 충분해 보여요. -# The Diffuse part +# Diffuse 부분 -## The importance of the surface normal +## 표면 법선의 중요성 -When light hits an object, an important fraction of it is reflected in all directions. This is the "diffuse component". (We'll see what happens with the other fraction soon) +빛이 물체에 닿을 때, 중요한 점은 모든 방향으로 반사된다는 점이에요. 그걸 "확산 요소"라고 불러요. (곧 있으면 도대체 무슨 일이 벌어지는 지 알 수 있을거에요.) ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuseWhite1.png) -When a certain flux of light arrives at the surface, this surface is illuminated differently according to the angle at which the light arrives. +어떤 빛이 표면에 도달하면, 표면은 빛이 도달한 각도에 따라 다르게 조명이 되요. -If the light is perpendicular to the surface, it is concentrated on a small surface. If it arrives at a gazing angle, the same quantity of light spreads on a greater surface : +만약 빛이 표면으로부터 수직으로 들어오면, 작은 표명에 집중되겠죠. 지금 보고 있는(시야) 각도에 도달하면, 같은 빛이 더 넓은 표면에 확산될 거에요 : ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuseAngle.png) -This means that each point of the surface will look darker with gazing light (but remember, more points will be illuminated, so the total quantity of light will remain the same) +그러니까. 표면의 각 점은 수직보다는 응시각도일 때 더 어두워보이겠죠? 왜냐하면 상대적으로 더 적은 빛이 들어오니까요. (하지만 기억하세요, 응시각도일때 더 많은 빛이 조명될거니. 결국 최종적으로는 같을거에요. ) -This means that when we compute the colour of a pixel, the angle between the incoming light and the surface normal matters. We thus have : +각 점이라는 말은 . 우리는 각각의 픽셀의 색상을 계산할 때 들어오는 빛과 표면 간의 각도가 중요하다는 것이죠. 이렇게 해볼 수 있을 것 같네요 : ``` glsl -// Cosine of the angle between the normal and the light direction, -// clamped above 0 -// - light is at the vertical of the triangle -> 1 -// - light is perpendicular to the triangle -> 0 +// 빛과 법선사이의 각도에 대해 cosine을 구할게요. (실제로 cos함수를 쓰지는 않아요. 왜냐하면 연산량을 먹을 뿐더러, 두 벡터가 단위 벡터(길이가 1이라면)라는 가정하에서 내적은 두 벡터의 각도의 cos 과 같게 나오니까요. - 역주 ) +// 법선 백터와 빛 백터 사이의 코사인 각도. +// 0으로 clamp 됨. +// - 빛이 삼각형에 대해서 수직임. -> 1 +// - 빛이 삼각형에 대해서 수평임. -> 0 float cosTheta = dot( n,l ); color = LightColor * cosTheta; ``` {: .highlightglslfs } -In this code, n is the surface normal and l is the unit vector that goes from the surface to the light (and not the contrary, even if it's non inuitive. It makes the math easier). +이 코드에서는, n은 표면의 법선이고, l은 표면으로 가는 빛의 단위 벡터에요. (표현이 글러먹은 것 같지만. 이게 좀... 직관적일거에요. 수학을 쉽게 만드려고 했어요.) -## Beware of the sign +## 사인을 조심하세요. -Something is missing in the formula of our cosTheta. If the light is behind the triangle, n and l will be opposed, so n.l will be negative. This would mean that colour = someNegativeNumber, which doesn't mean much. So we have to clamp cosTheta to 0 : + +우리의 cosTheta 공시에 뭐가 빠진 것 같아요. 빛이 삼각형 뒤에 있으면, n과 l이 뒤에 있다는 말이니까... n과 l의 내적은 음수가 되겠네요. 그러면 color = 음수숫자 가 되버리는데. 그건 그리 좋지 않을 것 같아요. 그러니까 cosTheat를 0으로 clamp 합시다. : ``` glsl -// Cosine of the angle between the normal and the light direction, -// clamped above 0 -// - light is at the vertical of the triangle -> 1 -// - light is perpendicular to the triangle -> 0 -// - light is behind the triangle -> 0 +// 법선 백터와 빛 백터 사이의 코사인 각도. +// 0으로 clamp 됨. +// - 빛과 법선이 수직이면 -> 1 +// - 빛과 법선이 수평이면 -> 0 +// - 빛이 삼각형 뒤에 있으면 ->0 float cosTheta = clamp( dot( n,l ), 0,1 ); color = LightColor * cosTheta; ``` {: .highlightglslfs } -## Material Color - -Of course, the output colour also depends on the colour of the material. In this image, the white light is made out of green, red and blue light. When colliding with the red material, green and blue light is absorbed, and only the red remains. +## Material 색깔 +물론, 출력 색상도 재질의 색상에 따라 다르겠죠. 이 이미지에서 흰색 빛은 녹색, 빨간색과 파란색 빛으로 만들어져요. 적색으로 뒤덮인 물체랑 충돌하면, 녹색과 청색 빛은 흡수디고 적색만 남겠죠? ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuseRed.png) - +우리는 이걸 간단한 곱샘으로 표현할 수 있어요 : We can model this by a simple multiplication : ``` glsl @@ -153,93 +155,96 @@ color = MaterialDiffuseColor * LightColor * cosTheta; ``` {: .highlightglslfs } -## Modeling the light +## 빛을 모델링하기. -We will first assume that we have a punctual light that emits in all directions in space, like a candle. +자. 먼저 촛불이랑 비슷하게, 모든 방향으로 방출하는 빛을 가지고 있다고 가정해봐요. -With such a light, the luminous flux that our surface will receive will depend on its distance to the light source: the further away, the less light. In fact, the amount of light will diminish with the square of the distance : +그런 빛은 물질의 표면에 닿을 빛의 양이 광원까지의 거리에 따라 달라지겠죠? 멀리 떨어져 있을 수록 빚이 적죠. 실제로도, 빛의 양은 거리의 제곱으로 서서히 줄어들어요 : ``` glsl color = MaterialDiffuseColor * LightColor * cosTheta / (distance*distance); ``` {: .highlightglslfs } -Lastly, we need another parameter to control the power of the light. This could be encoded into LightColor (and we will in a later tutorial), but for now let's just have a color (e.g. white) and a power (e.g. 60 Watts). +마지막으로, 우리는 빛의 세기를 조정할 매개변수가 필요하겠네요. 그걸 LightColor라고 정해요. (나중에 설명할 예정이에요. ) 그럼 이제, 우리는 빛의 색깔이랑 (예 : 흰색), 세기로 나타낼 수 있겠네요. (예 : 60 와트) ``` glsl color = MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance); ``` {: .highlightglslfs } -## Putting it all together +## 다 넣어보기 -For this code to work, we need a handful of parameters (the various colours and powers) and some more code. +자. 위에서 설명한 코드가 돌아가려면 매개 변수 몇개랑(아까 말한 색상과 힘이요.) 더 많은 코드가 필요해요. -MaterialDiffuseColor is simply fetched from the texture. +MaterialDiffuseColor는 간단하게 텍스쳐에서 가져옵시다. -LightColor and LightPower are set in the shader through GLSL uniforms. +LightColor와 LightPower는 GLSL을 통해 셰이더로 보내면 될 것 같고요. -cosTheta depends on n and l. We can express them in any space provided it's the same for both. We choose the camera space because it's easy to compute the light's position in this space : +cosTheta는 n과 l에 의존하고 있죠. l은 어디서나 끌어오면 되는데. 카메라 공간에서 끌고 오는 게 제일 쉬워보이니까. 카메라 공간에서 끌어옵시다! : ``` glsl -// Normal of the computed fragment, in camera space + // fragment shader에서 계산된 법선, 카메라 공간 기준. vec3 n = normalize( Normal_cameraspace ); - // Direction of the light (from the fragment to the light) + // 빛의 방향. vec3 l = normalize( LightDirection_cameraspace ); ``` {: .highlightglslfs } -with Normal_cameraspace and LightDirection_cameraspace computed in the Vertex shader and passed to the fragment shader : +Normal_cameraspace와 LightDirection_cameraspace는 Vertex Shader에서 계산을 끝내고, fragment shader로 보내줄거에요 : ``` glsl -// Output position of the vertex, in clip space : MVP * position +// 정점의 최종 좌표, 화면 상에선 : MVP * position gl_Position = MVP * vec4(vertexPosition_modelspace,1); -// Position of the vertex, in worldspace : M * position +// 정점의 월드 좌표, 월드 공간에선 : M * position Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz; -// Vector that goes from the vertex to the camera, in camera space. -// In camera space, the camera is at the origin (0,0,0). +// 정점에서 카메라 공간을 향하는 벡터. +// 카메라 공간에선, 카메라는 원점에 있습니다. vec3 vertexPosition_cameraspace = ( V * M * vec4(vertexPosition_modelspace,1)).xyz; EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace; -// Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity. +// 카메라 공간에서 빛으로, 그리고 정점으로 가는 벡터. M은 단위 행렬 (숫자로 치면 1 - 역주) 이기에 생략했습니다. vec3 LightPosition_cameraspace = ( V * vec4(LightPosition_worldspace,1)).xyz; LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace; -// Normal of the the vertex, in camera space +// 카메라 공간에서 정점의 법선 벡터. Normal_cameraspace = ( V * M * vec4(vertexNormal_modelspace,0)).xyz; // Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not. ``` {: .highlightglslvs } -This code can seem impressive but it's nothing we didn't learn in Tutorial 3 : Matrices. I paid attention to write the name of the space in each vector's name, so that keeping track of what is happening is much easier. **You should do that, too.** +이 코드는 Tutorial 3 : 메트릭스랑 비슷하게 보이지만. 거기서 배웠던 거랑 달라요. 아. 유의할 점이 하나 있어요. 지금 코드에는 각 벡터의 이름에 어느 공간인지 표시해주고 있죠? (예를 들면, Normal_cameraspace처럼요. - 역주) 이렇게 하면 이 벡터를 잘못 쓸 일이 없을거에요! 잘못 쓸 일이 뭐냐고요? 예를 들면 월드 공간에 있는 걸 카메라 공간이랑 곱하는 일을 피할 수 있어요. 피트랑 미터법이 다른 것처럼. 그런 원천적인 실수를 봉인할 수 있죠. + +이렇게 길게 설명한 이유는 다들 아실거에요. **당연히 이걸 보는 여러분도 이렇게 하셔야 해요!** -M and V are the Model and View matrices, which are passed to the shader in the exact same way as MVP. +는 모델과 뷰 행렬을 말하는데요, MVP와 같은 방식으로 넘겨주시면 되요. -## Time for work +## 일할 시간! -You've got everything you need to code a diffuse lighting. Go ahead, and learn the hard way :) +자. 이제 Diffuse Lighting에 필요한 모든 코드가 준비 되었어요. 먼저 해보시고, 열심히 배워봐요! :) -## Result +## 결과 -With only the Diffuse component, we have the following result (sorry for the lame texture again) : +Diffuse만 사용하면, 이런 결과가 나올 거에요. (구린 텍스쳐에 다시 한 번 사과드려요) : ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuse_only.png) -It's better than before, but there is still much missing. In particular, the back of Suzanne is completely black since we used clamp(). +저번보다는 괜찮지만, 뭔가 많이 빠진 것 같네요. 특히. Clamp를 써서 그런가 모델링의 뒤는 완전히 검은색이에요. -# The Ambient component +# 주변광 -The Ambient component is the biggest cheat ever. +주변광은 엄청나게 멋져요. -We expect the back of Suzanne to be receive more light because in real life, the lamp would light the wall behind it, which would in turn (slightly less) light the back of the object. +봐요. 실제 상황에서는 렘프의 빛이 벽에 부딪치고, 그게 반사되고, 반사되고, 반사되서 결국 모델링의 뒤에 빛이 조금이라도 갈거에요. 그런데 지금은 어떻죠? 그냥 검정색이죠. -This is awfully expensive to compute. +그런데. 그걸 진짜로 다 계산하기에는 너무 시간이 많이 걸려요. -So the usual hack is to simply fake some light. In fact, is simply makes the 3D model *emit *light so that it doesn't appear completely black. +그래서, 일반적인 편법은 간단히 약간의 가짜 빛을 추가하는거죠. 그러니까. 3D 모델이 약간의 빛을 자체적으로 방출해서 완벽하게 검은 색으로 보이지 않게 하는거에요. -This can be done this way : + +그럼. 이렇게 짤 수 있겠죠? : ``` glsl vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor; @@ -248,65 +253,66 @@ vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor; ``` glsl color = - // Ambient : simulates indirect lighting + // 주변 광 : 간접광을 시뮬레이션 합니다. MaterialAmbientColor + - // Diffuse : "color" of the object + // 확산 광 : 오브젝트의 "색깔"입니다. MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) ; ``` {: .highlightglslfs } -Let's see what it gives +무슨 결과가 나오는 지 한번 봅시다! -## Results +## 결과 -Ok so that's a little bit better. You can adjust the (0.1, 0.1, 0.1) if you want better results. +오. 좀 더 괜찮아 보이네요. 만약에 더 괜찮은 결과를 보고 싶으시면, (0.1, 0.1, 0.1)로 조정해도 될 것 같아요. ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuse_ambiant.png) -# The Specular component +# 반사광 -The other part of light that is reflected is reflected mostly in the direction that is the reflection of the light on the surface. This is the specular component. +반사되는 빛의 대부분은, 표면에서 빛이 들어오는 방향으로 반사되요. 그게 반사광이죠. ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/specular.png) -As you can see in the image, it forms a kind of lobe. In extreme cases, the diffuse component can be null, the lobe can be very very very narrow (all the light is reflected in a single direction) and you get a mirror. +이미지에서 볼 수 있듯. 일종의 로브(망토처럼 퍼져 보이죠? - 역주)를 형성해요. 하지만 극단적일 경우엔 확산 요소가 없을 지도 모르니. 로브는 아주, 아주, 아주 작을 지도 모르죠. (모든 빛은 단일 방향으로 반사되니까요.) 그러면 - 거울이 되는 거에요. 모든 빛을 정반사하니까요. -(*we can indeed tweak the parameters to get a mirror, but in our case, the only thing we take into account in this mirror is the lamp. So this would make for a weird mirror)* +(*거울을 만들려고 매개 변수를 조정할 수는 있겠는데. 지금 경우에는 반사할 수 있는게 광원 밖에 없어요. 그건 거울이 아니라, 가짜 거울이죠.)* ``` glsl -// Eye vector (towards the camera) +// 눈 벡터 - 카메라 쪽. vec3 E = normalize(EyeDirection_cameraspace); -// Direction in which the triangle reflects the light +// 삼각형이 빛을 반사하는 방향. vec3 R = reflect(-l,n); -// Cosine of the angle between the Eye vector and the Reflect vector, -// clamped to 0 -// - Looking into the reflection -> 1 -// - Looking elsewhere -> < 1 +// 눈 벡터와 반사 벡터 사이의 코사인 각도, +// 0으로 clamp 됨. +// - 반사광을 들어다보고 있으면 -> 1 +// - 다른 곳을 보고 있으면 -> < 1 float cosAlpha = clamp( dot( E,R ), 0,1 ); color = - // Ambient : simulates indirect lighting + // 주변광 : 간접 조명 시뮬레이션 MaterialAmbientColor + - // Diffuse : "color" of the object - MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) ; - // Specular : reflective highlight, like a mirror + // 확산광 : 오브젝트의 "색깔" + MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) + + // 반사광 : 반사 하이라이트, 거울 같이. MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance*distance); ``` {: .highlightglslfs } -R is the direction in which the light reflects. E is the inverse direction of the eye (just like we did for "l"); If the angle between these two is little, it means we are looking straight into the reflection. +R은 빛의 반사 방향이에요. E는 눈(우리가"l"이라고 말한거 말이에요.)의 반대 방향이고요; 만약에 이 둘 사이에 각도가 작으면, 반사광을 똑바로 보고 있음을 말하는 거겠죠? -pow(cosAlpha,5) is used to control the width of the specular lobe. Increase 5 to get a thinner lobe. +pow(cosAlpha,5)는 반사광의 로브의 넓이를 조정하는 데 쓸 수 있어요. 5보다 더 증가하면 - 얇은 로브를 얻을 수 있죠. (아마, 지금 반사하는 곳을 '로브'같다고 해서 로브라고 하는 것 같아요 - 역주 ) -## Final result +## 최종 결과 ![]({{site.baseurl}}/assets/images/tuto-8-basic-shading/diffuse_ambiant_specular.png) +코와 눈쪽에 반사 하이라이트가 있는 거, 보이시죠? + +이 쉐이딩 방법은 간단해서 수년 동안 사용되어 왔는데요. 하지만 여러가지 문제가 있어서, microfaect BRDF같은 물리 -기반-렌더링으로 대체되고 있어요. 그건 나중에 알아봐요. -Notice the specular highlights on the nose and on the eyebrows. -This shading model has been used for years due to its simplicity. It has a number of problems, so it is replaced by physically-based models like the microfacet BRDF, but we will see this later. +다음 튜토리얼에선, VBO라는 것을 이용해서 성능을 어떻게 높일지 알아볼거에요! 그게 첫 중급 튜토리얼이 될거에요! 그러니까. 초급 튜토리얼은 끝났어요! 여기까지 따라와주신 여러분들. 축하드려요! 그럼 힘내서 중급 튜토리얼까지 가봅시다! -In the next tutorial, we'll learn how to improve the performance of your VBO. This will be the first Intermediate tutorial ! From 000e5d2358a3582d2cadcdb0b3a4d6d250640380 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 19:42:22 +0900 Subject: [PATCH 12/18] Tutorial 5 Title rename --- .../tutorial-5-a-textured-cube/index.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown b/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown index 5aa70fc02..5640d9bbd 100644 --- a/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown +++ b/kr/beginners-tutorials/tutorial-5-a-textured-cube/index.markdown @@ -2,7 +2,7 @@ layout: tutorial status: publish published: true -title: 'Tutorial 5 : A Textured Cube' +title: 'Tutorial 5 : 텍스쳐가 입혀진 큐브' date: '2011-04-26 07:55:58 +0200' date_gmt: '2011-04-26 07:55:58 +0200' categories: [tuto] From 9c48646675d21db7fff4dce95ac574dbbe384a0d Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 19:43:10 +0900 Subject: [PATCH 13/18] Tutorial 6 Rename English to Korean --- .../tutorial-6-keyboard-and-mouse/index.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown index 902122166..f15a4cf5f 100644 --- a/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown +++ b/kr/beginners-tutorials/tutorial-6-keyboard-and-mouse/index.markdown @@ -2,7 +2,7 @@ layout: tutorial status: publish published: true -title: 'Tutorial 6 : Keyboard and Mouse' +title: 'Tutorial 6 : 키보드와 ' date: '2011-05-08 08:26:13 +0200' date_gmt: '2011-05-08 08:26:13 +0200' categories: [tuto] From 49503425bccf0e69f4c98cd6ca842b3c9676ffb1 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 19:43:51 +0900 Subject: [PATCH 14/18] Tutorial 7 Renamed English To Korean --- kr/beginners-tutorials/tutorial-7-model-loading/index.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown b/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown index 3fbf4fad9..7408e6cd4 100644 --- a/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown +++ b/kr/beginners-tutorials/tutorial-7-model-loading/index.markdown @@ -2,7 +2,7 @@ layout: tutorial status: publish published: true -title: 'Tutorial 7 : Model loading' +title: 'Tutorial 7 : 모델 불러오기' date: '2011-05-08 17:48:12 +0200' date_gmt: '2011-05-08 17:48:12 +0200' categories: [tuto] From af510f27b258d5ab3cbb2be6a1419b503ea41fe6 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Mon, 5 Nov 2018 19:44:31 +0900 Subject: [PATCH 15/18] Tutorial 8 Renamed --- .../tutorial-8-basic-shading/index.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown b/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown index 40d03ecdc..34c2230d1 100644 --- a/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown +++ b/kr/beginners-tutorials/tutorial-8-basic-shading/index.markdown @@ -1,8 +1,8 @@ - --- +--- layout: tutorial status: publish published: true -title: 'Tutorial 8 : Basic shading' +title: 'Tutorial 8 : 기초 ' date: '2011-05-08 19:12:46 +0200' date_gmt: '2011-05-08 19:12:46 +0200' categories: [tuto] From e32c5ff4fd85190e31ed5c74df0df2346102854e Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Wed, 7 Nov 2018 12:11:43 +0900 Subject: [PATCH 16/18] Tutorial 9 translation to korean. --- .../tutorial-9-vbo-indexing/index.markdown | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/kr/intermediate-tutorials/tutorial-9-vbo-indexing/index.markdown b/kr/intermediate-tutorials/tutorial-9-vbo-indexing/index.markdown index c2fc2f39f..3d3395f8b 100644 --- a/kr/intermediate-tutorials/tutorial-9-vbo-indexing/index.markdown +++ b/kr/intermediate-tutorials/tutorial-9-vbo-indexing/index.markdown @@ -11,37 +11,35 @@ tags: [] language: kr --- -# The principle of indexing +# 색인 생성 원칙 -Until now, when building your VBO, we always duplicated our vertices whenever two triangles shared an edge. +지금까지, 우리가 VBO를 만들때마다 삼각형이 같은 정점을 공유함에도 불구하고 정점을 복사했어요. -In this tutorial, we introduce indexing, which enables to reuse the same vertex over and over again. This is done with an *index buffer*. +이번 튜토리얼에선 인덱싱을 소개할게요. 동일한 정점을 반복해서 사용하는 기법인데. *인덱스 버퍼*라고 불러요. ![]({{site.baseurl}}/assets/images/tuto-9-vbo-indexing/indexing1.png) -The index buffer contains integers, three for each triangle in the mesh, which reference the various *attribute buffers* (position, colour, UV coordinates, other UV coordinates, normal, ...). It's a little bit like in the OBJ file format, with one huge difference : there is only ONE index buffer. This means that for a vertex to be shared between two triangles, all attributes must be the same. +인덱스 버퍼는 다양한 메시의 *속성 버퍼*(Position, Normal, UV 좌표...)를 참조하는 세 개의 정수를 저장하는 버퍼에요. OBJ 파일 형식이랑 비슷한데, 기억나요? 한가지 차이점이 있다면 인덱스 버퍼는 오직 하나고. 삼각형끼리 꼭지점을 공유하려면 모든 속성이 같아야 한다는 거에요. -# Shared vs Separate +# 공유 정점 VS 분할 정점 -Let's take the example of the normals. In this figure, the artist who created these two triangle probably wanted them to represent a smooth surface. We can thus blend the normals of the two triangle into a single vertex normal. For visualization purposes, I added a red line which represents the aspect of the smooth surface. +자. 법선 예제를 들고와볼게요. 이 그림에서 두 삼각형을 만든 예술가는 매끄러운 표면을 만들고 싶어 하는 것 같아요. 그렇게 해주려면 두 삼각형의 법선을 단일 꼭지점 법선으로 합칠 수 있겠죠? 시각화를 위해 매끄러운 표면의 모습을 나타내는 빨간선을 그릴게요. ![]({{site.baseurl}}/assets/images/tuto-9-vbo-indexing/goodsmooth.png) - -In this second figure however, the artist visibly wanted a "seam", a rough edge. But if we merge the normals, this means that the shader will smoothly interpolate as usual and create a smooth aspect just like before : +두번째 그림에서는 어, 아티스트가 뾰족하게 보이길 원했나봐요. 하지만 만약 우리가 법선을 합친다면 - 셰이더는 평소같이 이걸 보간할 거고, 방금전이랑 비슷한 부드러운 면을 만들겠네요. ![]({{site.baseurl}}/assets/images/tuto-9-vbo-indexing/badmooth.png) - -So in this case it's actually better to have two different normals, one for each vertex. The only way to do this in OpenGL is to duplicate the whole vertex, with its whole set of attributes. +그래서 이 경우에는 두 개의 다른 법선(각 꼭지점마다 하나씩이요.)을 갖는 게 더 좋아보이네요. OpenGL에서 이 작업을 하려면 전체 속성 세트과 전체 정점을 복제해야해요. 그게 유일한 방법이에요. ![]({{site.baseurl}}/assets/images/tuto-9-vbo-indexing/spiky.png) -# Indexed VBO in OpenGL +# OpenGL에서 VBO(Vertex Buffer Object) 인덱싱하기 -Using indexing is very simple. First, you need to create an additional buffer, which you fill with the right indices. The code is the same as before, but now it's an ELEMENT_ARRAY_BUFFER, not an ARRAY_BUFFER. +인덱싱을 사용하는건 아주 간단해요. 우선, 인덱스를 담을만한 추가적인 버퍼를 만드셔야 해요. 이 코드는 옛날이랑 같지만. ARRAY_BUFFER가 ELEMENT_ARRAY_BUFFER로 바뀌었어요. ``` cpp std::vector indices; @@ -55,7 +53,7 @@ std::vector indices; glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); ``` -and to draw the mesh, simply replace glDrawArrays by this : +glDrawArrays 대신, 이걸 써서 그림을 그릴게요 : ``` cpp // Index buffer @@ -69,30 +67,30 @@ and to draw the mesh, simply replace glDrawArrays by this : (void*)0 // element array buffer offset ); ``` +(참고 : "unsighed int"를 쓰는 것보단 "unsigned short"를 쓰는 것이 더 좋을거에요. 왜냐하면 그게 더 메모리를 적게먹고, 더 빠르거든요.) -(quick note : it's better to use "unsigned short" than "unsigned int", because it takes less memory, which also makes it faster) +# 인덱스 버퍼 체우기 -# Filling the index buffer +자. 이제 진짜 문제가 생겼어요. 아까 말했듯 OpenGL은 하나의 인덱스 버퍼만 가질 수 있는데. OBJ(와, 인기있는 다른 3D 형식들은) *속성마다* 하나의 인덱스 버퍼를 쓰고 있어요. 그러니까 우리는 이 N개의 인덱스 버퍼를, 하나의 인덱스 버퍼로 만들어야해요. -Now we actually have a problem. As I said before, OpenGL can only use one index buffer, whereas OBJ (and some other popular 3D formats like Collada) use one index buffer *by attribute*. Which means that we somehow have to convert from N index buffers to 1 index buffer. - -The algorithm to do this is as follows : +알고리즘은 다음과 같아요. : ``` -For each input vertex - Try to find a similar ( = same for all attributes ) vertex between all those we already output - If found : - A similar vertex is already in the VBO, use it instead ! - If not found : - No similar vertex found, add it to the VBO +For each 입력 정점마다. + 이미 나온 결과에서 비슷한 정점(모든 속성이 같은걸로!)을 찾아봐요. + 찾았다! : + 비슷한 정점이 이미 VBO에 있네요. 대신 사용합시다! + 못찾았다.. : + 비슷한 정점은 없으니, VBO에 추가합시다. ``` -The actual C++ code can be found in common/vboindexer.cpp. It's heavily commented so if you understand the algorithm above, it should be all right. +이 구현 내용이 담긴 C++ 코드는 common/vboindexer.cpp에서 볼 수 있어요. 알고리즘을 이해시키기 위해 많은 주석이 달려져 있어요. + +유사성의 기준은, 정점의 위치, UV, 그리고 법선이 동일해야한다는 말이에요. 더 많은 속성을 추가하시면 똑같이 검사하셔야 하고요. -The criterion for similarity is that vertices' position, UVs and normals should be ** equal. You'll have to adapt this if you add more attributes. +유사한 정점 검사는 느린 선형 검사로 실행되요. std::map을 쓰는 것이 현업에서 쓰는데 더 적합할거에요. -Searching a similar vertex is done with a lame linear search for simplicity. A std::map would be more appropriate for real use. +# 추가 : FPS 측정기 -# Extra : the FPS counter +이건 인덱싱에 관한건 아닌데,[The FPS counter](http://www.opengl-tutorial.org/miscellaneous/an-fps-counter/)를 볼 좋은 시기인 것 같아요. 왜냐하면 우리가 오늘 한 인덱싱으로 속도가 얼마나 향상되엇나 볼 수 있거든요. 다른 성능 도구들은 [Tools - Debuggers](http://www.opengl-tutorial.org/miscellaneous/useful-tools-links/#debugging-tools)에서 볼 수 있어요. -It's not directly related to indexing, but it's a good moment to have a look at [the FPS counter](http://www.opengl-tutorial.org/miscellaneous/an-fps-counter/) because we can eventually see the speed improvement of indexing. Other performance tools are available in [Tools - Debuggers](http://www.opengl-tutorial.org/miscellaneous/useful-tools-links/#debugging-tools). From 8a8b024d063132c61cae7e3a34a4f0d593e309f8 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Thu, 8 Nov 2018 11:35:32 +0900 Subject: [PATCH 17/18] Tutorial 10 translate to korean. --- .../tutorial-10-transparency/index.markdown | 75 ++++++++++++------- 1 file changed, 49 insertions(+), 26 deletions(-) diff --git a/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown b/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown index 59eb89981..93c2b5859 100644 --- a/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown +++ b/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown @@ -2,7 +2,7 @@ layout: tutorial status: publish published: true -title: 'Tutorial 10 : Transparency' +title: 'Tutorial 10 : 투명도' date: '2011-05-13 23:00:42 +0200' date_gmt: '2011-05-13 23:00:42 +0200' categories: [tuto] @@ -11,16 +11,19 @@ tags: [] language: kr --- -# The alpha channel +# 알파 채널 +알파 채널에 대한 개념은 꽤 간단해요. RGB 대신에, RGBA를 써보세요. The concept of the alpha channel is pretty simple. Instead of a writing an RGB result, you write an RGBA : ``` glsl +// Output Data ; 이제는 vec4에요. // Ouput data : it's now a vec4 out vec4 color; ``` {: .highlightglslfs } +처음 .xyz는 옛날이랑 똑같고, 대신 뒤에 .a가 더 붙었어요. the first 3 components are still accessed with the .xyz swizzle operator, while the last one is accessed with .a : ``` glsl @@ -28,74 +31,94 @@ color.a = 0.3; ``` {: .highlightglslfs } +비직관적이지만. 알파 = 불투명도에요. 그래서 알파가 = 1이면 완전히 불투명하고요. 알파가 = 0 이면 완전히 투명하다는 말이에요. Unintuitively, alpha = opaqueness, so alpha = 1 means fully opaque while alpha = 0 means fully transparent. + +여기, 우리는 간단하게 알파 채널을 0.3으로 하드코딩했어요, 하지만 당연히 우리는 uniform으로 알파 값을 읽거나, 정 안되면 RGBA 텍스쳐에서 읽기를 원하죠. ( TGA는 알파 채널을 지원하고, GLFW2는 TGA를 지원해요. 다만 GLFW3은 안돼요.) Here, we simply hardcode the alpha channel at 0.3, but you probably want to use a uniform, or read it from a RGBA texture ( TGA supports the alpha channel, and GLFW supports TGA ) +여기 결과에요. 아. 백페이스 컬링은 꺼주세요. (glDisable(GL_CULL_FACE)) 왜냐하면 우리는 이제 메쉬를 뚫어서 볼 수 있어서, 우리가 못 볼 수 없는 "뒷면"은 이제 없거든요. Here's the result. Make sure to turn backface culling off (glDisable(GL_CULL_FACE) ) because since we can look through the mesh, we could see that it has no "back" face. ![]({{site.baseurl}}/assets/images/tuto-10-transparency/transparencyok.png) -# Order matters ! +# 순서 문제! +아까 스크린샷은 괜찮아 보이는데. 우리가 그냥 운이 좋아서 그래요. 실체를 봅시다. The previous screenshot looks okay-ish, but that's just because we're lucky. -## The problem +## 문제 +자, 50프로의 알파로 두 개의 사각형을 그렸어요. 하나는 초록색이고, 하나는 빨간색이죠. 여기서 순서가 중요하다는 걸 확인하실 수 있을건데, 결과로 나오는 색깔은 두 사각형에 깊이에 따라 차이가 있다는 걸 확인할 수 있을거에요. Here, I drew two squares with 50% alpha, one green and one red. You can see that order is important, the final colour gives an important clue to the eyes for proper depth perception. ![]({{site.baseurl}}/assets/images/tuto-10-transparency/transparencyorder.png) - +위 현상은 우리에게도 발생하는데, 카메라 위치를 바꿔볼까요? : This phenomena also happens in our scene. Let's change the viewpoint a bit : ![]({{site.baseurl}}/assets/images/tuto-10-transparency/transparencybad.png) - +옆에서 보면 엄청 심각한 문제라는걸 확인할 수 있어요. 사실 게임에서 투명도 많이 쓰는 거 못봤잖아요, 안 그래요? 있어도 진짜 투명이거나. 적어도 색깔은 안 입혔죠. It turns out that this is a very hard problem. You never see lots of transparency in games, do you ? -## Usual solution +## 주 해결책 +주 해결책은 모든 투명도가 적용된 삼각형을 정렬하는거에요. 네. **모든** 투명도가 적용된 삼각형을. **정렬**해야해요. The usual solution is to sort all transparent triangles. Yes, ALL transparent triangles. +* 깊이 버퍼가 우선 세계의 불투명한 부분을 먼저 그려요. 그러면 안보이는 투명 삼각형은 자연스럽게 없앨 수 있겠죠? +* 가장 멀리 떨어져있는 것부터, 가장 가까운 것까지 투명 삼각형을 정렬해요. +* 투명 삼각형을 그려요. + * Draw the opaque part of the world so that the depth buffer already can reject hidden transparent triangles * Sort transparent triangles, from the furthest to the closest * Draw the transparent triangles. +qsort(C)나 std::sort(C++)를 이용해 정렬을 할 수 있을거에요. 하지만 더 자세히는 파고 싶지는 않네요. 왜냐하면... You can sort whatever you want with qsort (in C) or std::sort (in C++). I won't dig in the details, because... -## Caveat +## 경고 +그렇게 하면 될건데, (다음 섹터에 더 자세히 설명하겠지만.) 하지만 : Doing so will work ( more on this in the next section ), but : +* 속도가 느려질거에요. 각 픽셀들은 10번, 20번, 혹은 훨씬 더 그려야할건데. 안 그래도 모자른 메모리 버스엔 너무 많은 일이에요. 게다가 일반적으론 깊이 버퍼는 먼 픽셀들을 그리지 않을 수 있는데. 여기서는 명시적으로 정렬했으니 깊이 버퍼는 쓸모가 없어요. +* 엄청 똑똑한 최적화를 쓰지 않는 이상, 픽셀당 4번(4xMSAA를 쓰고 있어요.) 이 작업을 해야해요. +* 모든 투명 삼각형을 정렬하는데도 시간이 걸리고요. +* 만약에 텍스쳐를 바꿔야만 하거나, 더 나쁜 경우로는 쉐이더를 삼각형 하나 하나마다 바꿔야 하면 성능에 심각한 문제가 있을거에요. 하지 마세요. * You will be fillrate limited. That is, each fragment will be written 10, 20 times, maybe more. This is way too much for the poor memory bus. Usually the depth buffer allows to reject enough "far" fragments, but here, you explicitly sorted them, so the depth buffer is actually useless. * You will be doing this 4 times per pixel ( we use 4xMSAA ), except if you use some clever optimisation * Sorting all the transparent triangles takes time * If you have to switch your texture, or worse, your shader, from triangle to triangle, you're going into deep performance trouble. Don't do this. +괜찮은 해결책은 주로 : A good enough solution is often to : -* Limit to a maximum the number of transparent polygons -* Use the same shader and the same texture for all of them -* If they are supposed to look very different, use your texture ! -* If you can avoid sorting, and it still doesn't look *too *bad, consider yourself lucky. - +* 투명 폴리곤의 숫자를 제한하세요. +* 이 친구들을 그릴 때엔 같은 쉐이더, 같은 텍스쳐를 쓰세요. +* 그들이 엄청 다르게 보이면, 텍스쳐를 쓰세요! 쉐이더는 건들지 말고! +* 정렬을 안해도 *적당히* 괜찮다면. 그냥 행운이라 생각하고 넘어가세요. -## Order-Independent Transparency +## 순서 - 독립 투명성 +만약에 엔진이 진짜, 진짜 최신 기술의 투명성이 필요하면. 다른 기법들을 조사해볼 가치가 있네요. A number of other techniques are worth investigating if your engine really, really needs state-of-the-art transparency : -* [The original 2001 Depth Peeling paper](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.18.9286&rep=rep1&type=pdf): pixel-perfect results, not very fast. +* [The original 2001 Depth Peeling paper](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.18.9286&rep=rep1&type=pdf): pixel-perfect results, not very fast. 픽셀 - 퍼펙트 결과, 그리 빠르진 않아요. * [Dual Depth Peeling](http://developer.download.nvidia.com/SDK/10/opengl/src/dual_depth_peeling/doc/DualDepthPeeling.pdf) : a slight improvement -* Several papers on bucket sort. Uses an array of fragments; sort them by depth in a shader. -* [ATI's Mecha Demo](http://fr.slideshare.net/hgruen/oit-and-indirect-illumination-using-dx11-linked-lists) : good and fast, but tricky to implement, needs recent hardware. Uses a linked list of fragments. -* [Cyril Crassin's variation on the ATI's technique](http://blog.icare3d.org/2010/07/opengl-40-abuffer-v20-linked-lists-of.html) : even harder implementation +* Several papers on bucket sort. Uses an array of fragments; sort them by depth in a shader. bucket 정렬에 대한 여러 논문. 픽셀의 배열을 사용해요. 셰이더에서 깊이별로 정렬하고요. +* [ATI's Mecha Demo](http://fr.slideshare.net/hgruen/oit-and-indirect-illumination-using-dx11-linked-lists) : good and fast, but tricky to implement, needs recent hardware. Uses a linked list of fragments. 훌륭하고, 빠르지만 구현하기 까다롭고. 최신 하드웨어가 필요해요. 연결된 프래그먼트 목록을 사용해요. +* [Cyril Crassin's variation on the ATI's technique](http://blog.icare3d.org/2010/07/opengl-40-abuffer-v20-linked-lists-of.html) : even harder implementation 더 어려운 구현! +강력한 콘솔에서 돌아가는 Liitle Big Planet 같은 게임도, 투명도를 1층만 사용한다는 걸 잊지마세요. Note that even a recent game like Little Big Planet, which ran on a powerful console, used only 1 layer of transparency. -# The blend function +# 블랜드 함수 +저번 코드에서 이어서 할게요. 블랜드 함수를 설정해주셔야 할 거에요. In order for the previous code to work, you need to setup your blend function. ``` cpp @@ -104,17 +127,17 @@ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ``` -Which means : -``` +뭔 말이냐면 : -New color in framebuffer = - current alpha in framebuffer * current color in framebuffer + - (1 - current alpha in framebuffer) * shader's output color +``` text +프레임 버퍼의 새로운 색깔 = + 현재 프레임 버퍼의 알파 * 현재 프레임 버퍼의 색깔 + (1 - 현재 프레임 버퍼의 알파값) + * 쉐이더 결과 색깔 ``` -Example from the image above, with red on top : +예를 하나 보여드릴게요. : ``` cpp -new color = 0.5*(0,1,0) + (1-0.5)*(1,0.5,0.5); // (the red was already blended with the white background) +new color = 0.5*(0,1,0) + (1-0.5)*(1,0.5,0.5); // (빨간 색은 이미 하얀색 배경화면과 섞였어요.) new color = (1, 0.75, 0.25) = the same orange ``` From 77e8a05b9f812566cf6e235b5f3ff28a2e0be8a6 Mon Sep 17 00:00:00 2001 From: HaruGakkaP Date: Fri, 9 Nov 2018 10:30:32 +0900 Subject: [PATCH 18/18] Tutorial 10 translate to korean. --- .../tutorial-10-transparency/index.markdown | 74 +++++++------------ 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown b/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown index 93c2b5859..4b400eecf 100644 --- a/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown +++ b/kr/intermediate-tutorials/tutorial-10-transparency/index.markdown @@ -13,116 +13,92 @@ language: kr # 알파 채널 -알파 채널에 대한 개념은 꽤 간단해요. RGB 대신에, RGBA를 써보세요. -The concept of the alpha channel is pretty simple. Instead of a writing an RGB result, you write an RGBA : +알파 채널에 대한 개념은 꽤 간단해요. RGB 대신에, RGBA를 써보세요. ``` glsl // Output Data ; 이제는 vec4에요. // Ouput data : it's now a vec4 out vec4 color; ``` + {: .highlightglslfs } -처음 .xyz는 옛날이랑 똑같고, 대신 뒤에 .a가 더 붙었어요. -the first 3 components are still accessed with the .xyz swizzle operator, while the last one is accessed with .a : +처음 .xyz는 옛날이랑 똑같고, 대신 뒤에 .a가 더 붙었어요. ``` glsl color.a = 0.3; ``` -{: .highlightglslfs } -비직관적이지만. 알파 = 불투명도에요. 그래서 알파가 = 1이면 완전히 불투명하고요. 알파가 = 0 이면 완전히 투명하다는 말이에요. -Unintuitively, alpha = opaqueness, so alpha = 1 means fully opaque while alpha = 0 means fully transparent. +{: .highlightglslfs } +비직관적이지만. 알파 = 불투명도에요. 그래서 알파가 = 1이면 완전히 불투명하고요. 알파가 = 0 이면 완전히 투명하다는 말이에요. 여기, 우리는 간단하게 알파 채널을 0.3으로 하드코딩했어요, 하지만 당연히 우리는 uniform으로 알파 값을 읽거나, 정 안되면 RGBA 텍스쳐에서 읽기를 원하죠. ( TGA는 알파 채널을 지원하고, GLFW2는 TGA를 지원해요. 다만 GLFW3은 안돼요.) -Here, we simply hardcode the alpha channel at 0.3, but you probably want to use a uniform, or read it from a RGBA texture ( TGA supports the alpha channel, and GLFW supports TGA ) -여기 결과에요. 아. 백페이스 컬링은 꺼주세요. (glDisable(GL_CULL_FACE)) 왜냐하면 우리는 이제 메쉬를 뚫어서 볼 수 있어서, 우리가 못 볼 수 없는 "뒷면"은 이제 없거든요. -Here's the result. Make sure to turn backface culling off (glDisable(GL_CULL_FACE) ) because since we can look through the mesh, we could see that it has no "back" face. +여기 결과에요. 아. 백페이스 컬링은 꺼주세요. (glDisable(GL_CULL_FACE)) 왜냐하면 우리는 이제 메쉬를 뚫어서 볼 수 있어서, 우리가 못 볼 수 없는 "뒷면"은 이제 없거든요. ![]({{site.baseurl}}/assets/images/tuto-10-transparency/transparencyok.png) -# 순서 문제! +# 순서 문제 -아까 스크린샷은 괜찮아 보이는데. 우리가 그냥 운이 좋아서 그래요. 실체를 봅시다. -The previous screenshot looks okay-ish, but that's just because we're lucky. +아까 스크린샷은 괜찮아 보이는데. 우리가 그냥 운이 좋아서 그래요. 실체를 봅시다. ## 문제 -자, 50프로의 알파로 두 개의 사각형을 그렸어요. 하나는 초록색이고, 하나는 빨간색이죠. 여기서 순서가 중요하다는 걸 확인하실 수 있을건데, 결과로 나오는 색깔은 두 사각형에 깊이에 따라 차이가 있다는 걸 확인할 수 있을거에요. -Here, I drew two squares with 50% alpha, one green and one red. You can see that order is important, the final colour gives an important clue to the eyes for proper depth perception. +자, 50프로의 알파로 두 개의 사각형을 그렸어요. 하나는 초록색이고, 하나는 빨간색이죠. 여기서 순서가 중요하다는 걸 확인하실 수 있을건데, 결과로 나오는 색깔은 두 사각형에 깊이에 따라 차이가 있다는 걸 확인할 수 있을거에요. ![]({{site.baseurl}}/assets/images/tuto-10-transparency/transparencyorder.png) 위 현상은 우리에게도 발생하는데, 카메라 위치를 바꿔볼까요? : -This phenomena also happens in our scene. Let's change the viewpoint a bit : ![]({{site.baseurl}}/assets/images/tuto-10-transparency/transparencybad.png) -옆에서 보면 엄청 심각한 문제라는걸 확인할 수 있어요. 사실 게임에서 투명도 많이 쓰는 거 못봤잖아요, 안 그래요? 있어도 진짜 투명이거나. 적어도 색깔은 안 입혔죠. -It turns out that this is a very hard problem. You never see lots of transparency in games, do you ? +옆에서 보면 엄청 심각한 문제라는걸 확인할 수 있어요. 사실 게임에서 투명도 많이 쓰는 거 못봤잖아요, 안 그래요? 있어도 진짜 투명이거나. 적어도 색깔은 안 입혔죠. ## 주 해결책 -주 해결책은 모든 투명도가 적용된 삼각형을 정렬하는거에요. 네. **모든** 투명도가 적용된 삼각형을. **정렬**해야해요. -The usual solution is to sort all transparent triangles. Yes, ALL transparent triangles. +주 해결책은 모든 투명도가 적용된 삼각형을 정렬하는거에요. 네. **모든** 투명도가 적용된 삼각형을. **정렬**해야해요. * 깊이 버퍼가 우선 세계의 불투명한 부분을 먼저 그려요. 그러면 안보이는 투명 삼각형은 자연스럽게 없앨 수 있겠죠? * 가장 멀리 떨어져있는 것부터, 가장 가까운 것까지 투명 삼각형을 정렬해요. * 투명 삼각형을 그려요. -* Draw the opaque part of the world so that the depth buffer already can reject hidden transparent triangles -* Sort transparent triangles, from the furthest to the closest -* Draw the transparent triangles. - qsort(C)나 std::sort(C++)를 이용해 정렬을 할 수 있을거에요. 하지만 더 자세히는 파고 싶지는 않네요. 왜냐하면... -You can sort whatever you want with qsort (in C) or std::sort (in C++). I won't dig in the details, because... ## 경고 그렇게 하면 될건데, (다음 섹터에 더 자세히 설명하겠지만.) 하지만 : -Doing so will work ( more on this in the next section ), but : * 속도가 느려질거에요. 각 픽셀들은 10번, 20번, 혹은 훨씬 더 그려야할건데. 안 그래도 모자른 메모리 버스엔 너무 많은 일이에요. 게다가 일반적으론 깊이 버퍼는 먼 픽셀들을 그리지 않을 수 있는데. 여기서는 명시적으로 정렬했으니 깊이 버퍼는 쓸모가 없어요. * 엄청 똑똑한 최적화를 쓰지 않는 이상, 픽셀당 4번(4xMSAA를 쓰고 있어요.) 이 작업을 해야해요. * 모든 투명 삼각형을 정렬하는데도 시간이 걸리고요. -* 만약에 텍스쳐를 바꿔야만 하거나, 더 나쁜 경우로는 쉐이더를 삼각형 하나 하나마다 바꿔야 하면 성능에 심각한 문제가 있을거에요. 하지 마세요. -* You will be fillrate limited. That is, each fragment will be written 10, 20 times, maybe more. This is way too much for the poor memory bus. Usually the depth buffer allows to reject enough "far" fragments, but here, you explicitly sorted them, so the depth buffer is actually useless. -* You will be doing this 4 times per pixel ( we use 4xMSAA ), except if you use some clever optimisation -* Sorting all the transparent triangles takes time -* If you have to switch your texture, or worse, your shader, from triangle to triangle, you're going into deep performance trouble. Don't do this. +* 만약에 텍스쳐를 바꿔야만 하거나, 더 나쁜 경우로는 쉐이더를 삼각형 하나 하나마다 바꿔야 하면 성능에 심각한 문제가 있을거에요. 하지 마세요. -괜찮은 해결책은 주로 : -A good enough solution is often to : +괜찮은 해결책은 주로 : -* 투명 폴리곤의 숫자를 제한하세요. -* 이 친구들을 그릴 때엔 같은 쉐이더, 같은 텍스쳐를 쓰세요. -* 그들이 엄청 다르게 보이면, 텍스쳐를 쓰세요! 쉐이더는 건들지 말고! -* 정렬을 안해도 *적당히* 괜찮다면. 그냥 행운이라 생각하고 넘어가세요. +* 투명 폴리곤의 숫자를 제한하세요. +* 이 친구들을 그릴 때엔 같은 쉐이더, 같은 텍스쳐를 쓰세요. +* 그들이 엄청 다르게 보이면, 텍스쳐를 쓰세요! 쉐이더는 건들지 말고! +* 정렬을 안해도 *적당히* 괜찮다면. 그냥 행운이라 생각하고 넘어가세요. ## 순서 - 독립 투명성 -만약에 엔진이 진짜, 진짜 최신 기술의 투명성이 필요하면. 다른 기법들을 조사해볼 가치가 있네요. -A number of other techniques are worth investigating if your engine really, really needs state-of-the-art transparency : +만약에 엔진이 진짜, 진짜 최신 기술의 투명성이 필요하면. 다른 기법들을 조사해볼 가치가 있네요. -* [The original 2001 Depth Peeling paper](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.18.9286&rep=rep1&type=pdf): pixel-perfect results, not very fast. 픽셀 - 퍼펙트 결과, 그리 빠르진 않아요. -* [Dual Depth Peeling](http://developer.download.nvidia.com/SDK/10/opengl/src/dual_depth_peeling/doc/DualDepthPeeling.pdf) : a slight improvement -* Several papers on bucket sort. Uses an array of fragments; sort them by depth in a shader. bucket 정렬에 대한 여러 논문. 픽셀의 배열을 사용해요. 셰이더에서 깊이별로 정렬하고요. -* [ATI's Mecha Demo](http://fr.slideshare.net/hgruen/oit-and-indirect-illumination-using-dx11-linked-lists) : good and fast, but tricky to implement, needs recent hardware. Uses a linked list of fragments. 훌륭하고, 빠르지만 구현하기 까다롭고. 최신 하드웨어가 필요해요. 연결된 프래그먼트 목록을 사용해요. -* [Cyril Crassin's variation on the ATI's technique](http://blog.icare3d.org/2010/07/opengl-40-abuffer-v20-linked-lists-of.html) : even harder implementation 더 어려운 구현! +* [The original 2001 Depth Peeling paper](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.18.9286&rep=rep1&type=pdf): 픽셀 - 퍼펙트로 결과가 나와요. 그리 빠르진 않아요. +* [Dual Depth Peeling](http://developer.download.nvidia.com/SDK/10/opengl/src/dual_depth_peeling/doc/DualDepthPeeling.pdf) : bucket 정렬에 대한 여러 논문. 픽셀의 배열을 사용해요. 셰이더에서 깊이별로 정렬하고요. +* [ATI's Mecha Demo](http://fr.slideshare.net/hgruen/oit-and-indirect-illumination-using-dx11-linked-lists) : 훌륭하고, 빠르지만 구현하기 까다롭고. 최신 하드웨어가 필요해요. 연결된 프래그먼트 목록을 사용해요. +* [Cyril Crassin's variation on the ATI's technique](http://blog.icare3d.org/2010/07/opengl-40-abuffer-v20-linked-lists-of.html) : 더 어려운 구현! -강력한 콘솔에서 돌아가는 Liitle Big Planet 같은 게임도, 투명도를 1층만 사용한다는 걸 잊지마세요. -Note that even a recent game like Little Big Planet, which ran on a powerful console, used only 1 layer of transparency. +강력한 콘솔에서 돌아가는 Liitle Big Planet 같은 게임도, 투명도를 1층만 사용한다는 걸 잊지마세요. # 블랜드 함수 -저번 코드에서 이어서 할게요. 블랜드 함수를 설정해주셔야 할 거에요. -In order for the previous code to work, you need to setup your blend function. +저번 코드에서 이어서 할게요. 블랜드 함수를 설정해주셔야 할 거에요. ``` cpp -// Enable blending +// 블랜딩 활성화 glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ```