본문 바로가기
Opengles 3.0 with Android

Chapter 2.2 (Vertex Array object) 사용한 Cube 렌더링

by SimonLee 2024. 10. 22.

 

큐브 렌더링에 앞서 VBO와 VAO의 차이의 대해서 정리하자.

 

**VAO (Vertex Array Object)**와 **VBO (Vertex Buffer Object)**는 모두 OpenGL에서 버텍스 데이터를 효율적으로 관리하고 렌더링하는 데 사용되는 객체입니다. 이 둘은 서로 보완적인 역할을 하며, 그래픽 파이프라인에서 버텍스 데이터를 처리하는 데 중요한 역할을 합니다. 그러나 VAO와 VBO는 그 목적과 기능이 다릅니다.

1. VAO (Vertex Array Object)

VAO는 버텍스 속성 상태를 저장하는 객체입니다. 이것은 버텍스 데이터의 포맷과 속성 설정을 관리합니다. VAO는 다음과 같은 정보를 저장합니다.

  • VBO를 통해 바인딩된 버텍스 데이터의 속성 포인터 설정 (glVertexAttribPointer로 설정한 속성)
  • 활성화된 버텍스 속성 (glEnableVertexAttribArray로 설정한 속성)
  • VBO의 바인딩 상태 (어떤 VBO가 바인딩되었는지)

VAO를 사용하면 버텍스 배열의 구성(포맷 및 속성)을 한 번 설정하고 저장할 수 있습니다. 이후에 VAO를 바인딩하면 저장된 설정을 즉시 적용할 수 있기 때문에, 여러 개의 객체를 효율적으로 관리하고 렌더링하는 데 유리합니다.

2. VBO (Vertex Buffer Object)

VBO는 버텍스 데이터를 GPU 메모리에 저장하는 버퍼 객체입니다. 이 버퍼는 모델의 정점, 색상, 텍스처 좌표, 노멀 등의 데이터를 포함할 수 있습니다. VBO는 GPU에 직접 데이터를 전달하여 렌더링 성능을 크게 향상시킬 수 있습니다.

  • 버텍스 데이터는 VBO에 저장되며, 이는 glBufferData 또는 glBufferSubData와 같은 함수를 사용하여 GPU 메모리로 전송됩니다.
  • VBO는 버퍼 바인딩을 통해 활성화되며, 이후의 OpenGL 함수들은 이 바인딩된 VBO에 데이터를 저장하거나 읽습니다.
  • 여러 개의 VBO를 사용하여 정점의 위치, 색상, 텍스처 좌표 등을 각각 별도의 VBO에 저장할 수도 있습니다.

VAO와 VBO의 차이점

특성VAO (Vertex Array Object)VBO (Vertex Buffer Object)

역할 버텍스 속성의 설정 및 상태 저장 버텍스 데이터를 GPU 메모리에 저장
주요 기능 버텍스 속성 포인터와 속성 활성화 상태를 저장 정점 위치, 색상, 텍스처 좌표 등의 실제 데이터를 저장
설정 방법 glBindVertexArray로 바인딩 후 속성 설정 glBindBuffer(GL_ARRAY_BUFFER, ...)로 바인딩
재사용성 다양한 버텍스 속성을 저장하고, 필요한 시점에 다시 사용 동일한 버퍼를 여러 번 바인딩하여 사용할 수 있음
저장하는 데이터 종류 버텍스 배열 포맷과 속성 설정 정보 실제 버텍스 데이터(정점 좌표, 색상 등)
성능 최적화 한번 설정한 속성 포인터 및 VBO 바인딩을 재사용 데이터를 GPU 메모리에 저장하여 CPU-GPU 데이터 전송 최소화

