눈팅하는 게임개발자 블로그

OpenGL Getting Started 2-8 Coordinate System 본문

공부한거/OpenGL

OpenGL Getting Started 2-8 Coordinate System

Palamore 2020. 9. 29. 16:36

원본 사이트

learnopengl.com/Getting-started/Coordinate-Systems

 

LearnOpenGL - Coordinate Systems

Coordinate Systems Getting-started/Coordinate-Systems In the last chapter we learned how we can use matrices to our advantage by transforming all vertices with transformation matrices. OpenGL expects all the vertices, that we want to become visible, to be

learnopengl.com

번역 사이트

heinleinsgame.tistory.com/11?category=757483

 

[Learn OpenGL 번역] 2-8. 시작하기 - 좌표 시스템

좌표 시스템 시작하기/좌표 시스템 마지막 강좌에서 변환 행렬을 사용하여 모든 vertex들을 변환함으로써 수학을 잘 활용하는 방법을 배웠습니다. OpenGL읜 각 vertext shader 실행 된 후 우리가 그리기

heinleinsgame.tistory.com

 

좌표 시스템

OpenGL은 vertex shader 실행 후 화면에 나타나야 할 모든 vertex들이 정규화된 디바이스 좌표(NDC)로 표시되어야 한다.

vertex의 x, y, z좌표가 -1.0 ~ 1.0 사이에 있어야 하며 이 범위 밖의 좌표는 화면상에 나타나지 않는다.

일반적으로는 직접 구성한 범위의 좌표를 vertex shader에서 NDC로 변환한다.

 

NDC로 좌표를 변환한 다음 스크린 좌표로 변환하는 것은 일반적으로 최종 스크린 좌표로 변환하기 전에

오브젝트의 vertex를  여러 좌표 시스템으로 변환하는 단계적인 방법으로 수행한다.

이들을 여러 중간 좌표 시스템으로 변환하면 일부 연산들이 특정 좌표 시스템에서 수행하기 쉬워진다는 장점이 있다.

주요 좌표 시스템은 5가지가 있다.

Local Space (Object Space)

World Space

View Space (Eye Space)

Clip Space

Screen Space

위 시스템들은 vertex들이 변환되어 결국 fragment가 되기 전에는 모두 다른 상태를 가진다.

 

전체 그림

하나의 공간에서 다음 좌표 공간으로 좌표를 변환하기 위해 여러가지 변환 행렬을 사용하는데.

이 중 가장 중요한 것들은 model, view, projection 행렬이다.

버텍스 좌표는 먼저 local 좌표를 사용하여 local 공간에서 시작하고 이를 처리하여

world 좌표, view 좌표, clip 좌표, 마지막으로 screen 좌표로 변환된다.

다음 이미지는 이 과정을 보여주고 각 변환이 무엇을 수행하는지 보여준다.

 

1. Local 좌표는 오브젝트의 원점을 기준으로 한 좌표이다.

2. local 좌표를 더 큰 world에 대한 좌표인 world space 좌표로 변환한다.

이 좌표는 전체적인 world의 원점을 기준으로 하는 좌표이고 해당 원점을 기준으로 다른 오브젝트들을 공간에 배치한다.

3. world 좌표를 각 좌표들이 카메라의 시점에서 보이는 view space 좌표로 변환한다.

4. 좌표들이 view space로 변환된 후 좌표들을 clip 좌표로 투영한다.

clip 좌표는 -1.0 ~ 1.0 범위로 처리되고 vertex들이 화면 위에 그려지게 될 것인지를 결정한다.

5. 마지막으로 viewport 변환이라고 불리는 작업 ( -1.0 ~ 1.0 범위의 좌표들을 glViewport 함수를 통해

정의된 좌포 볌위로 변환)을 통해 clip 좌표를 screen 좌표로 변환한다.

이후 결과물은 rasterizer로 보내져 fragment로 변환된다.

 

