Showing

[Open GL] 2D삼각형에 이동, 회전 애니메이션 적용 및 원점 리스폰 본문

Open GL

[Open GL] 2D삼각형에 이동, 회전 애니메이션 적용 및 원점 리스폰

RabbitCode 2023. 12. 2. 08:53

https://github.com/JoeyDeVries/LearnOpenGL/tree/master/src/1.getting_started/3.3.shaders_class

위의 코드를 변경하여, 삼각형에 애니메이션을 주도록 한다.

 

 

움직이는 삼각형(only c, not using glm)

기존 버텍스 쉐이더에 유니폼 벡터를 추가하고,

update 함수 내에서 유니폼 벡터의 값을 계속 갱신해주면 물체의 위치가 계속 움직이는 애니메이팅 효과를 줄 수 있다.

 

 

step은 움직이는 정도,

moveCur은 movement vector의 정의

const float step = 0.0001f;
GLfloat moveCur[] = {
   0.0f, 0.0f, 0.0f, 0.0f
};

 

아래 함수는 삼각형이 점점 오른쪽으로 갈 수 있도록 uMove.x(유니폼 변수가 될 uMove)이 될 moveCur[0]에 step을 더해준 것이다.

void updateFunc(void) {
    moveCur[0] += step;
}

 

버텍스 쉐이더 변경

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;
uniform vec4 uMove;

void main()
{
    gl_Position = vec4(aPos, 1.0) + uMove;
    ourColor = aColor;
}

 

 

이제 uMove의 값을 MoveCur 값으로 설정해줄 것이다.

 

glUniform4fv와 glUniform4f는 OpenGL에서 Uniform 변수를 설정하는 함수들인데, 인자 규칙만 지킨다면 둘 중 어떤 것을 사용하여도 상관없다.

  1. glUniform4fv:
    • 타입: glUniform4fv는 "4D 벡터를 설정"
    • 사용 예제:
    • GLfloat color[] = { 1.0f, 0.0f, 0.0f, 1.0f }; // 4D 벡터 glUniform4fv(location, 1, color);
    • location은 Uniform 변수의 위치
    • 1은 설정할 벡터의 개수
    • color는 4D 벡터의 데이터
  2. glUniform4f:
    • 타입: glUniform4f는 "4개의 개별 값을 설정"하는 함수
    • 사용 예제:
      glUniform4f(location, 1.0f, 0.0f, 0.0f, 1.0f);
    • location은 Uniform 변수의 위치

이 함수는 x, y, z, w 값을 개별적으로 받아들여 Uniform 변수에 설정

  • glUniform4fv를 사용할 때는 배열 형태로 데이터를 전달하며, glUniform4f는 개별적인 값을 나열하여 전달
  • glUniform4fv는 여러 벡터를 한 번에 설정할 수 있도록 유용
  • 데이터가 이미 배열 형태로 존재하는 경우에는 glUniform4fv를 사용하는 것이 효율적일 수 있다.
  • 선택은 주로 개발자의 편의나 코드 가독성 등에 따라 달라질 수 있다.
        GLuint locMove = glGetUniformLocation(ourShader.ID, "uMove");
        glUniform4fv(locMove, 1, moveCur);
        updateFunc();
        GLuint locMove = glGetUniformLocation(ourShader.ID, "uMove");
        glUniform4f(locMove, moveCur[0],moveCur[1], moveCur[2], moveCur[3]);
        updateFunc();

결과

본래 위치로 Respawn

위의 코드를 아래와 같이 수정하기만 하면 된다.

GLfloat moveOrigin[] = {
   0.0f, 0.0f, 0.0f, 0.0f
};

GLfloat moveCur[] = {
   0.0f, 0.0f, 0.0f, 0.0f
};

void updateFunc(void) {
    moveCur[0] += step;
    if (moveCur[0] > 1.6f) {
        memcpy(moveCur, moveOrigin, sizeof(moveOrigin));
    }
}

원점 좌표를 따로 저장해두고, 좌표 이동이 특정 기준을 넘어가면 memcpy를 통해 원점 좌표를 움직였던 현재 좌표에 다시 덮어씌워준다.

 

키 입력에 따라 바로 원점으로 리스폰시키는 것도 가능하다.

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS)
        memcpy(moveCur, moveOrigin, sizeof(moveOrigin));
}

 

 

움직이는 삼각형(c++, using glm)

위의 코드를 glm 헤더를 가져와서 c++ glm 스타일로 바꾸면 아래 코드와 같다.

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <learnopengl/shader_s.h>