VAO와 VBO의 사용 방식

  1. VBO 생성 및 데이터 전송
    • 먼저 VBO를 생성하고, 버텍스 데이터를 GPU에 전송합니다.
    • 예를 들어, 정점 위치 데이터를 VBO에 저장합니다.
    GLuint vbo; glGenBuffers(1, &vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  2. VAO 생성 및 설정
    • VAO를 생성하고 바인딩한 뒤, VBO의 속성을 VAO에 저장합니다.
    • 이 과정에서 VBO의 포인터 설정과 속성 활성화를 glVertexAttribPointer와 glEnableVertexAttribArray로 수행합니다.
    GLuint vao; 
    glGenVertexArrays(1, &vao); 
    glBindVertexArray(vao); 
    
    // Start 여기서 부터 어트리뷰트 속성 값이 저장됨.
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); 
    // End
    
    glEnableVertexAttribArray(0)
  3. 렌더링
    • VAO를 바인딩하고 그리기 명령을 호출하면, VAO에 저장된 설정을 사용하여 렌더링됩니다.
glBindVertexArray(vao); 
glDrawArrays(GL_TRIANGLES, 0, vertexCount);

요약

  • **VAO (Vertex Array Object)**는 버텍스 속성의 설정을 저장하고 관리하는 객체입니다. VAO를 사용하면 버텍스 배열의 설정을 한 번만 수행하고, 필요할 때마다 해당 설정을 불러와 효율적으로 사용 가능합니다.
  • **VBO (Vertex Buffer Object)**는 버텍스 데이터를 GPU 메모리에 저장하는 객체입니다. VBO는 실제 데이터를 GPU에 전달하고, CPU와 GPU 간의 데이터 전송을 줄여 성능을 향상시킵니다.

VAO와 VBO를 함께 사용하면 복잡한 버텍스 데이터 관리 및 렌더링 설정을 최적화할 수 있습니다. VAO는 속성 관리, VBO는 데이터 저장에 중점을 둔다는 점이 핵심 차이점입니다.

 

아래 예제는 하나의 오브젝트를 렌더링 할때는 VAO를 사용안해도 큰 문제는 안되지만

여러 개의 오브젝트 렌더링 할때 VAO를 사용하지 않으면,  렌더링 함수에서 매번 
glEnableVertexAttribArray(), glVertexAttribPointer() 를 호출해 주어야 한다.

 

Vao를 사용하면 Vao만 바인딩을 하게 되면 버텍스 속성 설정을 전부 설정해 주어 편리하다.

아래 코드는 VAO를 사용하지 않은 코드이다.

void Square::CreateVBO() {
    // VBO 생성 -> 바인딩 -> 버퍼에 데이터 복사
    glGenBuffers(1, &bufferId);
    glBindBuffer(GL_ARRAY_BUFFER, bufferId);
    // 버텍스 배열 먼저 넣고 뒤에 컬러 배열 있음.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) + sizeof(colors), nullptr, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices), sizeof(colors), colors);

    // EBO 생성 -> 바인딩 -> 버퍼 복사
    glGenBuffers(1, &indexId);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW);
}