모든 vertex들을 이 모든 공간으로 변환하는 이유는 일부 연산들이 특정 좌표 시스템에서

사용하기가 쉬워지기 때문이다.

예를 들어 하나의 오브젝트를 수정할 때는 local space에서.

오브젝트의 월드 내 위치, 각도 등을 수정할 때에는 world space에서 수행한다.

local space에서 clip space로 한 번에 변환하는 변환 행렬을 정의할 수 있지만

이는 위와 같은 상황에서의 유연성을 저하시킨다.

 

Local Space

오브젝트에 대한 좌표 공간이다. (오브젝트를 중심으로 하는 하나의 공간)

최종 응용 프로그램에서의 오브젝트의 위치는 다른 위치로 바뀌겠지만

local space 내부에서의 오브젝트의 초기 위치는 보통 0, 0, 0으로 고정된다.

 

World Space

오브젝트들이 실제로 위치하는 곳은 결국 원점이 (0, 0, 0)인 World Space가 된다.

모든 오브젝트들은 더 넓은 범위의 공간인 World Space에 배치되며 이는

Local Space에 Model 행렬을 이용한 변환을 통해 이루어진다.

Model 행렬은 오브젝트를 이들이 존재해야 할 위치 / 방향으로 World Space 안에 배치하기 위해

오브젝트를 이동, 스케일, 회전시키는 변환 행렬이다.

예를 들어 변환을 통해 0.5배 만큼 축소된 오브젝트가 world space의 (10, 10, 10) 좌표에 존재한다면

Model 행렬은 오브젝트를 0.5배 스케일하며 10, 10, 10만큼 이동시키는 행렬이 된다.

 

View Space

View Space는 OpenGL에서 일반적으로 카메라를 나타낸다.

View Space는 World Space의 좌표를 카메라의 시점 앞에 있는 좌표로 변환했을 때의 결과이다.

따라서 View Space는 특정 카메라의 관점에서 바라보는 공간이 된다.

장면의 이동과 회전을 조합하여 특정 오브젝트가 카메라의 앞으로 변환된다.

조합된 변환 행렬은 View Matrix에 저장되고 해당 행렬로 인해

World Space 좌표가 View Space 좌표로 변환된다.

 

Clip Space

각 vertex shader 실행의 마지막에 OpenGL은 지정된 범위의 좌표를 받아들이고

이 범위에서 벗어난 모든 좌표는 clipped(자르다) 된다.

clip된 좌표들은 폐기되고 남은 좌표들은 최종적으로 fragment가 되어 화면에 나타난다.

눈에 보이는 모든 좌표들을 -1.0 ~ 1.0 범위 안으로 지정하는 것은 직관적이지 않기 때문에

직관적인 좌표를 지정하고 작업을 수행한 후 이를 다시 OpenGL이 원하는 NDC(-1.0 ~ 1.0 범위)로 변환한다.

 

vertex 좌표를 view에서 clip space로 변환하기 위해서 좌표의 범위를 지정하는 project matrix를 정의한다.

그 다음 projection 행렬은 지정된 범위 안에 있는 좌표들을 NDC로 변환한다.

지정된 범위 밖에 있는 좌표들은 NDC에 매핑되지 않고 clip되어 배제되게 된다.

 

primitive의 일부만 clipping volume에서 벗어났다면(오브젝트의 일부가 clip 당한다면)

OpenGL은 clipping 범위에 맞도록 하나 이상의 삼각형을 재구성한다.

 

projection 행렬이 생성하는 viewing box는 절두체(frustum)라고 불리고 이 절두체 내부에 있는 좌표들은

스크린에 나타나게 된다. 

projection 행렬이 3D 좌표를 2D에 매핑하기 쉬운 NDC로 투영하기 때문에

지정된 범위에서 NDC로 변환하는 전체적인 과정은 projection(투영)이라고도 불린다.

 

