본문 바로가기
<LIBRARY>/OPENGL

4. 삼각형 움직이기

by CodeGrimie 2021. 1. 17.

이번엔 아주 간단한 애니메이션을 적용시켜본다.

쉐이더 역시 하나의 프로그램이기 때문에 외부에서 변수를 받아서 변환할 수 있는 것을 연습할 수 있다.

삼각형을 움직이는데 필요한 변수 선언

▼ 코드

//---------------------------------------------
GLuint VAO, VBO, shader, uniformXMove;

// 방향값(왼쪽, 오른쪽)
bool direction = true;
// 삼각형의 차이값
float triOffset = 0.0f;
// 삼각형의 최대 차이값
float triMaxOffset = 1.0f;
// 삼각형의 변화값
float triIncrement = 0.01f;
//---------------------------------------------

쉐이더에 특정 값을 던져 줌으로써 실시간으로 변환시켜 볼 수 있다.

 

삼각형이 스스로 좌우로 움직이기 위해서 방향 값(direction)을 주고 있다.

오른쪽이냐 아니냐로 구분할 수 있기 때문에 bool로 처리할 수 있다.

 

차이 값(Offset)이 등장하는데 게임에서 자주 사용되는 용어다.

기준값에서 얼마나 변동이 있었는지를 뜻하는데 아래의 그림을 보면 쉽게 이해할 수 있다.

 

▼ 얼마나 움직였는지 수치화한 것

그래서 triOffset은 삼각형이 기준값에서 얼마나 차이 나게 움직였는지를 저장하고 triMaxOffset은 이 삼각형이 최대로 움직일 수 있는 최대 차이 값을 저장한다.

triIncrement는 삼각형이 한번에 얼마나 차이 날지를 저장한다.

보통 triSpeed와 같이 직접적으로 속도로 명시하기도 하지만 이번엔 Offset의 개념에 익숙해지기 위해 증가 값으로 정한다.

 

삼각형을 움직이는 데 필요한 변수는 모두 선언하고 초기화를 완료하였다.

이젠 정점 쉐이더 코드를 수정해보자.

정점 쉐이더 코드 수정

▼ uniform의 등장

// 정점 쉐이더
static const char* vShader = R"(
#version 330

layout (location = 0) in vec3 pos;

// 쉐이더에서 사용할 변수
uniform float xMove;

void main()
{
    // X축에 xMove를 더해준다.
    gl_Position = vec4(pos.x + xMove, pos.y, pos.z, 1.0);
})";

일정화(Uniform)는 쉐이더로 전달되는 읽기 전용 변수다.

일정하다는 의미가 붙는 이유는 CPU에서 GPU로 데이터를 던져줄 때 OpenGL은 모든 값들을 일정한 규격으로 보내기 때문이다.

 

아래의 글을 더 자세히 읽어보면 좋다.

thebookofshaders.com/03/

 

The Book of Shaders

Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders.

thebookofshaders.com

아무튼 우리는 X축(왼쪽, 오른쪽)만 움직일 것이기 때문에 XMove라는 유니폼 변수를 선언한다.

이 변수는 외부에서 입력 받아서 사용하기 때문에 곧바로 우리의 pos.x에 더해준다.

 

이제 외부 입력을 받을 수 있도록 변수를 연동시켜보자.

프로그램의 uniformXMove와 쉐이더의 xMove 연결

▼ 저번에 만든 CompileShader 함수 끝에 추가한다.

void CompileShader()
{
    shader = glCreateProgram();

    if (shader == NULL)
    {
        printf("Error Creating Shader Program!\n");
        return;
    }

    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // 쉐이더 프로그램 연결
    glLinkProgram(shader);
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error Linking Program: '%s'\n", eLog);
        return;
    }

    // 쉐이더 프로그램 검증
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error Validating Program: '%s'\n", eLog);
        return;
    }

    //---------------------------------------------
    // 프로그램의 uniformXMove와 쉐이더의 xMove를 연결한다.
    uniformXMove = glGetUniformLocation(shader, "xMove");
    //---------------------------------------------
}

glGetUniformLocation은 말 그대로 OpenGL에 등록된 Uniform 변수의 위치를 찾아서 연결해준다.

이 코드를 통해서 uniformXMove는 이제 shader에 등록된 XMove와 연동되었다.

이 uniformXMove를 수정하면 XMove 역시 변경된다는 것을 의미한다.

방향에 따른 차이값 설정하기

▼ 아주 간단한 로직이다.

