Link

๋ Œ๋”๋ง ์•„ํ‚คํ…์ฒ˜๊ฐ€ ์ „์ฒด ์”ฌ์„ ๋ถˆ๋Ÿฌ์˜ฌ ์ค€๋น„๊ฐ€ ๋˜์—ˆ์ง€๋งŒ, ์นด๋ฉ”๋ผ๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ๋‹ค๋ฉด ํฐ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋งˆ์šฐ์Šค๋ฅผ ์‚ฌ์šฉํ•ด ์ฃผ์œ„๋ฅผ ๋‘˜๋Ÿฌ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์นด๋ฉ”๋ผ๋ฅผ ๊ตฌ์„ฑํ•ด ๋ถˆ๋Ÿฌ์˜จ ๋ ˆ๋ฒจ์„ ์ž์œ ๋กญ๊ฒŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ด…์‹œ๋‹ค.

์นด๋ฉ”๋ผ๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ๊ฒŒ์ž„ํ”Œ๋ ˆ์ด ๊ณ„์ธต์˜ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” VulkanEngine์— ์นด๋ฉ”๋ผ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฒ ์ง€๋งŒ, ์‹ค์ œ ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š” ์ž…๋ ฅ ์ฒ˜๋ฆฌ๋‚˜ ๊ฒŒ์ž„ ๋กœ์ง์„ ์—”์ง„ ๋‚ด๋ถ€์—์„œ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ๋Œ€์‹ , ๋ Œ๋”๋ง์— ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋‹ด์€ ์นด๋ฉ”๋ผ ๊ตฌ์กฐ์ฒด๋ฅผ ์ €์žฅํ•˜๊ณ , ๊ฒŒ์ž„ ๋กœ์ง์ด ์—…๋ฐ์ดํŠธ ๋  ๋•Œ ํ•ด๋‹น ํ–‰๋ ฌ๋“ค์„ ๊ฐฑ์‹ ํ•ด ๋ Œ๋”๋ง์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋ฐฉ์‹์ด ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

์นด๋ฉ”๋ผ ๊ตฌ์กฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ camera.h์— ์ถ”๊ฐ€ํ•ด๋ด…์‹œ๋‹ค.

#include <vk_types.h>
#include <SDL_events.h>

class Camera {
public:
    glm::vec3 velocity;
    glm::vec3 position;
    // vertical rotation
    float pitch { 0.f };
    // horizontal rotation
    float yaw { 0.f };

    glm::mat4 getViewMatrix();
    glm::mat4 getRotationMatrix();

    void processSDLEvent(SDL_Event& e);

    void update();
};

ํ–‰๋ ฌ์„ ์ €์žฅํ•ด๋‘์ง€ ์•Š๊ณ , ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ๊ณ„์‚ฐํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ญ์ƒ ํ–‰๋ ฌ์ด ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ผ์น˜ํ•˜๋„๋ก ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

WASD ์ž…๋ ฅ ์ƒํƒœ๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด Velocity ๋ฒกํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ, ์ดํ›„์—๋Š” ๊ฐ€์† ๊ฐœ๋…์„ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. Position์€ ์นด๋ฉ”๋ผ์˜ ์›”๋“œ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์ „์ฒด ํšŒ์ „ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  pitch์™€ raw๋งŒ์œผ๋กœ ํšŒ์ „์„ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์–ธ๋ฆฌ์–ผ ์—”์ง„์˜ ์นด๋ฉ”๋ผ ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•˜๋ฉฐ, FPS ๋กœ์ง์„ ๋ณด๋‹ค ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

ํ•จ์ˆ˜์—์„œ๋Š” Update()๋ฅผ ํ˜ธ์ถœํ•ด velocity์— ๋”ฐ๋ผ position์„ ๊ฐฑ์‹ ํ•˜๊ณ , processSDLEvent๋Š” ์ž…๋ ฅ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋กœ์ง์œผ๋กœ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์ œ camera.cpp์— ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

#include <camera.h>
#include <glm/gtx/transform.hpp>
#include <glm/gtx/quaternion.hpp>

void Camera::update()
{
    glm::mat4 cameraRotation = getRotationMatrix();
    position += glm::vec3(cameraRotation * glm::vec4(velocity * 0.5f, 0.f));
}

void Camera::processSDLEvent(SDL_Event& e)
{
    if (e.type == SDL_KEYDOWN) {
        if (e.key.keysym.sym == SDLK_w) { velocity.z = -1; }
        if (e.key.keysym.sym == SDLK_s) { velocity.z = 1; }
        if (e.key.keysym.sym == SDLK_a) { velocity.x = -1; }
        if (e.key.keysym.sym == SDLK_d) { velocity.x = 1; }
    }

    if (e.type == SDL_KEYUP) {
        if (e.key.keysym.sym == SDLK_w) { velocity.z = 0; }
        if (e.key.keysym.sym == SDLK_s) { velocity.z = 0; }
        if (e.key.keysym.sym == SDLK_a) { velocity.x = 0; }
        if (e.key.keysym.sym == SDLK_d) { velocity.x = 0; }
    }

    if (e.type == SDL_MOUSEMOTION) {
        yaw += (float)e.motion.xrel / 200.f;
        pitch -= (float)e.motion.yrel / 200.f;
    }
}