모든 vertex들이 clip space로 변환되었다면 perspective division이라고 불리는 마지막 작업이 수행된다.

여기서 위치 벡터의 x, y, z 요소들을 벡터의 w 요소로 나눈다.

perspective division은 4D Clip space 좌표를 3D NDC로 변환하는 것이다.

이 작업은 vertex shader의 실행 마지막에 자동으로 수행된다.

 

이 단계 이후에는 결과 좌표들을 screen 좌표에 매핑(glViewport 함수의 설정을 사용)하고 fragment로 변환한다.

projection 행렬은 view 좌표를 clip 좌표로 변환하기 위해 두 개의 다른 형식을 받는다.

각 형식은 자신만의 고유한 절도체를 가지며 정사영(orthographic) projection 행렬과

원근(perspective) projection 행렬 중에서 하나를 생성한다.

 

 

Orthographic projection

정사영 행렬은 정육면체와 같은 절도체 상자를 정의한다.

해당 절도체 상자의 밖에 있는 vertex들은 clip당하는 clipping 공간을 정의한다.

orthographic projection 행렬을 생성할 때 눈에 보이는 절도체의 너비, 높이, 길이를 정의한다.

orthographic projection 행렬로 변환이 완료된 후에 이 절도체 안에 있는 모든 좌표들은 clip되지 않는다.

이 절도체는 보이게 될 좌표들을 결정하고 너비와 높이, 가까운 평면, 먼 평면으로 이루어진다.

가까운 평면의 앞에 있는 모든 좌표들은 clip되고 먼 평면 뒤에 있는 좌표들도 clip된다.

orthographic 절도체는 각 벡터의 w 요소를 건드리지 않기 때문에

절도체 내부에 있는 모든 좌표들을 NDC로 직접 매핑한다.

여기서 w요소가 1.0일 경우 perspective division은 좌표를 수정하지 않는다.

 

orthograhpic projection 행렬을 생성하기 위해 GLM의 glm::ortho 함수를 사용한다.

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

처음 두 개의 파라미터는 절두체의 왼쪽, 오른쪽 좌표를 지정한다.

세, 네번째 파라미터는 절두체의 아래와 위의 좌표를 지정한다.

이 4개의 지점은 가까운 평면과 먼 평면의 크기를 정의하고

다섯, 여섯번째 파라미터로 가까운 평면과 먼 평면 사이의 거리를 정의한다.

지정된 projection 행렬은 x, y, z범위 값을 가진 모든 좌표들을 NDC로 변환한다.

 

orthographic projection 행렬은 좌표들을 화면의 2D 평면에 똑바로 매핑하지만 실제로 똑바로 투영하는 것은

비현실적인 결과를 생성한다.

이는 원근감이 고려되지 않았기 때문이다. 이는 perspective projection 행렬이 해결해 줄 문제이다.

 

Perspective Projection

실제 세상을 그래픽으로 구현할 경우 멀리 있는 오브젝트는 작아져야 한다.

이 효과는 perspective(원근감)이라고 불리며 다음 이미지를 통해 확인할 수 있다.

위와 같은 효과는 perspective projection 행렬을 사용하여 만들어 낼 수 있다.

해당 행렬은 주어진 절도체를 clip된 공간에 매핑할 뿐 아니라

각  vertex 좌표의 w 값을 조작한다.

시점(카메라)로부터 vertex 좌표가 멀어질수록 w 값이 증가한다.

좌표들이 clip space로 변환되고 나면 모든 vertex는 -w ~ w 범위 안에 있게 된다.

OpenGL은 -1.0 ~ 1.0 범위의 좌표들만 보이도록 하기 때문에

좌표들이 clip space 의 -w ~ w 범위 안에 있으면 perspective division은 clip space 좌표에 적용된다.

vertex 좌표의 각 요소들은 w 값으로 나누어진다.

