이번장에서는 오랬동안 길게 끌어온 OpenGL의 렌더링 성능을 향상 시켜주는 Display List에 대해 알아보고자 한다. 사실 이 부분을 16장에 위치시키기에는 그 내용이 기본적이긴 하지만 필자 스스로의 혼란을 줄이기 위해 그냥 순서대로 번호를 매겼다. 이 강좌는 1장에 이여 두번째로 NeHe의 Display List를 그대로 번역한다. 이제 시작해 보자.
이 강좌에서는 Display List에 대한 설명이다. 간단한 장면을 연출하고자 할때 Display List를 사용함으로써 속도의 개선 뿐만이 아니라 코드 줄수 또한 줄일 수 있다.
예를 들어서, 소행성 게임을 만들고 있다고 해 보자. 각 레벨은 최소한 2개 이상의 소행성으로 시작된다. 그래서 당신은 3차원의 소행성을 어떻게 만들 것인지 계산한다. 일단 계산이 끝난후 폴리곤이나 Quad를 이용하여 소행성을 만든다. 이 소행성이 여덜개의 면으로 이루어졌다고 해보자. 이렇게 만든 하나의 소행성을 여러개 만들기 위해, 간결하게 반복문을 이용하여 여러개의 소행성들을 그리게 될 것이다. 소행성을 만들기 위해 18줄 이상의 코드를 쳐 넣을 것이다. 소행성 하나 하나를 시스템 상에서 계산하여 매번 소행성을 그려내는 것은 비효율적이다. 소행성이 복잡하면 복잡할 수록 그 효율은 극적으로 떨어지게 된다.
그렇다면 그 해결책은 무엇인가? 바로 Display Lists이다. Display List를 사용함으로써, 소행성을 단 한번만 만들기만 하면 된다. 여기에 텍스쳐 맵핑을 할 수도 있고 색을 입힌다거나 원하는 모든 것들을 적용할 수 있다. 이 Display List에 이름을 부여할 수 있는데, 예를 들어 소행성의 Display List로 생성을 하고 이름을 ‘Asteroid’라고 한후에 나중에 소행성이 필요할때 마다 단지 이미 정해둔 이름인 ‘Asteroid’로써 Display List를 호출하기만 하면 된다. 즉, 앞으로 소행성을 그리고자 한다면 단지 단 한줄의 코드인 glCallList(asteroid)를 호출하기만 하면 된다. 이미 만들어진 소행성은 즉시 화면에 나타나게 된다. 소행성은 이미 Display List에서 모든 계산이 이루어져 생성되었기 때문에 OpenGL은 다시 소행성을 그리기 위해 계산할 필요가 없어진다. 이것은 메모리 상에 이미 계산의 결과를 저장해 놓았기 때문이다. 이것이 바로 왜 Dislpay List를 사용함으로써 속도가 극적으로 향상되는지하는지에 대한 해답니다.
이제 Display List에 대해 배워볼 마음이 생기는가? 🙂 우리는 앞으로 Q-Bert Display List 데모 프로그램을 만들어 볼 것이다. 화면상에 15개의 큐브를 올려 놓아 보는 데모이다. 각각의 큐브는 BOX와 TOP으로 구성되져 있다. TOP은 별도의 Display List로서 만들어질 것인데 이것은 이 것에 좀더 어두운 색으로 연출하고자 함이다. BOX는 윗면이 없는 큐브이다.
이 코드는 강좌 6의 것을 사용한다. 무엇을 전달하고자 하는지를 쉽게 하기 위해 프로그램을 거의 전체적으로 다시 작성할 것이다. 다음의 코드 라인은 대부분의 강좌에서의 표준 코드들이다. (역자주: 강좌 6의 소스 코드를 일단 다운 로드 받아 살펴보기 바란다. 단지 텍스쳐 맵핑에 대한 내용을 다룬 것이다.)
#include // Header File For Windows
#include // Header File For Standard Input/Output
#include // Header File For The OpenGL32 Library
#include // Header File For The GLu32 Library
#include // Header File For The GLaux Library
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HWND hWnd=NULL; // Holds Our Window Handle
HINSTANCE hInstance; // Holds The Instance Of The Application
bool keys[256]; // Array Used For The Keyboard Routine
bool active=TRUE; // Window Active Flag Set To TRUE By Default
bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default
이제, 앞으로 사용할 변수를 선언해 보자. 먼저 하나의 텍스쳐를 저장하기 위한 변수를 선언하고 앞에서 언급한 2개의 Display List를 위한 변수 2개를 선언하자. 이러한 변수는 Display List가 램에서 어디에 저장되어 있는가 하는 지시자로써의 행동한다. 이 2개의 Display List를 BOX와 TOP이라 부를 것이다.
2개의 변수를 선언한 후에 xloop와 yloop라는 변수를 선언하는데 이것은 화면상의 큐브를 위치시키는데 사용된다. 또 xrot와 yrot라는 2개의 변수를 선언하는데 이것은 x와 y축으로 큐브들을 회전시키는데 사용되는 변수들이다.
GLuint texture[1]; // Storage For One Texture
GLuint box; // Storage For The Display List
GLuint top; // Storage For The Second Display List
GLuint xloop; // Loop For X Axis
GLuint yloop; // Loop For Y Axis
GLfloat xrot; // Rotates Cube On The X Axis
GLfloat yrot; // Rotates Cube On The Y Axis
다음에 우리는 2개의 색상 배열을 생성한다. 첫번째의 boxcol은 밝은 계열의 색상들로 빨간색, 오랜지색, 초록색 그리고 파란색의 색상값을 저장한다. {}안에 지정된 각각의 값은 Red, Green Blue 값을 나타낸다. 각각의 {}의 구룹은 지정된 색이다.
두번째 색상의 배열은 어두운 계열의 색상들로 빨간색, 오랜지색, 초록색 그리고 파란색을 위한 것이다. 어두운 색들은 박스의 윗면을 나타내는데 사용된다. 우리는 박스의 나머지 부분보다 박스의 뚜껑을 좀더 어둡게 하고자 한다.
static GLfloat boxcol[5][3]= // Array For Box Colors
{// Bright: Red, Orange, Yellow, Green, Blue
{1.0f,0.0f,0.0f},
{1.0f,0.5f,0.0f},
{1.0f,1.0f,0.0f},
{0.0f,1.0f,0.0f},
{0.0f,1.0f,1.0f}};
static GLfloat topcol[5][3]= // Array For Top Colors
{ // Dark: Red, Orange, Yellow, Green, Blue
{.5f,0.0f,0.0f},
{0.5f,0.25f,0.0f},
{0.5f,0.5f,0.0f},
{0.0f,0.5f,0.0f},
{0.0f,0.5f,0.5f}};
// Declaration For WndProc
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
이제 실제 Display List를 만들어 본다. 이미 짐작했겠지만, 박스를 만들기 위한 모든 코드는 첫번째 List로 하고 뚜껑으로 사용할 박스의 Top은 다른 List에 들어가게 된다. 다음 절에서 더 자세한 내용을 설명하도록 하겠다.
GLvoid BuildLists() // Build Box Display List
{
우리는 OpenGL에게 우리가 2개의 List를 필요로 한다고 알리는 것으로 시작한다. glGenLists(2)는 두개의 List를 위한 공간을 생성하고 첫번재 List를 가르키는 포인터를 반환한다. box가 첫번째 List의 위치를 가지게 된다. 언제든지 box를 호출한다면 첫번째 list에 담긴 물체가 그려지게 된다.
box=glGenLists(2); // Building Two Lists
List를 위한 공간을 마련했으므로 실제 List를 생성해 보자. 우리는 이미 두개의 List를 위한 메모리 공간을 마련했고 box가 첫번째 list를 저장하는 공간을 가르킨다는 것을 알고 있다. 이제 OpenGL에게 알려할 모든 것은 리스드가 어디로 가야하며 만들어질 리스트가 어떤 타입인가 하는 것들이다.
작업을 하기 위해 glNewList() 명령을 사용한다. 우리는 첫번째 인자가 box라는 것을 알게 될 것이다. 이것은 box가 가르키고 있는 메모리의 위치에 리스트가 저장되어져 있다고 openGL에게 알리는 것이다. 두번째 인자인 GL_COMPILE는 OpenGL에게 우리가 메모리 상에 모든 계산을 미리 해 놓으라는 지시인데 이렇게 함으로써 나중에 다시 불피요한 재계산을 하지 않아도 되도록 하는 것이다.
GL_COMPILE은 프로그래밍과 유사하다. 프로그램을 작성한다면 컴파일러에 적재해야 한다. 코드를 실행하기 위해서 매번 컴파일을 해야 한다. 코드가 이미 실행파일 형태로 컴파일 되어있다면 이후부터 컴파일 할 필요없이 실행파일만을 클릭하여 실행하기만 하면 된다. 컴파일이 필요없는 것이다. 일단 GL이 display list를 컴파일했다면 이미 렌더링 준비가 되어진 것이고 나중에 또 컴파일이 요구되지 않는다. 이것이 바로 display list를 사용함으로써 속도를 향상시키는 이유이다.
glNewList(box,GL_COMPILE); // New Compiled box Display List
코드의 다음부는 뚜껑(top)이 없는 박스를 그리는 것이다. 이것은 화면상에 나타나지 않고 단지 display list상에 저장된다.
glNewList()와 glEndList()사이에 당신이 원하는 코드들을 추가할 수 있다. 색상을 설정하거나 텍스쳐 맵핑을 바꾼다거나 하는 것등. 추가할 수 없는 유일한 코드는 list에 지정된 객체의 형태를 변경시키는 코드이다. 일단 list로 만들어지면 변경할 수 없다.
만약 glColor3ub(rand()%255, rand()%255, rand()%255)의 코드를 아래에 제시한 코드에 추가했다면, 아마도 당신은 화면상에 물체가 그려지는 때마다 이것의 색이 바뀐다고 생각할 것이다. 그러나 이미 리스트를 생성할때 색상이 지정되졌기 때문에 색은 변경되지 않는다. (역자주: 만약 변경하고자 한다면 list를 생성할때 색상을 지정하는 코드를 사용해서는 않된다)
glBegin(GL_QUADS); // Start Drawing Quads
// Bottom Face
// - Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
// - Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
// - Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// - Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
// Front Face
// - Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
// - Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// - Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
// - Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
// Back Face
// - Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
// - Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
// - Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
// - Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
// Right face
// - Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
// - Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
// - Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
// - Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// Left Face
// - Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
// - Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
// - Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
// - Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd(); // Done Drawing Quads
우리는 List를 다 만들었다는 것을 OpenGL에게 알리기 위해 glEndList() 명령을 내린다. glNewList()와 glEndList() 사이의 부분은 Display list의 부분이다. glNewList() 앞의 부분이나 glEndList()의 뒷부분은 현재 display list의 부분과는 상관없는 부분이다.
glEndList(); // Done Building The box List
이제, 두번째 Display list를 만들 것이다. 두번째 display list가 메모리에 저장될 곳을 찾기 위해 우리는 예전의 display list(box)에 1을 추가한다. 아래의 코드는 두번째 display list의 위치와 같은 ‘top’을 만들 것이다.
top=box+1; // top List Value Is box List Value +1
이제 두번째 display list을 저장할 곳을 알았으므로 뚜껑에 대한 list를 만들 수 있다. 첫번재 list를 만드는 같은 방법을 사용하지만 이번에는 ‘box’대신 ‘top’에 list를 저장한다고 OpenGL에게 알린다.
glNewList(top,GL_COMPILE); // New Compiled top Display List
다음 코드는 단지 박스의 꼭대기를 그려주는 것이다. Z 평면 상의 간단한 사각형이다.
glBegin(GL_QUADS); // Start Drawing Quad
// Top Face
// - Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
// - Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
// - Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
// - Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glEnd(); // Done Drawing Quad
다시 우리는 OpenGL에게 두번째 list를 생성했다고 glEndList() 명령을 이용해 알린다. 바로 아래처럼. 우리는 성공적으로 2개의 display list를 생성했다.
glEndList(); // Done Building The top Display List
}
비트맵/텍스쳐를 생성하는 코드는 이전에 강좌의 비트맵을 로딩하고 텍스쳐를 생성하는 코드와 같다. (역자주: Dip2K의 튜터리얼도 NeHe의 텍스쳐 맵핑 방법과 동일하다. 우리는 각각의 큐브의 모든 6면 상에 맵핑할 텍스쳐를 원한다.좀더 부드러운 텍스쳐 맵핑을 위해 Mipmapping을 사용하고자 한다. 필자는 픽셀을 보는 것을 무척 싫어한다. (역자주: 픽셀을 본다는 것은 알리아싱 현상을 의미한다) ‘cube.bmp’라는 비트맵을 로딩한다. data 디렉토리에 이 비트맵 파일이 위치한다. LoadBMP 함수를 찾아서 다음과 같이 수정한다.
if (TextureImage[0]=LoadBMP("Data/Cube.bmp")) // Load The Bitmap
폼의 크기가 재조정될때 발생하는 이벤트의 코드는 강좌 6의 코드와 동일하다.
init 코드는 약간의 수정만이 필요하다. BuildList() 코드를 추가한다. 이것은 앞에서 display list를 생성하는 코드를 담고 있다. LoadGLTextures()함수 이후에 BuildList() 를 호출한다는 것에 주의해야 한다. 이것은 BuildList() 함수에서 텍스처를 필요로 하므로 먼저 텍스쳐를 생성해야 한다는 것이다.
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
if (!LoadGLTextures()) // Jump To Texture Loading Routine
{
return FALSE; // If Texture Didn't Load Return FALSE
}
BuildLists(); // Jump To The Code That Creates Our Display Lists
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
다음의 세줄의 코드는 quick & dirty 빛을 활성화 하는 것이다. (역자주: quick & dirty 광원은 비디오 카드에 기본적으로 설정된 광원이다) Light0는 대부분의 비디오 카드에 미리 정의된 것이다. light0을 활성화 함으로써 빛을 사용한다. 만약 독자의 비디오 카드에서 light0가 작동하지 않는다면(화면에 마냥 검다면) 그냥 광원을 꺼라.
마지막 라인인 GL_COLOR_MATRIAL은 텍스쳐 맵핑에 색을 추가하도록 해 준다. 이 재질의 컬러링을 가능하지 않게 한다면, 텍스쳐는 항상 원래의 색상으로 나타난다. 즉 glColor3f(r,g,b)가 아무런 효과를 내지 못한다. 그래서 이것을 가능하게 해주는 것이 중요하다.
glEnable(GL_LIGHT0); // Quick And Dirty Lighting (Assumes Light0 Is Set Up)
glEnable(GL_LIGHTING); // Enable Lighting
glEnable(GL_COLOR_MATERIAL); // Enable Material Coloring
최종적으로 멋지게 보이도록 하기 위해서 투영에 대한 힌트를 GL에게 제공하고 초기화가 잘되었음을 알리기 위해 TRUE를 반환한다.
// Nice Perspective Correction
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
return TRUE; // Initialization Went OK
이제 그려주는 코드에 대한 것이다. 일단 화면과 Depth Buffer를 청소하는 일부터 시작한다.
그 다음에 큐브에 텍스쳐 맵핑을 바인드한다. Display list 안에 이 텍스쳐 맵핑을 추가할 수 있었으나 그 밖으로 빼 놓았다. 이것으로써 원한다면 언제든 텍스쳐 맵핑을 변경할 수 있는 것이다. (역자주: 이미 앞에서 언급한대로 List를 만들때 색상이라든지 텍스쳐 맵핑을 지정하면 나중에 그 List의 객체의 색상, 텍스쳐 맵핑을 변경할 수 없다)
int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texture[0]); // Select The Texture
이제 재미있는 것에 대해 살펴보자. yloop라 불리는 반복문이 있다. 이 반복문은 큐브들의 Y 축상(위에서 아래)에 위치시키는데 사용된다. 위에서 아래로 5개의 큐브를 원하므로 6보다 하나 작은 반복을 한다(즉, 다섯번)
for (yloop=1;yloop<6;yloop++) // Loop Through The Y Plane
{
xloop라는 다른 반복문이 있다. 이것은 X 축상(왼쪽에서 오른쪽)에 큐브들을 위치시키는데 사용된다. 왼쪽에서 오른쪽으로 그리고자 하는 큐브는 정해져 있지 않다. 만약 가장 위쪽의 줄이라면 xloop는 0에서 1까지의 xloop 반복이면 된다. 그리고 그 다음 줄의 xloop는 0에서 2가 될것이다(즉, 2개의 큐브가 그려져야 한다) (역자주: 계속 하나씩 증가한다, 실제 결과 프로그램을 실행하여 살펴보면 확실히 알수있다)
// Loop Through The X Plane
for (xloop=0;xloop {
glLoadIdentity()를 호출함으로써 View를 초기화한다.
glLoadIdentity(); // Reset The View
다음은 화면상의 지정된 지점으로 이동시키는 코드이다. 이것은 다소 헤깔리지만 실제로는 그렇지 않다. X축 상에서, 다음의 내용이 이뤄진다 :
큐브들이 모여 이뤄진 피라미드가 화면상의 중심에 놓여지도록 오른쪽으로 1.4 유닛만큼 이동한다. 그 다음에 xloop에 2.8을 곱한후 다시 여기에 1.4를 더한다. 마지막으로 yloop*1.4를 뺀다. 이것은 큐브들을 그들이 위치한 줄에 따라 왼쪽으로 이동하게 된다.
Y축상에서 6에서 yloop를 뺌으로써 아래 방향으로 피라미드가 만들어진다. 다음에 그 결과에 2.4를 곱한다. 큐브들은 y축 상의 각각의 꼭대기 상에 놓여진다. 다음에 7을 빼는데 이것은 피라미드가 화면의 아래쪽에서 시작하고 윈쪽으로 만들어지게 한다.
최종적으로 Z축 상에서 화면 안쪽으로 20 유닛 이동한다. 이것은 피라미드를 멋지게 만든다.
// Position The Cubes On The Screen
glTranslatef(1.4f+(float(xloop)*2.8f)-(float(yloop)*1.4f),
((6.0f-float(yloop))*2.4f)-7.0f,-20.0f);
이제 x축 상으로 회전시켜 보자. 우리는 큐브를 뷰쪽으로 45도에서 yloop*2를 뺀 상태로 기울인다.. 퍼스펙티브 모드는 자동으로 큐브들을 기울이므로 이 기울어짐을 제거 했다. 이것은 최선은 아니지만 원하는대로 작동한다. 🙂
최종적으로 xrot를 더한다. 이것은 키보드를 통해 각을 조정할 수있도록 한다.
x축으로 회전한후에 y축상으로 45도 회전하고 yrot을 더하는데 이것은 키보드를 통해서 yrot값을 조정할 수 있다.
glRotatef(45.0f-(2.0f*yloop)+xrot,1.0f,0.0f,0.0f); // Tilt The Cubes Up And Down
glRotatef(45.0f+yrot,0.0f,1.0f,0.0f); // Spin Cubes Left And Right
다음에 큐브들이 위치한 줄에 따라 달리 색상을 지정한다.
glColor3fv(boxcol[yloop-1]); // Select A Box Color
이제 박스에 대한 List를 호출한다.
glCallList(box); // Draw The Box
큐브의 꼭대기에 놓여질 뚜껑의 색상을 지정한다.
glColor3fv(topcol[yloop-1]); // Select The Top Color
뚜껑에 대한 List를 호출하고 모든것이 잘되었으므로 TRUE를 호출한다.
glCallList(top); // Draw The Top
}
}
return TRUE; // Jump Back
}
이제 남은 코드들은 WinMain 함수에서 이뤄지는데 다음은 키보드에 대해 xrot와 yrot의 값을 변경해 주는 키보드 입력 처리 코드이다. SwapBuffer(hDC) 코드 다음에 놓여진다.
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
if (keys[VK_LEFT]) // Left Arrow Being Pressed?
{
yrot-=0.2f; // If So Spin Cubes Left
}
if (keys[VK_RIGHT]) // Right Arrow Being Pressed?
{
yrot+=0.2f; // If So Spin Cubes Right
}
if (keys[VK_UP]) // Up Arrow Being Pressed?
{
xrot-=0.2f; // If So Tilt Cubes Up
}
if (keys[VK_DOWN]) // Down Arrow Being Pressed?
{
xrot+=0.2f; // If So Tilt Cubes Down
}
<변역 끝>
변역을 여기서 끝마친다. 사실 이 강좌는 Display List에 대한 장이다. Display List에 대한 사용법이 쉽다는 것을 알 수 있다. 필자는 이 강좌를 번역할때 List를 제작하고 사용하는 방법에 대해서는 최대한 모든내용을 번역했지만 후반부의 큐브를 화면상에 피라미드 모양으로 위치시키는 세세한 내용은 그냥 술~ 술~ 넘기며 번역했음을 고백한다.