#include <iostream>

const float step = 0.0001f;


// c style
//GLfloat moveOrigin[] = {
//   0.0f, 0.0f, 0.0f, 0.0f
//};
glm::vec4 moveOrigin = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);

// glm c++ style
glm::vec4 moveCur = glm::vec4(0.0f , 0.0f, 0.0f, 0.0f);

//GLfloat moveCur[] = {
//   0.0f, 0.0f, 0.0f, 0.0f
//};

void updateFunc(void) {
    // before using glm
    //moveCur[0] += step;
    moveCur.x += step;
    if (moveCur[0] > 1.6f) {
        // c style
        //memcpy(moveCur, moveOrigin, sizeof(moveOrigin));
        moveCur = moveOrigin;
    }
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // build and compile our shader program
    // ------------------------------------
    Shader ourShader("3.3.shader.vs", "3.3.shader.fs"); // you can name your shader files however you like

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = {
        // positions         // colors
         0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  // bottom right
        -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  // bottom left
         0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f   // top 
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    // glBindVertexArray(0);


    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        GLuint locMove = glGetUniformLocation(ourShader.ID, "uMove");
        // c style
        //glUniform4fv(locMove, 1, moveCur);
        // glUniform4f(locMove, moveCur[0],moveCur[1], moveCur[2], moveCur[3]);
        
        glUniform4fv(locMove, 1, glm::value_ptr(moveCur)); // value_ptr: returns a pointer to the memory layout of any GLM vector or matrix
        //glUniform4f(locMove, moveCur.x, moveCur.y, moveCur.z, moveCur.w);
        updateFunc();

        // render the triangle
        ourShader.use();
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
        //memcpy(moveCur, moveOrigin, sizeof(moveOrigin));
        moveCur = moveOrigin;
    }
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

 

회전 삼각형

https://gaussian37.github.io/math-la-rotation_matrix/

 

회전 변환 행렬 (2D, 3D)

gaussian37's blog

gaussian37.github.io

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;
uniform vec4 uMove;
uniform float uTheta;

void main()
{
    gl_Position.x = aPos.x * cos(uTheta) - aPos.y * sin(uTheta);
    gl_Position.y = aPos.x * sin(uTheta) + aPos.y * cos(uTheta);
    gl_Position.zw = aPos.zw;
    ourColor = aColor;
}
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <learnopengl/shader_s.h>

#include <iostream>

const float step = 0.001f;

float theta = 0.0f;
const float theta_step = 0.001f;
// c style
//GLfloat moveOrigin[] = {
//   0.0f, 0.0f, 0.0f, 0.0f
//};
glm::vec4 moveOrigin = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);

// glm c++ style
glm::vec4 moveCur = glm::vec4(0.0f , 0.0f, 0.0f, 0.0f);

//GLfloat moveCur[] = {
//   0.0f, 0.0f, 0.0f, 0.0f
//};

void updateFunc(void) {
    // before using glm
    //moveCur[0] += step;
    moveCur.x += step;
    if (moveCur[0] > 1.6f) {
        // c style
        //memcpy(moveCur, moveOrigin, sizeof(moveOrigin));
        moveCur = moveOrigin;
    }
}

void updateRot(void) {
    theta += theta_step;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // build and compile our shader program
    // ------------------------------------
    Shader ourShader("3.3.shader.vs", "3.3.shader.fs"); // you can name your shader files however you like

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = {
        // positions         // colors
         0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  // bottom right
        -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  // bottom left
         0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f   // top 
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    // glBindVertexArray(0);


    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        GLuint locMove = glGetUniformLocation(ourShader.ID, "uMove");
        // c style
        //glUniform4fv(locMove, 1, moveCur);
        // glUniform4f(locMove, moveCur[0],moveCur[1], moveCur[2], moveCur[3]);
        
        glUniform4fv(locMove, 1, glm::value_ptr(moveCur)); // value_ptr: returns a pointer to the memory layout of any GLM vector or matrix
        //glUniform4f(locMove, moveCur.x, moveCur.y, moveCur.z, moveCur.w);
        
        GLuint locRot = glGetUniformLocation(ourShader.ID, "uTheta");
        glUniform1f(locRot, theta);
        updateFunc();
        updateRot();
        // render the triangle
        ourShader.use();
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
        //memcpy(moveCur, moveOrigin, sizeof(moveOrigin));
        moveCur = moveOrigin;
    }

    if (glfwGetKey(window, GLFW_KEY_O) == GLFW_PRESS) {
        theta = 0.0f;
    }
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}