void Square::Render() {
    glUseProgram( program->ProgramID );

    // 버퍼 정보를 셰이더에 알려줌
    char attrib = ProgramManagerObj->ProgramGetVertexAttribLocation(program, (char*)"VertexPosition");
    glVertexAttribPointer(attrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(attrib);
    char attribColor = ProgramManagerObj->ProgramGetVertexAttribLocation(program, (char*)"VertexColor");
    // 버텍스 배열 이후에 컬러 배열 있음.
    glVertexAttribPointer(attribColor, 3, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(vertices));
    glEnableVertexAttribArray(attribColor);

    // 렌더링 삼각형 2개는 count : 6, 마지막 인자는 0이면 바인딩된 인덱스 버퍼 처음부터 사용.
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

 

VAO만 바인딩이 되면,

glVertexAttribPointer로 설정한 버텍스 속성 포인터와 그 설정도 VAO에 포함됩니다.

이렇게 설정된 속성 포인터와 활성화 상태는 VAO에 저장되어,

나중에 동일한 VAO를 바인딩하면 한 번에 모든 어트리뷰트와 속성 포인터가 적용됩니다.

 

아래 코드에서 VBO를 생성하고 GPU로 데이터를 전송하고,

VAO를 생성하고 어트리뷰트 설정값을 입력하면, 

렌더링 코드에서는 glBindVertexArray(vao) 한줄의 코드로 어트리뷰트 바인딩을 대체할 수 있다.

 

추가적으로

VBO 설정이 끝나고 VBO 언바인딩 할때 

GL_ELEMENT_ARRAY_BUFFER에 바인딩된 인덱스 버퍼는 VAO에 종속되기 때문에 VBO 언바인딩을 하지 않아도 되지만,  GL_ARRAY_BUFFER는 VAO와 독립적이기 때문에 언바인딩이 필요하다.

Cube::Cube( Renderer* parent )
{
    ...
    // VBO 생성
    glGenBuffers(1, &vId);
    glGenBuffers(1, &iId);

    // 버텍스 VBO 설정
    size = 24 * sizeof(float);
    glBindBuffer( GL_ARRAY_BUFFER, vId );
    glBufferData( GL_ARRAY_BUFFER, size + size, 0, GL_STATIC_DRAW );
    glBufferSubData( GL_ARRAY_BUFFER, 0,			size,	cubeVerts );
    glBufferSubData( GL_ARRAY_BUFFER, size,			size,	cubeColors );
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // 인덱스 VBO 설정
    unsigned short indexSize = sizeof( unsigned short ) * 36;
    glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, iId );
    glBufferData( GL_ELEMENT_ARRAY_BUFFER, indexSize, 0, GL_STATIC_DRAW );
    glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, indexSize,	cubeIndices );
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

void Cube::InitModel()
{
    ...
    
    // VAO 바인딩 - 여기서부터 모든 설정이 VAO에 저장됨
    glGenVertexArrays(1, &vAo);
    glBindVertexArray(vAo);

    glBindBuffer(GL_ARRAY_BUFFER, vId);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iId);

    // 버텍스 버퍼 어트리뷰트 설정
    glEnableVertexAttribArray(attribColor);
    glEnableVertexAttribArray(attribVertex);
    glVertexAttribPointer(attribVertex, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glVertexAttribPointer(attribColor, 3, GL_FLOAT, GL_FALSE, 0, (void*)size);

    glBindBuffer( GL_ARRAY_BUFFER, 0 );
    // GL_ELEMENT_ARRAY_BUFFER는 GL_ARRAY_BUFFER와는 다르게, VAO에 영구적으로 바인딩되기 때문에 언바인딩 할필요 없음.
    // GL_ELEMENT_ARRAY_BUFFER에 바인딩된 인덱스 버퍼는 VAO에 종속됩니다. 즉, 특정 VAO가 바인딩될 때마다 해당 VAO에 바인딩된 인덱스 버퍼가 함께 활성화됩니다.
    glBindVertexArray(0); // 여기까지 설정이 다 저장됌.

    return;
}

void Cube::RenderCube()
{
    glBindVertexArray(vAo);
    glUniformMatrix4fv( mvp, 1, GL_FALSE,(float*)TransformObj->TransformGetModelViewProjectionMatrix() );
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, (void*)0);
    glBindVertexArray(0);
}

 

특히 멀티 오브젝트 렌더링할때는

렌더링 코드가 코드의 간결함을 위해 VAO를 반드시 사용하자.

 

이제 MVP 행렬의 대해서 알아본다.

 

MVP 행렬은 Model-View-Projection 행렬의 약자로, 3D 그래픽스에서 객체의 좌표를 화면에 렌더링하기 위한 일련의 변환 과정을 정의합니다. MVP 행렬은 모델 행렬(Model Matrix), 뷰 행렬(View Matrix), **투영 행렬(Projection Matrix)**의 곱셈을 통해 만들어집니다. 이 세 가지 행렬은 3D 좌표를 화면 좌표로 변환하는 과정을 단계적으로 처리합니다.