결과적으로 카메라로부터 멀리 떨어진 vertex에게 작은 vertex 좌표가 주어지게 된다.

이처럼 벡터의 w요소는 perspective projection을 수행하는데 있어서 중요한 역할을 한다.

이후 좌표들은 NDC의 범위 안에 존재하게 된다.

 

perspective projection 행렬은 GLM으로 다음과 같이 생성할 수 있다.

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);

perspective 함수가 하는 일은 눈에 보이는 공간을 정의하는 큰 절도체를 생성하는 것이다.

perpective 절도체는 균일하지 않은 상자 모양으로 시각화 될 수 있다.

이 상자 내부의 좌표들은 clip space에 매핑될 수 있다.

시각화한 이미지는 다음과 같다.

perspective 함수의 첫 번째 파라미터는 fov 값을 지정한다.

fov는 field of view의 줄임말로서 view space가 얼마나 큰지를 설정한다.

현실적인 시점을 위해서 일반적으로 45도로 설정되지만 둠-스타일 결과를 원한다면 더 높은 값으로 설정할 수 있다.

두 번째 파라미터는 viewport의 너비를 높이로 나눔으로써 계산되는 화면 비율을 설정한다.

세 번째, 네 번째 파라미터는 가까운 평면과 먼 평면의 거리를 설정한다.

일반적으로 가까운 평면의 거리는 0.1f로, 먼 평면의 거리는 100.0f로 설정한다.

이 사이에 있으며 절도체 내부에 있는 모든 vertex들은 렌더링 된다.

 

이처럼 orthographic project를 사용할 때 vertex의 각 요소들은 복잡한 perspective division없이 clip sapce에 매핑되며

멀리 떨어진 오브젝트들이 작게 보이지 않는다.(원근법이 적용되지 않는다.)

이러한 이유 때문에 orthographic projection은 주로 원근법에 의해 왜곡된 vertex들을 사용할 필요가 없는

2D 렌더링과 일부 구조적이거나 공학적인 응용 프로그램에서 사용된다.

Blender와 같은 3D 모델링을 위한 응용 프로그램은

오브젝트를 각 축에 대해 정밀하게 그릴 수 있기 때문에 때때로 모델링을 위해 해당 projection을 사용한다.

다음은 Blender에서 두 가지의 projection 방법을 비교한 것이다.

perpective projection에서는 멀리 떨어진 vertex들이 작아진 것을 볼 수 있고

orthographic projection에서는 vertex들이 같은 거리에 있음을 볼 수 있다.

 

모든 것을 한 곳에 넣기

위의 모든 단계에 필요한 각각의 변환 행렬들을 생성한다.

model, view, projection 행렬이 있다. vertex 좌표들은 다음과 같이 clip 좌표로 변환된다.

언제나 그렇듯 행렬 곱셈의 순서는 오른쪽에서 왼쪽이다.

결과 vertex는 vertex shader의 gl_Position에 할당되어야 한다.

그 다음 OpenGL은 perspective division과 clipping을 자동으로 수행하게 된다.

 

Going 3D

3D 좌표를 2D 좌표로 변환하는 방법을 알아보았다.

이제 오브젝트를 2D 평면이 아니라 현실 3D 오브젝트처럼 렌더링할 수 있다.

 

3D로 그리기 위해서는 우선 model 행렬을 생성해야 한다.

model 행렬은 vertex들을 world space로 변환하기 위한 이동, 스케일, 회전과 같은 변환들로 이루어져 있다.

평면을 x축을 중심으로 조금 회전시켜 변환해보도록 한다.

이렇게 하면 오브젝트가 바닥에 누워있는 것처럼 보이게 된다.

해당 model 행렬은 다음과 같다.

glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); 

vertex 좌표를 이 model 행렬과 곱하면 world 좌표로 변환할 수 있다.

 

다음은 view 행렬을 생성해야 한다.

오브젝트가 좀 더 뒤로가서 보이도록 한다.

