文章目录
先叨叨
在实现深度测试后,已经可以实现简单的3D渲染了。为了今后能更好的观察3D效果,所以需要在渲染时动态变换视角。本篇就介绍如何动态变换视角。
代码信息
- repository: https://gitee.com/J8_series/easy-car-ui
- tag:24-MouseControl
- url: https://gitee.com/J8_series/easy-car-ui/tree/24-MouseControl
具体思路
glm::lookAt
可以生成视角矩阵,因此主要思路就是在每帧渲染之前通过这个方法生成视角矩阵。
glm::lookAt
的第一个参数是视角的位置,第二个参数时视角盯着的点,第三个参数是视角的正上方向量。
我们定义一个Camera类,该类有三个属m_posistion
、m_face
、m_up
,分别代表Camera的位置、朝向、正上方。
定义了Camera类之后就可以这样使用glm::lookAt
。
mvp.view = glm::lookAt(m_camera.m_posistion, m_camera.m_posistion + m_camera.m_face, m_camera.m_up);
接下来就是根据用户输入,更新m_posistion
、m_face
、m_up
即可。
我希望向第一视角游戏一样通过 W、S、A、D 控制视角的前后左右。按住鼠标左键可以控制视角朝向, 朝向由Yaw和Pitch角定义,Yaw的值由鼠标X值变化量得到,Pitch角由鼠标Y值的变化量得到 。
关键代码
SDL_AppEvent
这个方法是SDL接受输入的方法,在这个方法里我们会Camera的状态。
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
}
if (event->type == SDL_EVENT_KEY_DOWN)
{
if (event->key.key == SDLK_W)
{
VulkanRenderer::GetInstance().m_camera.m_isForward = true;
}
else if (event->key.key == SDLK_S)
{
VulkanRenderer::GetInstance().m_camera.m_isRetreated = true;
}
else if (event->key.key == SDLK_A)
{
VulkanRenderer::GetInstance().m_camera.m_isLeftwards = true;
}
else if (event->key.key == SDLK_D)
{
VulkanRenderer::GetInstance().m_camera.m_isRightwards = true;
}
}
if (event->type == SDL_EVENT_KEY_UP)
{
if (event->key.key == SDLK_W)
{
VulkanRenderer::GetInstance().m_camera.m_isForward = false;
}
else if (event->key.key == SDLK_S)
{
VulkanRenderer::GetInstance().m_camera.m_isRetreated = false;
}
else if (event->key.key == SDLK_A)
{
VulkanRenderer::GetInstance().m_camera.m_isLeftwards = false;
}
else if (event->key.key == SDLK_D)
{
VulkanRenderer::GetInstance().m_camera.m_isRightwards = false;
}
}
if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
{
if (event->button.button == SDL_BUTTON_LEFT)
{
isMouseLeftButtonDown = true;
lastX = event->motion.x;
lastY = event->motion.y;
deltaYaw = 0;
deltaPitch = 0;
}
}
if (event->type == SDL_EVENT_MOUSE_BUTTON_UP)
{
if (event->button.button == SDL_BUTTON_LEFT)
{
isMouseLeftButtonDown = false;
VulkanRenderer::GetInstance().m_camera.CommitFace(deltaYaw, deltaPitch);
}
}
if (event->type == SDL_EVENT_MOUSE_MOTION)
{
if (true == isMouseLeftButtonDown)
{
offsetX = event->motion.x - lastX;
offsetY = lastY - event->motion.y;
deltaYaw = offsetX * 0.1;
deltaPitch = offsetY * 0.1;
VulkanRenderer::GetInstance().m_camera.UpdateFace(deltaYaw, deltaPitch);
}
}
return SDL_APP_CONTINUE; /* carry on with the program! */
}
Camera::UpdatePositon
该方法根据Camera的状态更新Camera的位置。
void Camera::UpdatePositon(float deltaSeconds)
{
if (true == m_isForward)
{
m_posistion += m_face * m_speed;
}
if (true == m_isRetreated)
{
m_posistion -= m_face * m_speed;
}
if (true == m_isLeftwards)
{
m_posistion -= glm::normalize(glm::cross(m_face, m_up)) * m_speed;
}
if (true == m_isRightwards)
{
m_posistion += glm::normalize(glm::cross(m_face, m_up)) * m_speed;
}
}
Camera::UpdateFace
该方法根据deltaYaw和deltaPitch 更新Camera的m_face
void Camera::UpdateFace(float deltaYaw, float deltaPitch)
{
m_deltaYaw = deltaYaw;
m_deltaPitch = deltaPitch;
float tmpPitch = m_pitch + m_deltaPitch;
if (tmpPitch > 89.0f)
{
tmpPitch = 89.0f;
}
if (tmpPitch < -89.0f)
{
tmpPitch = -89.0f;
}
glm::vec3 direction {};
direction.x = cos(glm::radians(tmpPitch)) * cos(glm::radians(m_yaw + m_deltaYaw));
direction.y = sin(glm::radians(tmpPitch));
direction.z = cos(glm::radians(tmpPitch)) * sin(glm::radians(m_yaw + m_deltaYaw));
m_face = glm::normalize(direction);
}
VulkanRenderer::UpdateUniformBuffer
在该方法中调用glm::lookAt
更新视角矩阵
void VulkanRenderer::UpdateUniformBuffer(float deltaSeconds)
{
static auto startTime = std::chrono::high_resolution_clock::now();
auto currentTime = std::chrono::high_resolution_clock::now();
float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
m_camera.UpdatePositon(deltaSeconds);
ModelViewProj mvp{};
mvp.model = glm::mat4(1.0f);
mvp.view = glm::lookAt(m_camera.m_posistion, m_camera.m_posistion + m_camera.m_face, m_camera.m_up);
mvp.proj = glm::perspective(glm::radians(45.0f), m_width / (float) m_height, 0.1f, 10.0f);
memcpy(m_uniformBufferMapped, &mvp, sizeof(mvp));
}
运行效果
我不会录制gif格式图片,不过现在已经可以用鼠标和键盘控制视角了