์œ„์น˜๋ฅผ ๊ฐฑ์‹ ํ•  ๋•Œ ํšŒ์ „ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š”, Velocity๊ฐ€ ์นด๋ฉ”๋ผ ๊ธฐ์ค€ ์ขŒํ‘œ๊ณ„์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Wํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด โ€˜์ „๋ฐฉโ€™์œผ๋กœ ์ด๋™ํ•˜๊ฒŒ ๋˜๋ฉฐ, ์—ฌ๊ธฐ์„œ โ€˜์ „๋ฐฉโ€™์ด ์˜๋ฏธํ•˜๋Š” ๋ฐฉํ–ฅ์€ ์นด๋ฉ”๋ผ์˜ ํšŒ์ „์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

์ž…๋ ฅ ๋กœ์ง์—์„œ๋Š” SDL์ด ํ‚ค๋ณด๋“œ ์ž…๋ ฅ(๋ˆ„๋ฆ„/๋—Œ)๊ณผ ๋งˆ์šฐ์Šค ์›€์ง์ž„์— ๋Œ€ํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•ด์ค๋‹ˆ๋‹ค. ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ํ•ด๋‹น ๋ฐฉํ–ฅ์— ๋งž๋Š” velocity๋ฅผ ์„ค์ •ํ•˜๊ณ , ํ‚ค๋ฅผ ๋–ผ๋ฉด velocity๋ฅผ 0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ด ๋ฐฉ์‹์€ ์ž…๋ ฅ์„ ์™„์ „ํžˆ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด W์™€ S๋ฅผ ๋™์‹œ์— ๋ˆ„๋ฅธ ๋’ค ํ•˜๋‚˜๋ฅผ ๋–ผ๋ฉด, ์›€์ง์ž„์ด ๋ฉˆ์ถ”๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด ์•ฝ๊ฐ„ ์–ด์ƒ‰ํ•˜๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ๊ฐœ์„ ํ•˜๋Š” ๊ฒƒ์€ ๋…์ž์—๊ฒŒ ๋งก๊ธฐ๊ฒ ์Šต๋‹ˆ๋‹ค.

๋งˆ์šฐ์Šค๊ฐ€ ์›€์ง์ด๋ฉด ์ˆ˜ํ‰ ๋ฐ ์ˆ˜์ง ์ด๋™๋Ÿ‰์„ pitch์™€ yaw ๊ฐ’์— ๋ˆ„์ ์‹œ์ผœ ํšŒ์ „์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ด ์ฝ”๋“œ์—์„œ์˜ ์›€์ง์ž„์€ ํ”„๋ ˆ์ž„์— ์˜ํ–ฅ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์—”์ง„์˜ ์‹คํ–‰ ์†๋„๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๊ตฌํ˜„์„ ๋‹จ์ˆœํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์„ ํƒ์ด๋ฉฐ, ์ด๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ํ”„๋ ˆ์ž„ ๊ฐ„ ์‹œ๊ฐ„(deltaTime)์„ update()์— ์ „๋‹ฌํ•˜๊ณ , velocity์— ํ•ด๋‹น ๊ฐ’์„ ๊ณฑํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์Šค์™‘์ฒด์ธ ์„ค์ •์œผ๋กœ ์ธํ•ด ๋ชจ๋‹ˆํ„ฐ ์ฃผ์‚ฌ์œจ์— ๋งž์ถฐ FPS๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ์œผ๋ฉฐ, ์—”์ง„์„ ๋А๋ฆฌ๊ฒŒ ํ•  ์ •๋„๋กœ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ–‰๋ ฌ ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

glm::mat4 Camera::getViewMatrix()
{
    // to create a correct model view, we need to move the world in opposite
    // direction to the camera
    //  so we will create the camera model matrix and invert
    glm::mat4 cameraTranslation = glm::translate(glm::mat4(1.f), position);
    glm::mat4 cameraRotation = getRotationMatrix();
    return glm::inverse(cameraTranslation * cameraRotation);
}

glm::mat4 Camera::getRotationMatrix()
{
    // fairly typical FPS style camera. we join the pitch and yaw rotations into
    // the final rotation matrix

    glm::quat pitchRotation = glm::angleAxis(pitch, glm::vec3 { 1.f, 0.f, 0.f });
    glm::quat yawRotation = glm::angleAxis(yaw, glm::vec3 { 0.f, -1.f, 0.f });

    return glm::toMat4(yawRotation) * glm::toMat4(pitchRotation);
}

ํšŒ์ „ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ๋‘ ๊ฐœ์˜ ์‚ฌ์›์ˆ˜๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” ์ˆ˜ํ‰ ํšŒ์ „, ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ์ˆ˜์ง ํšŒ์ „์šฉ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด pitch์™€ yaw ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ด๋ฅผ ์กฐํ•ฉํ•ด ์›ํ•˜๋Š” ํšŒ์ „ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