scene 주위를 이동하기 위해 다음과 같은 생각을 할 수 있다.

- 카메라를 뒤로 이동하는 것은 모든 scene을 앞으로 이동하는 것과 같다.

이것이 정확히 view 행렬이 하는 일이다.

scene 전체를 카메라를 이동시켜야 할 방향과 반대로 움직인다.

카메라가 뒤로 움직여야 하고, OpenGL은 오른손좌표계 이기 때문에 z축의 양의 방향으로 이동해야 한다.

따라서 scene 전체를 z축의 음의 방향으로 이동시킴으로써 이를 수행할 수 있다.

 

다음 강좌에서 scene에서 이동하는 방법을 더 상세히 다룰 것이다. 지금의 view 행렬은 다음과 같다.

glm::mat4 view;
// 우리가 움직이고 싶은 방향과 반대의 방향으로 scene을 이동시키고 있다는 것을 알아두세요.
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); 

 

마지막으로 정의해야 할 행렬은 projection 행렬이다.

perspective projection을 사용할 것이므로 다음과 같이 선언한다.

glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);

 

이제 shader에 전달할 변환 행렬들을 생성하였다.

먼저 vertex shader에 변환 행렬을 uniform으로 선언하고 그들을 vertex 좌표에 곱해준다.

#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // 곱셈을 오른쪽에서 왼쪽으로 읽는다는 것을 알아두세요.
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ...
}

 

이제 shader에 행렬을 전달해야 한다.(일반적으로 변환 행렬은 자주 변하기 때문에 각 렌더링 루프가 돌 때마다 수행된다.)

int modelLoc = glGetUniformLocation(ourShader.ID, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
... // same for View Matrix and Projection Matrix

 

이제 vertex 좌표들은 model, view, projection 행렬에 의해 변환되어졌다.

최종 오브젝트는 바닥을 향해 뒤쪽으로 기울어져 있으며.

카메라에서 약간 떨어져 있고, 원근법을 사용하여 출력되었다.

결과는 다음과 같다.

 

 

좀 더 3D 답게

이제 2D 평면을 3D 정육면체로 확장해보자.

정육면체를 렌더링하기 위해서는 총 36개(6개의 면 * 2개의 삼각형 * 3개의 vertex)의 vertex가 필요하다.

36개의 vertex는 다음과 같다.(길다)

float vertices[] = {
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
};

 

시간이 지남에 따라 정육면체를 회전시켜 보자. (게임 루프 안에 넣어야 한다.)

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));  

그 다음 glDrawArrays함수를 사용해 정육면체를 그린다.

이번에는 36개의 vertex를 사용한다.

glDrawArrays(GL_TRIANGLES, 0, 36);

다음과 같은 결과를 얻을 수 있다.

 

하지만 아직 뭔가 이상하다.

정육면체의 일부 면들이 정육면체의 다른 면들 위로 그려진다.

이는 OpenGL이 오브젝트를 삼각형 단위로 그리기 때문에 발생하는 것이다.

다른 픽셀이 해당 위치에 이미 그려져 있음에도 불구하고 그 위에 픽셀을 그리는 것이다.

이 때문에 일부 삼각형들은 겹치지 않았음에도 다른 삼각형들 위에 그려져 겹치게 된다.

 

OpenGL은 해당 깊이 정보를 z-buffer라고 불리는 버퍼에 저장한다.

이 버퍼는 OpenGL이 픽셀 위에 그릴 것인지 안 그릴 것인지를 결정하게 해준다.

z-buffer를 사용하여 OpenGL이 depth-testing을 할 수 있도록 구성할 수 있다.

 

 

Z-버퍼

OpenGL은 z-buffer에 모든 깊이 정보들을 저장한다. 이 버퍼는 깊이 버퍼라고도 부른다.

GLFW는 이와 같은 버퍼를 자동으로 생성한다.