1. 모델 행렬 (Model Matrix)

모델 행렬은 객체의 로컬 좌표계에서 월드 좌표계로 변환하는 행렬입니다. 즉, 각 객체가 월드 공간 내에서의 위치, 회전, 크기를 정의합니다.

  • 예를 들어, 큐브가 원점(0, 0, 0)에 위치하고 있을 때, 모델 행렬을 사용하여 큐브를 특정 위치로 이동하거나 회전, 크기 조정(스케일링)할 수 있습니다.
  • 모델링 변환은 모델링 좌표계에서의 객체의 위치를 월드 좌표계로 변환하는 과정입니다.

2. 뷰 행렬 (View Matrix)

뷰 행렬은 카메라의 위치와 방향을 설정하는 행렬로, 월드 좌표계를 카메라 좌표계로 변환합니다. 이를 통해 객체를 카메라 시점에서 바라보는 관점으로 변환할 수 있습니다.

  • 뷰 행렬은 카메라의 위치와 방향을 설정하여 장면의 시점을 정의합니다.
  • glm::lookAt 함수를 통해 카메라의 위치, 바라보는 방향, 위쪽 방향을 설정할 수 있습니다.
  • 이 변환은 객체의 월드 좌표를 카메라의 상대적 좌표로 변환하여, 카메라 시점에서 바라보는 장면을 구성합니다.

3. 투영 행렬 (Projection Matrix)

투영 행렬은 카메라 좌표계의 3D 좌표를 화면의 2D 좌표로 변환하는 과정입니다. 투영 행렬에는 **원근 투영(Perspective Projection)**과 **직교 투영(Orthographic Projection)**이 있습니다.

  • 원근 투영: 카메라에서 멀어질수록 물체가 작아 보이도록 하는 방식으로, 현실적인 원근감을 제공합니다. glm::perspective 함수로 설정할 수 있습니다.
  • 직교 투영: 모든 물체가 카메라에서의 거리와 관계없이 동일한 크기로 보이도록 하는 방식으로, 주로 CAD나 2D 게임에서 사용됩니다. glm::ortho 함수로 설정합니다.

MVP 행렬의 구성

MVP 행렬은 다음과 같이 계산됩니다:

MVP = Projection * View * Model;

 

계산 순서는 모델 좌표 → 월드 좌표 → 카메라 좌표 → 화면 좌표로 변환됩니다.

  1. Model 행렬: 로컬 좌표를 월드 좌표로 변환
  2. View 행렬: 월드 좌표를 카메라 좌표로 변환
  3. Projection 행렬: 카메라 좌표를 화면 좌표로 변환

이 과정은 각각의 행렬을 차례로 곱하는 방식으로 구현되며, 최종적으로 3D 장면을 2D 화면에 올바르게 렌더링할 수 있도록 변환해줍니다.

MVP 행렬의 중요성

MVP 행렬은 3D 그래픽스에서 객체의 위치와 크기를 제어하고, 카메라와의 관계를 설정하며, 최종적으로 화면에 표시할 수 있는 좌표로 변환하는 데 필수적입니다.

  • 모델링 변환(Model): 객체의 위치와 모양을 조정
  • 뷰 변환(View): 카메라 시점에서 바라보는 장면을 구성
  • 투영 변환(Projection): 3D 장면을 화면 좌표로 변환
glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5.0f));
glm::mat4 view = glm::lookAt(cameraPos, cameraTarget, upVector);
glm::mat4 projection = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);

glm::mat4 mvp = projection * view * model;

 

위 코드는 모델, 뷰, 투영 행렬을 각각 설정하고, 이들을 곱하여 최종적인 MVP 행렬을 계산하는 예시입니다.