๋ทฐ ํ–‰๋ ฌ์€ ํšŒ์ „ ํ–‰๋ ฌ๊ณผ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ณ€ํ™˜ ํ–‰๋ ฌ์„ ๊ฒฐํ•ฉํ•˜์—ฌ ์นด๋ฉ”๋ผ์˜ ์ตœ์ข… ๋ณ€ํ™˜ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„, ์ด ํ–‰๋ ฌ์„ ์—ญํ–‰๋ ฌ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์‹ค์ œ๋กœ ์นด๋ฉ”๋ผ๊ฐ€ ์›€์ง์ด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์…ฐ์ด๋”์—์„œ๋Š” ์นด๋ฉ”๋ผ์˜ ์›€์ง์ž„๊ณผ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ์›”๋“œ๋ฅผ ์ด๋™์‹œํ‚ค๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์‹ค์ œ ์นด๋ฉ”๋ผ์˜ ์ด๋™์€ ์›”๋“œ ์ขŒํ‘œ๊ณ„๋ฅผ ๋ฐ˜๋Œ€๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋ชจ๋“  ๊ตฌํ˜„์ด ๋๋‚ฌ์œผ๋‹ˆ, VulkanEngine ํด๋ž˜์Šค์— ์—ฐ๊ฒฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์นด๋ฉ”๋ผ๋ฅผ ํด๋ž˜์Šค ๋ฉค๋ฒ„๋กœ ์ถ”๊ฐ€ํ•ด๋ด…์‹œ๋‹ค.

#include <camera.h>

class VulkanEngine{

     Camera mainCamera;
}

์ด์ œ ์ด๋ฅผ ๋ Œ๋”๋Ÿฌ์— ์—ฐ๊ฒฐํ•ด๋ด…์‹œ๋‹ค. ๋จผ์ € run()ํ•จ์ˆ˜๋กœ ์ด๋™ํ•˜์—ฌ SDL ์ด๋ฒคํŠธ์™€ ์—ฐ๊ฒฐํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

 // Handle events on queue
 while (SDL_PollEvent(&e) != 0) {
     // close the window when user alt-f4s or clicks the X button
     if (e.type == SDL_QUIT)
         bQuit = true;

     mainCamera.processSDLEvent(e);
     ImGui_ImplSDL2_ProcessEvent(&e);
 }

๋‹ค์Œ์€ update_scene() ํ•จ์ˆ˜์—์„œ ์นด๋ฉ”๋ผ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์นด๋ฉ”๋ผ์˜ ํ–‰๋ ฌ์„ ๋ Œ๋”๋Ÿฌ์— sceneData ๊ตฌ์กฐ์ฒด์— ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

void VulkanEngine::update_scene()
{
    mainCamera.update();

    glm::mat4 view = mainCamera.getViewMatrix();

    // camera projection
    glm::mat4 projection = glm::perspective(glm::radians(70.f), (float)_windowExtent.width / (float)_windowExtent.height, 10000.f, 0.1f);

    // invert the Y direction on projection matrix so that we are more similar
    // to opengl and gltf axis
    projection[1][1] *= -1;

    sceneData.view = view;
    sceneData.proj = projection;
    sceneData.viewproj = projection * view;
}

init()ํ•จ์ˆ˜์˜ ๋งˆ์ง€๋ง‰์—๋Š” ์นด๋ฉ”๋ผ ์ดˆ๊ธฐํ™” ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์นด๋ฉ”๋ผ๊ฐ€ ์›์ ์„ ๋ฐ”๋ผ๋ณด๋„๋ก ์„ค์ •๋˜๋ฏ€๋กœ, ์ขŒํ‘œ (0,0,0)์— ์žˆ๋Š” ๋ฉ”์‹œ๊ฐ€ ํ™”๋ฉด์— ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

mainCamera.velocity = glm::vec3(0.f);
	mainCamera.position = glm::vec3(0, 0, 5);

mainCamera.pitch = 0;
mainCamera.yaw = 0;

์ด๊ฒƒ์œผ๋กœ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์ธ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์นด๋ฉ”๋ผ๊ฐ€ ์ค€๋น„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ง์ ‘ ์‚ฌ์šฉํ•ด๋ณด๋ฉฐ ์ฃผ์œ„๋ฅผ ๋‘˜๋Ÿฌ๋ณด์„ธ์š”. WASDํ‚ค๋กœ ์นด๋ฉ”๋ผ๋ฅผ ์ด๋™ํ•˜๊ณ  ๋งˆ์šฐ์Šค๋กœ ์‹œ์•ผ๋ฅผ ์›€์ง์ด๋ฉด, ์ด์ „์— ๋ฐฐ์น˜ํ•ด ๋‘์—ˆ๋˜ ์›”๋“œ ๋‚ด ๋ฉ”์‹œ๋“ค์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Next: GLTF Scene Nodes