깊이는 각 fragment를 저장하고 fragment가 출력되길 원할 때마다 OpenGL은

해당 깊이 값과 z-buffer를 비교하고 만약 현재 fragment가 다른 fragment의 뒤에 있다면 폐기, 앞에 있다면 덮어 씌운다.

이 과정을 depth testing이라 부르고 OpenGL에 의해 자동적으로 수행된다.

 

하지만 OpenGL이 실제로 depth testing을 수행하도록 하려면 먼저 OpenGL에게

depth testing을 사용할 것이라는 것을 알려주어야 한다. 이는 기본적으로 사용하지 않도록 설정되어 있다.

glEnable 함수를 사용하여 depth testing을 가능하게 할 수 있다.

glEnable함수와 glDisable함수는 OpenGL에서 특정 기능에 대해 사용가능/ 사용 불가를 설정할 수 있다.

 

다음은 GL_DEPTH_TEST를 사용 가능 상태로 만들어 depth testing이 수행되도록 하는 코드이다.

glEnable(GL_DEPTH_TEST);  

 

이제 깊이 버퍼를 사용하고 있기 때문에 루프가 돌 때마다 깊이 버퍼를 비워주어야 한다.

그렇지 않으면 이전 프레임의 깊이 정보가 그대로 버퍼에 남아있게 된다.

컬러 버퍼를 지우는 것과 마찬가지로 glClear함수에 DEPTH_BUFFER_BIT 비트를 지정함으로써 깊이 버퍼를 지울 수 있다.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 

결과는 다음과 같다.

 

더 많은 정육면체들

만약 화면에 10개의 정육면체를 출력하기로 한다면

각 정육면체는 똑같이 생겼지만 world space에서 위치와 회전 값이 다를 것이다.

정육면체의 그래픽 레이아웃은 이미 정의되어 있으므로 더 많은 오브젝트를 렌더링할 때

버퍼나 attribute 배열들을 수정할 필요가 없다.

각 오브젝트에 대해 수정해야 할 부분은 world로 변환할 model행렬 뿐이다.

 

각 정육면체에 대한 이동 벡터를 정의하여 world space에서의 그들의 위치를 지정한다.

glm::vec3 배열에 10 개의 정육면체의 위치를 정의할 것이다.

glm::vec3 cubePositions[] = {
  glm::vec3( 0.0f,  0.0f,  0.0f), 
  glm::vec3( 2.0f,  5.0f, -15.0f), 
  glm::vec3(-1.5f, -2.2f, -2.5f),  
  glm::vec3(-3.8f, -2.0f, -12.3f),  
  glm::vec3( 2.4f, -0.4f, -3.5f),  
  glm::vec3(-1.7f,  3.0f, -7.5f),  
  glm::vec3( 1.3f, -2.0f, -2.5f),  
  glm::vec3( 1.5f,  2.0f, -2.5f), 
  glm::vec3( 1.5f,  0.2f, -1.5f), 
  glm::vec3(-1.3f,  1.0f, -1.5f)  
};

 

이제 게임 루프 안에서 glDrawArrays 함수를 10번 호출해야 한다.

그리고 렌더링 할 때마다 다른 model 행렬을 vertex shader에게 보내야 한다.

게임 루프 안에서 생성될 오브젝트를 다른 model 행렬을 사용하여 10번 렌더링하는 작은 반복문을 만든다.

또한 각 컨테이너에 약간의 회전도 추가한다.

 

실행 결과는 다음과 같다.

 

 

 

'공부한거 > OpenGL' 카테고리의 다른 글

OpenGL Lighting 3-1 Color  (0) 2020.10.11
OpenGL Getting Started 2-9 Camera  (0) 2020.09.29
OpenGL Getting Started 2-7 Translation  (0) 2020.09.27
OpenGL Getting Started 2-6 Texture  (0) 2020.09.24
OpenGL Getting Started 2-5 Shaders  (0) 2020.09.24