원근 투영과 직교 투영의 대해서 자세히 알아본다.

perspective와 ortho는 3D 그래픽스에서 3D 장면을 2D 화면에 렌더링할 때 사용하는 두 가지 주요 투영 방식입니다. 이들은 각기 다른 방식으로 물체의 깊이감을 표현하며, **원근 투영(perspective projection)**과 **직교 투영(orthographic projection)**으로 불립니다.

1. 원근 투영 (Perspective Projection)

원근 투영은 현실 세계에서 우리가 물체를 보는 방식과 유사하게, 카메라에서 멀어질수록 물체가 작아지고 가까울수록 커지게 보이도록 하는 투영 방식입니다. 이를 통해 깊이감과 원근감을 시각적으로 표현할 수 있습니다.

주요 특징

  • 원근감 표현: 물체가 멀리 있을수록 작게, 가까이 있을수록 크게 보입니다. 예를 들어, 기차의 선로가 멀리 갈수록 좁아지는 것처럼 보이는 효과가 발생합니다.
  • FOV(Field of View, 시야각): 원근 투영에서는 시야각을 설정할 수 있습니다. 시야각이 넓으면 더 많은 장면이 보이지만 원근감이 더 두드러지게 나타나고, 시야각이 좁으면 물체가 더 가까이서 크게 보이는 효과가 있습니다.
  • 주로 사용되는 경우: 게임, 영화 등 현실적인 3D 장면을 렌더링할 때 사용됩니다.

설정 방식

원근 투영은 glm::perspective 함수를 사용하여 설정할 수 있습니다. 예를 들어:

glm::mat4 projection = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);
  • 45.0f는 시야각(FOV)이며, 화면에 보이는 범위를 결정합니다.
  • aspectRatio는 화면의 가로 세로 비율입니다.
  • 0.1f와 100.0f는 클리핑 거리로, 카메라로부터 렌더링할 최소 및 최대 거리입니다.

2. 직교 투영 (Orthographic Projection)

직교 투영은 원근감을 무시하고 물체의 크기를 일정하게 유지하는 투영 방식입니다. 거리에 상관없이 모든 물체가 동일한 크기로 보이게 됩니다.

주요 특징

  • 원근감 없음: 카메라에서 멀어지더라도 물체의 크기가 변하지 않습니다. 물체의 크기는 화면에서의 크기와 무관하게 고정됩니다.
  • 평행 투영: 모든 투영선이 평행하게 유지되며, 멀리 있는 물체와 가까운 물체의 크기 차이가 없습니다.
  • 주로 사용되는 경우: CAD 소프트웨어, 2D 게임, 건축 시각화 등 물체의 실제 크기를 정확하게 표현해야 하는 경우에 사용됩니다.

설정 방식

직교 투영은 glm::ortho 함수를 사용하여 설정할 수 있습니다. 예를 들어:

glm::mat4 projection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, 0.1f, 100.0f);
  • -10.0f와 10.0f는 좌우 클리핑 평면의 위치를 설정합니다.
  • -10.0f와 10.0f는 하단 및 상단 클리핑 평면의 위치를 설정합니다.
  • 0.1f와 100.0f는 가까운 평면과 먼 평면의 깊이 클리핑 범위를 나타냅니다.

원근 투영과 직교 투영의 차이점 요약

특성원근 투영 (Perspective)직교 투영 (Orthographic)

원근감 멀리 있는 물체가 작게, 가까운 물체가 크게 보임 모든 물체가 동일한 크기로 보임
투영선 중심점을 향해 수렴 평행하게 유지
사용 사례 게임, 영화, 가상 현실 CAD, 2D 게임, 기술적 도면
설정 방식 glm::perspective glm::ortho
시야각 설정 시야각(FOV)을 설정할 수 있음 시야각 설정 없음

