接上一章:OpenGL基础14:摄像机

现在来尝试通过输入控制摄像机

一、键盘输入

其实从第一章开始,代码里面一直都有实现键盘输入:

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

实现的是:按下Esc键,关闭窗口

这样的话,如果我们想通过WASD来控制摄像机往对应方向移动,就只需要多加几行判定:

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);GLfloat cameraSpeed = 0.05f;if (key == GLFW_KEY_W)cameraPos += cameraSpeed * cameraFront;if (key == GLFW_KEY_S)cameraPos -= cameraSpeed * cameraFront;if (key == GLFW_KEY_A)cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (key == GLFW_KEY_D)cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

还记得上一章的LookAt方法嘛,里面传入的是摄像机位置、目标位置和世界空间的上向量,其中摄像机位置对应的正是cameraPos,而世界空间的上向量对应的是cameraUp,我们设定cameraFront为摄像机的方向向量,那么目标位置就可以是cameraPos + cameraFront

初始化全局变量:

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);

LookAt方法传参:

view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);

经过测试你会发现两个问题:

  1. 不能同时按下两个键(例如W和A)来控制摄像机
  2. 刚开始长按某个方向的按键的时候明显会感觉到有一段0.5秒左右的停顿

原因是keyCallback每次只能同时响应一个key,因此正确的方法是在 while (!glfwWindowShouldClose(window)) 循环内判断哪些按键是按下状态,并进行值的更新,这样的话,keyCallback回调里面我们只需要记录和改变按键的状态就可以了,改动如下:

void cameraMove()
{GLfloat cameraSpeed = 0.05f;if (keys[GLFW_KEY_W])cameraPos += cameraSpeed * cameraFront;if (keys[GLFW_KEY_S])cameraPos -= cameraSpeed * cameraFront;if (keys[GLFW_KEY_A])cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (keys[GLFW_KEY_D])cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);if (action == GLFW_PRESS)           //如果当前是按下操作keys[key] = true;else if (action == GLFW_RELEASE)            //松开键盘keys[key] = false;
}
while (!glfwWindowShouldClose(window))
{//……cameraMove();//……
}

测试一下,非常舒服!

二、deltaTime变量

一般电脑游戏流畅运行,大概是1秒60帧左右,也就是FPS = 60,如果配置强悍,FPS能到达144以上,每台电脑每秒绘制的次数不同就会导致在同样的时间内,上面的cameraMove()方法调用的次数不同

当我们发布我们的游戏/应用时,我们需要确保无论哪台设备,输入控制的移动速度都一样,可是上面的代码是做不到这一点的,这个时候我们就需要deltaTime这个变量,如果之前学过Unity3D等其它图形应用的话,应该都会对这个变量非常清楚,它储存着渲染上一帧所用的时间

这样的话,我们只需要每一帧进行cameraMove()计算的时候,将deltaTime的值乘以速度参数,这样就可以做到速度与刷新率的平衡,无论你的机器快还是慢,摄像机的速度都会保持一致,每个用户的体验就都一样了

openGL的话,自己算吧:

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void cameraMove()
{GLfloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;GLfloat cameraSpeed = 1.0f * deltaTime;if (keys[GLFW_KEY_W])cameraPos += cameraSpeed * cameraFront;if (keys[GLFW_KEY_S])cameraPos -= cameraSpeed * cameraFront;if (keys[GLFW_KEY_A])cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (keys[GLFW_KEY_D])cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

三、滚轮缩放

有了键盘输入后,我们来看看鼠标的输入

一般游戏都有滑动滚轮控制视角缩放的功能,这里也尝试一下:

和键盘输入一样:注册回调函数并实现

glfwSetScrollCallback(window, scroll_callback);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{if (aspect >= 1.0f && aspect <= 45.0f)aspect -= yoffset;if (aspect <= 1.0f)aspect = 1.0f;if (aspect >= 45.0f)aspect = 45.0f;
}

修改下投影矩阵,搞定!

projection = glm::perspective(glm::radians(aspect), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);

四、其它鼠标输入

上面实现了摄像机的移动和缩放,当然还有一个非常重要的东西:摄像机视角控制

一般游戏的视角都是通过鼠标的移动来控制的,这相对于移动和缩放,要更难和复杂,所以这一章就先不讲了

现在暂时只了解如何获取鼠标的移动事件以及按键点击事件吧

鼠标点击事件:

glfwSetMouseButtonCallback(window, mouse_button_callback);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{if (action == GLFW_PRESS){switch (button){case GLFW_MOUSE_BUTTON_LEFT:printf("鼠标左键按下!!");break;case GLFW_MOUSE_BUTTON_MIDDLE:printf("鼠标中间按下!!");break;case GLFW_MOUSE_BUTTON_RIGHT:printf("鼠标右键按下!!");break;default:return;}}return;
}

鼠标移动事件:

glfwSetCursorPosCallback(window, mouse_callback);
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}GLfloat xoffset = xpos - lastX;GLfloat yoffset = lastY - ypos;lastX = xpos;lastY = ypos;printf("鼠标移动差:x = %.1f, y = %.1f", xoffset, yoffset);
}

