๋ ๋๋ง ์ํคํ ์ฒ๊ฐ ์ ์ฒด ์ฌ์ ๋ถ๋ฌ์ฌ ์ค๋น๊ฐ ๋์์ง๋ง, ์นด๋ฉ๋ผ๊ฐ ๊ณ ์ ๋์ด ์๋ค๋ฉด ํฐ ์๋ฏธ๊ฐ ์์ต๋๋ค. ๋ง์ฐ์ค๋ฅผ ์ฌ์ฉํด ์ฃผ์๋ฅผ ๋๋ฌ๋ณผ ์ ์๋ ์ธํฐ๋ํฐ๋ธ ์นด๋ฉ๋ผ๋ฅผ ๊ตฌ์ฑํด ๋ถ๋ฌ์จ ๋ ๋ฒจ์ ์์ ๋กญ๊ฒ ํ์ํ ์ ์๋๋ก ํด๋ด ์๋ค.
์นด๋ฉ๋ผ๋ ๋ณธ์ง์ ์ผ๋ก ๊ฒ์ํ๋ ์ด ๊ณ์ธต์ ๊ฐ์ฒด์ ๋๋ค. ์ฌ๊ธฐ์๋ 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