예시로 차이 이해하기

  • 원근 투영 예시: 사람의 눈으로 사물을 볼 때와 유사합니다. 가까이 있는 물체는 크게 보이고, 멀리 있는 물체는 작게 보입니다. 이 방식은 3D 게임이나 영화에서 현실적인 깊이감을 표현하기에 적합합니다.
  • 직교 투영 예시: 종이 위에 그림을 그리거나, CAD 소프트웨어에서 도면을 볼 때와 유사합니다. 모든 물체가 실제 크기 그대로 보이며, 원근감 없이 정확한 크기로 표시됩니다.

정리

  • **원근 투영(Perspective Projection)**은 현실적인 깊이감과 원근감을 시각적으로 표현하는 데 사용되며, 3D 그래픽스에서 흔히 사용됩니다.
  • **직교 투영(Orthographic Projection)**은 원근감을 무시하고 물체의 크기를 일정하게 유지하는 방식으로, 기술적 도면이나 2D 그래픽에서 주로 사용됩니다.

 

요약

MVP 행렬은 3D 좌표를 화면에 렌더링하기 위해 필요한 변환 단계를 모두 포함합니다. 모델 변환 → 뷰 변환 → 투영 변환의 순서로 계산하여 최종적으로 화면에 물체를 올바르게 표시할 수 있습니다. 이를 통해 3D 장면을 카메라 시점에서 관찰하고, 원근감을 적용하여 화면에 정확하게 렌더링할 수 있게 됩니다.

 

아래 코드를 보자.

 

아래 코드는 TransformObj 라는 클래스에 행렬 계산 하는 부분이 포함이 되어있다.

MVP 행렬을 구해야 하는데, MVP 행렬을 곱하는 순서는 반대이다.

Projection Matvix * View Matrix *  Model Matrix 이다.

 

아래 코드를 보면 각 행렬을 계산하는 부분은 다음과 같다.

Projection Matrix

- 원근 기법을 적용하기 위한 매트릭스이다. ( perspective 만 !! )

- fov, aspect ratio, near z, far z 4가지의 파라메터가 들어가서 행렬이 계산되어 진다.

View Matrix (camera Matrix)

- eye, center, up 파라메터로 카메라 스페이스로 이동할때 사용하는 행렬이다.

- TransformLookat() 함수를 사용

Model Matrix

- 오브젝트 스페이스의 이동,회전,스케일 적용할 행렬이다.

 ProgramManager* ProgramManagerObj	= &RenderMemData.ProgramManagerObj;
	....
    TransformObj->TransformSetMatrixMode( PROJECTION_MATRIX );
    TransformObj->TransformLoadIdentity();
    TransformObj->TransformSetPerspective(60.0f, aspectRatio, 0.001, 1000, 0);
    
    TransformObj->TransformSetMatrixMode(VIEW_MATRIX);
    TransformObj->TransformLoadIdentity();
    glm::vec3 eye(0.0f, 0.0f, 40.0f);      // 카메라 위치
    glm::vec3 center(0.0f, 0.0f, 0.0f);    // 카메라가 바라보는 지점
    glm::vec3 up(0.0f, 1.0f, 0.0f);        // 카메라의 위쪽 방향
    TransformObj->TransformLookAt(&eye, &center, &up);
    
    TransformObj->TransformSetMatrixMode( MODEL_MATRIX );
    TransformObj->TransformLoadIdentity();

 

현재  모델 매트릭스로 설정이 되어있어서 translate 하면 오브젝트 스페이스에 서 

사각형이 transform 된다.

 

아래 RenderCubeOfCubes 에서는

여러개의 큐브를 동시에 렌더링 하기 위해서 각 행렬을 배열에 저장한다.