// GLFW가 종료되지 않는 한 계속 도는 순환문
while (glfwWindowShouldClose(mainWindow) == GLFW_FALSE)
{
    // GLFW 이벤트 입력
    glfwPollEvents();

    //---------------------------------------------
    // 방향이 오른쪽인지 왼쪽인지
    if (direction == true)
    {
        triOffset += triIncrement;
    }
    else
    {
        triOffset -= triIncrement;
    }

    // 최대 차이값을 넘기게 되면 방향 전환
    if (abs(triOffset) >= triMaxOffset)
    {
        direction = !direction;
    }
    //---------------------------------------------

    // 연두색 화면 그리기
    glClearColor(0.4f, 0.6f, 0.2f, 1.0f);
    // OpenGL 배경색상 초기화
    glClear(GL_COLOR_BUFFER_BIT);

    // Shader 적용
    glUseProgram(shader);

    // VBO에 있는 데이터 바인딩
    glBindVertexArray(VBO);
    // 데이터를 바탕으로 그리기
    glDrawArrays(GL_TRIANGLES, 0, 3);
    // 데이터 바인딩 해제
    glBindVertexArray(0);

    // Shader 해제
    glUseProgram(0);

    // GLFW 더블 버퍼링
    glfwSwapBuffers(mainWindow);
}

삼각형이 매 프레임마다 이동하기를 원하기 때문에 순환문 안에 작업한다.

방향이 오른쪽일 때는 triOffset에 triIncrement를 더해주고 왼쪽일 때는 반대로 빼준다.

 

그리고 오른쪽이던 왼쪽이던 triOffset이 triMaxOffset보다 크면 방향을 바꿔서 계속 왔다 갔다 하도록 한다.

 

그러나 한가지 문제점이 있다.

triOffset은 float인데 OpenGL이 쉐이더로 던지는 uniformXMove는 GLuint다.

이 문제를 해결하기 위해서 float을 Uniform으로 변환하는 함수를 사용한다.

triOffset을 uniformXMove으로 변환

▼ glUseProgram(shader); 바로 밑에 작성한다.

// GLFW가 종료되지 않는 한 계속 도는 순환문
while (glfwWindowShouldClose(mainWindow) == GLFW_FALSE)
{
    // GLFW 이벤트 입력
    glfwPollEvents();

    //---------------------------------------------
    // 방향이 오른쪽인지 왼쪽인지
    if (direction == true)
    {
        triOffset += triIncrement;
    }
    else
    {
        triOffset -= triIncrement;
    }

    // 최대 차이값을 넘기게 되면 방향 전환
    if (abs(triOffset) >= triMaxOffset)
    {
        direction = !direction;
    }
    //---------------------------------------------

    // 연두색 화면 그리기
    glClearColor(0.4f, 0.6f, 0.2f, 1.0f);
    // OpenGL 배경색상 초기화
    glClear(GL_COLOR_BUFFER_BIT);

    // Shader 적용
    glUseProgram(shader);
    
    //---------------------------------------------
    // triOffset을 uniformXMove으로 변환한다.
    glUniform1f(uniformXMove, triOffset);
    //---------------------------------------------

    // VBO에 있는 데이터 바인딩
    glBindVertexArray(VBO);
    // 데이터를 바탕으로 그리기
    glDrawArrays(GL_TRIANGLES, 0, 3);
    // 데이터 바인딩 해제
    glBindVertexArray(0);

    // Shader 해제
    glUseProgram(0);

    // GLFW 더블 버퍼링
    glfwSwapBuffers(mainWindow);
}

 

쉐이더에서 사용해야하기 때문에 glUseProgram(shader); 바로 밑에 작성한다.

 

glUniform1f 함수는 일반 float 변수를 GLuint형 변수로 변환할 수 있게 해 준다.

순환문을 돌면서 triOffset이 변하면 이 함수를 통해서 uniformXMove에도 변한 값이 적용된다.

그리고 uniformXMove는 쉐이더의 xMove로 전달되고 삼각형은 움직일 것이다.

 

이제 컴파일하고 실행하면 삼각형이 움직일 것이다!

아주 간단하긴 해도 잘 작동하는 하나의 애니메이션을 완성했다.

실행 화면

▼ 컴파일 및 실행 결과

삼각형이 너무 커서 보기에 예쁘지 않다.

정점 좌표를 수정하는 방법도 있지만 정점 쉐이더의 코드를 약간 수정해도 작은 삼각형을 그릴 수 있다.

 

▼ 정점 쉐이더 수정을 통한 작은 삼각형

// 정점 쉐이더
static const char* vShader = R"(
#version 330

layout (location = 0) in vec3 pos;

// 쉐이더에서 사용할 변수
uniform float xMove;

void main()
{
    // X축에 xMove를 더해준다.
    gl_Position = vec4(0.5 * pos.x + xMove, 0.5 * pos.y, pos.z, 1.0);
})";

▼ 정점 쉐이더 수정 후 실행 결과

'<LIBRARY> > OPENGL' 카테고리의 다른 글

6. 삼각형 회전 시키기  (0) 2021.01.25
5. GLM 라이브러리 적용하기  (0) 2021.01.19
3. 삼각형 그리기(2)  (0) 2021.01.17
2. 삼각형 그리기 (1)  (0) 2021.01.14
1. 기본 코드 작성하기  (0) 2021.01.11

댓글