别忘了窗口y轴坐标是从下往上的!所以从代码中可以看到:在计算y轴的移动差的时候,是反过来的

隐藏光标:

  • glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED):该函数为输入模式设置,第二个参数为要设置的模式,第三个参数为对应的值,这里设置的功能是隐藏光标,并捕捉(Capture)它。捕捉鼠标意味着当应用集中焦点到鼠标上的时候光标就应该留在窗口中,除非应用拾取焦点或退出

完整代码:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Shader.h"
#include<opengl/freeglut.h>
#include<SOIL.h>bool keys[1024];
bool firstMouse = true;
GLfloat lastX, lastY;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void cameraMove();
GLfloat aspect = 45.0f;
const GLuint WIDTH = 800, HEIGHT = 600;
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);glfwSetKeyCallback(window, key_callback);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);glfwSetMouseButtonCallback(window, mouse_button_callback);glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);glewExperimental = GL_TRUE;glewInit();int width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);Shader shaderYellow("VShader.txt", "FShaderY.txt");GLfloat vertices[] = {-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,0.5f, -0.5f, -0.5f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 1.0f,0.5f,  0.5f,  0.5f,  1.0f, 1.0f,-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f,  0.5f,  0.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  1.0f, 1.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f};GLuint VBO, VAO, texture;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenTextures(1, &texture);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBindTexture(GL_TEXTURE_2D, texture);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(1);int picWidth, picHeight;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);unsigned char* image = SOIL_load_image("Texture/wood.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);SOIL_free_image_data(image);glBindTexture(GL_TEXTURE_2D, 0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);glEnable(GL_DEPTH_TEST);while (!glfwWindowShouldClose(window)){glfwPollEvents();glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glClear(GL_DEPTH_BUFFER_BIT);cameraMove();glBindTexture(GL_TEXTURE_2D, texture);shaderYellow.Use();float radius = 5.0f;float camX = sin(glfwGetTime()) * radius;float camZ = cos(glfwGetTime()) * radius;glm::mat4 model = glm::mat4(1.0f);glm::mat4 view = glm::mat4(1.0f);glm::mat4 projection = glm::mat4(1.0f);model = glm::rotate(model, glm::radians(57.0f), glm::vec3(-0.5f, 1.0f, 0.0f));view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);projection = glm::perspective(glm::radians(aspect), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);GLint modelLoc = glGetUniformLocation(shaderYellow.Program, "model");GLint viewLoc = glGetUniformLocation(shaderYellow.Program, "view");GLint projLoc = glGetUniformLocation(shaderYellow.Program, "projection");glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);glfwSwapBuffers(window);}glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glfwTerminate();return 0;
}GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void cameraMove()
{GLfloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;GLfloat cameraSpeed = 1.0f * deltaTime;if (keys[GLFW_KEY_W])cameraPos += cameraSpeed * cameraFront;if (keys[GLFW_KEY_S])cameraPos -= cameraSpeed * cameraFront;if (keys[GLFW_KEY_A])cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (keys[GLFW_KEY_D])cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);if (action == GLFW_PRESS)           //如果当前是按下操作keys[key] = true;else if (action == GLFW_RELEASE)            //松开键盘keys[key] = false;
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{if (aspect >= 1.0f && aspect <= 45.0f)aspect -= yoffset;if (aspect <= 1.0f)aspect = 1.0f;if (aspect >= 45.0f)aspect = 45.0f;
}void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{if (action == GLFW_PRESS){switch (button){case GLFW_MOUSE_BUTTON_LEFT:printf("鼠标左键按下!!");break;case GLFW_MOUSE_BUTTON_MIDDLE:printf("鼠标中间按下!!");break;case GLFW_MOUSE_BUTTON_RIGHT:printf("鼠标右键按下!!");break;default:return;}}return;
}void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}GLfloat xoffset = xpos - lastX;GLfloat yoffset = lastY - ypos;lastX = xpos;lastY = ypos;printf("鼠标移动差:x = %.1f, y = %.1f", xoffset, yoffset);
}