void Cube::Render()
{
    glEnable( GL_DEPTH_TEST );
    glUseProgram( program->ProgramID );
    if(distance > 5)
        op = true;
    if(distance < 2.0)
        op = false;

    if (Animate){
        if(op)
            distance -= 0.1;
        else
            distance += 0.1;
    }

    TransformObj->TransformTranslate(0.0f, 0.0f, 30.f);
    TransformObj->TransformRotate(k++, 0, 1, 0);
    RenderCubeOfCubes();

    // LOGI("Render");
}

void Cube::RenderCube()
{
    glBindVertexArray(vAo);
    glUniformMatrix4fv( mvp, 1, GL_FALSE,(float*)TransformObj->TransformGetModelViewProjectionMatrix() );
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, (void*)0);
    glBindVertexArray(0);
}

void Cube::RenderCubeOfCubes()
{
    TransformObj->TransformTranslate(-distance*dimension/2,  -distance*dimension/2, -distance*dimension/2);
    for (int i = 0; i < dimension; i++){
        TransformObj->TransformTranslate(distance,  0.0, 0.0);
        TransformObj->TransformPushMatrix();

        for (int j = 0; j < dimension; j++){
            TransformObj->TransformTranslate(0.0,  distance, 0.0);
            TransformObj->TransformPushMatrix();

            for (int k = 0; k < dimension; k++){
                 TransformObj->TransformTranslate(0.0,  0.0, distance);
                 RenderCube();
            }
            TransformObj->TransformPopMatrix();
        }
        TransformObj->TransformPopMatrix();
    }
}

 

3D 공간에 사각형(큐브)을 격자로 배치하여, 여러 개의 큐브가 일정한 간격으로 반복적으로 쌓여 보이게 만드는 구조입니다. dimension과 distance를 통해 격자의 크기와 간격을 조정하여 화면에 큐브의 집합체를 렌더링합니다. 설명을 단계별로 해 보겠습니다.

코드 동작 요약

  1. 초기 위치 설정
    • TransformTranslate(-distance * dimension / 2, -distance * dimension / 2, -distance * dimension / 2);을 통해 전체 큐브 구조의 시작 위치를 공간의 중심에 배치합니다.
    • 이렇게 하면 큐브 구조의 중심이 화면의 중심에 오게 되어 전체 격자가 중심을 기준으로 균형 있게 배치됩니다.
  2. 외부 루프: i 루프 (X축 방향)
    • 첫 번째 for 루프는 i를 기준으로 X축 방향으로 큐브의 위치를 distance 간격으로 이동시키며, 각 열의 큐브를 그립니다.
    • TransformPushMatrix와 TransformPopMatrix를 통해 X축 위치가 다른 새 열로 이동할 때 이전 위치로 돌아가도록 저장해 줍니다.
  3. 중간 루프: j 루프 (Y축 방향)
    • 두 번째 for 루프는 j를 기준으로 Y축 방향으로 이동하면서 X축 방향에서 같은 Y 좌표에 있는 큐브 열을 그립니다.
    • TransformPushMatrix와 TransformPopMatrix를 사용하여 각 열의 Y축 위치를 독립적으로 조정할 수 있도록 합니다.
  4. 내부 루프: k 루프 (Z축 방향)
    • 세 번째 for 루프는 k를 기준으로 Z축 방향으로 큐브를 distance 간격으로 이동하면서 그리므로 X, Y 위치가 동일한 Z 축의 큐브 열을 생성합니다.
    • RenderCube() 함수가 호출될 때마다 하나의 큐브가 그려집니다.
  5. 결과
    • 전체적으로 dimension x dimension x dimension 크기의 3D 격자 구조가 만들어집니다.
    • distance 값이 크면 큐브 간의 간격이 넓어지고, 작으면 큐브가 더 촘촘히 배치됩니다.

코드 예시 결과

예를 들어, dimension = 3이고 distance = 1.0이라면 3x3x3 배열의 큐브가 1 단위 거리 간격으로 배치되어, 화면에 27개의 큐브가 보이게 됩니다. dimension이 커질수록 큐브의 개수가 기하급수적으로 증가하게 됩니다.