OpenGL基础15:输入控制相关推荐

  1. opengl 教程(15) 摄像机控制(2)

    原帖地址:http://ogldev.atspace.co.uk/www/tutorial15/tutorial15.html 本篇教程中,我们将实现鼠标控制摄像机的方位.摄像机可以有多个自由度(所谓 ...

  2. OpenGL基础16:视角

    一.欧拉角 三种欧拉角: 俯仰角(Pitch):沿x轴旋转的角,从上往下看的角 偏航角(Yaw):沿y轴旋转的角,从左往右看的角 滚转角(Roll):沿z轴旋转的角(对于摄像机而言,一般不关心这个) ...

  3. opengl基础学习

    转自:http://www.cnblogs.com/crazyxiaom/articles/2073586.html 说起编程作图,大概还有很多人想起TC的#include <graphics. ...

  4. 计算机网络系统中每台计算机的地位是什么,东北师范计算机应用基础15秋在线作业1满分答案...

    东北师范计算机应用基础15秋在线作业1满分答案 一.单选题 1. 计算机网络按其所涉及范围的大小和计算机之间互联距离的不同,其类型可分 为____. A. 局域网.广域网和万维网 B. 局域网.广域网 ...

  5. OpenGL基础54:点光源阴影

    前置: OpenGL基础53:阴影映射(下) 一.万象阴影贴图 之前成功实现了平行光阴影,生成阴影贴图时使用的矩阵是正交矩阵,若是想要实现点光源的阴影效果,那么理论上只需要修改投影矩阵为透视矩阵就好了 ...

  6. OpenGL基础图形编程

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  7. OpenGL基础编程

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  8. 【转】OpenGL基础图形编程(一)

    原文:http://blog.chinaunix.net/uid-20638550-id-1909183.html  分类: 一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 ...

  9. OpenGL基础图形编程(转)

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

最新文章

  1. shell版俄罗斯方块
  2. Pod详解-端口设置
  3. java弹窗点击事件_[Java教程]jQuery的click事件在当前页弹出层窗口(不打开新页面)...
  4. d3js fill与class优先级
  5. matlab 结构体遍历,matlab 遍历结构体struc的成员
  6. root android手机型号,兼容支持各种型号手机的Root权限获取方法
  7. 人氏java速成(java基础内容)(一)java的基本类型
  8. java 登陆微信获取好友列表_微信api接口,触发推送微信好友列表及返回
  9. plt文件怎么转化为txt文件
  10. 数据库文档 SCREW 一键生成数据库文档
  11. NPOI Word 原有表格增加一行
  12. 不需要数据库的php迷你博客程序,GitHub - Smilefish0/miniblog: 一个不需要数据库、轻量级、微型、开源的博客程序!...
  13. 【ACWing】4246. 最短路径和
  14. 朋友圈如何秀一把!用Python一键生成炫酷九宫格图片!
  15. mysql 实现 connect by start with
  16. GoDaddy 推出第四代虚拟主机
  17. Windows10数字权利
  18. vmospro启动黑屏_vmosproroot权限启动器下载-vmospro步数倍率修改器v1.1.25 免费版-007游戏网...
  19. Vue3-路由跳转专题详细总结
  20. 关于调用新浪支付接口

热门文章

  1. python装饰器详解-学习笔记-Python装饰器详解
  2. python一对一视频教学-使用Python的Tornado框架实现一个一对一聊天的程序
  3. 如何系统的自学python-如何系统学习python
  4. python有什么用途-Python是什么 Python的用处
  5. 蝙蝠未能连接到服务器是什么原因,打开蝙蝠侠提示网络异常或者连接不上
  6. JavaScript面向对象之构造函数
  7. npm ERR! Unexpected end of JSON input while parsing near '...Comment: https://open'
  8. winform 往第三方传值 put
  9. 1432: 【蓝桥杯】:剪格子(迷宫问题变体)
  10. node代理请求 vue_vue-cli项目开发/生产环境代理实现跨域请求+webpack配置开发/生产环境的接口地址...