参考资料:http://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-6-keyboard-and-mouse/
知识点1:主要思想:实现键鼠操作对画面的影响,实际上是通过每一帧都变化的MVP矩阵来实现的,那么我们只需要对键鼠操作返回不同的MVP组成矩阵即可,然后在主循环中更新MVP矩阵,最终实现画面的切换。
do{ // ... // Compute the MVP matrix from keyboard and mouse input computeMatricesFromInputs(); glm::mat4 ProjectionMatrix = getProjectionMatrix(); glm::mat4 ViewMatrix = getViewMatrix(); glm::mat4 ModelMatrix = glm::mat4(1.0); glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix; // ... }这段代码需要3个新函数: computeMatricesFromInputs()读键盘和鼠标操作,然后计算投影观察矩阵。这一步正是精华所在。 getProjectionMatrix()返回计算好的投影矩阵。 getViewMatrix()返回计算好的观察矩阵。
// Include GLFW #include <glfw3.h> extern GLFWwindow* window; // The "extern" keyword here is to access the variable "window" declared in tutorialXXX.cpp. This is a hack to keep the tutorials simple. Please avoid this. // Include GLM #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> using namespace glm; #include "controls.hpp" glm::mat4 ViewMatrix; glm::mat4 ProjectionMatrix; glm::mat4 getViewMatrix(){ return ViewMatrix; } glm::mat4 getProjectionMatrix(){ return ProjectionMatrix; } //初始位置的设置 // Initial position : on +Z glm::vec3 position = glm::vec3( 0, 0, 5 ); // Initial horizontal angle : toward -Z float horizontalAngle = 3.14f; // Initial vertical angle : none float verticalAngle = 0.0f; // Initial Field of View float initialFoV = 45.0f; //鼠标属性的设定,速度,再乘以帧差时间,就可以得到移动距离 float speed = 3.0f; // 3 units / second float mouseSpeed = 0.005f; //计算MVP矩阵的各分量 void computeMatricesFromInputs(){ // glfwGetTime is called only once, the first time this function is called static double lastTime = glfwGetTime(); // Compute time difference between current and last frame double currentTime = glfwGetTime(); float deltaTime = float(currentTime - lastTime); // Get mouse position 鼠标位置获取 double xpos, ypos; glfwGetCursorPos(window, &xpos, &ypos); // Reset mouse position for next frame 下一帧鼠标位置设定在屏幕中央 glfwSetCursorPos(window, 1024/2, 768/2); // Compute new orientation 新视角的变化 horizontalAngle += mouseSpeed * float(1024/2 - xpos ); verticalAngle += mouseSpeed * float( 768/2 - ypos ); // Direction : Spherical coordinates to Cartesian coordinates conversion 视线方向的向量 glm::vec3 direction( cos(verticalAngle) * sin(horizontalAngle), sin(verticalAngle), cos(verticalAngle) * cos(horizontalAngle) ); // Right vector glm::vec3 right = glm::vec3( sin(horizontalAngle - 3.14f/2.0f), 0, cos(horizontalAngle - 3.14f/2.0f) ); // Up vector glm::vec3 up = glm::cross( right, direction ); // Move forward if (glfwGetKey( window, GLFW_KEY_UP ) == GLFW_PRESS){ position += direction * deltaTime * speed; } // Move backward if (glfwGetKey( window, GLFW_KEY_DOWN ) == GLFW_PRESS){ position -= direction * deltaTime * speed; } // Strafe right if (glfwGetKey( window, GLFW_KEY_RIGHT ) == GLFW_PRESS){ position += right * deltaTime * speed; } // Strafe left if (glfwGetKey( window, GLFW_KEY_LEFT ) == GLFW_PRESS){ position -= right * deltaTime * speed; } float FoV = initialFoV;// - 5 * glfwGetMouseWheel(); // Now GLFW 3 requires setting up a callback for this. It's a bit too complicated for this beginner's tutorial, so it's disabled instead. // Projection matrix : 45?Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); // Camera matrix ViewMatrix = glm::lookAt( position, // Camera is here position+direction, // and looks here : at the same position, plus "direction" up // Head is up (set to 0,-1,0 to look upside-down) ); // For the next frame, the "last time" will be "now" lastTime = currentTime; }知识点2:背面剔除
自由移动鼠标,您会观察到:如果移动到立方体内部,多边形仍然会显示。这看起来理所当然,实则大有优化的余地。实际上在常见应用中您绝不会身处立方体内部。 有一种思路是让GPU检查摄像机与三角形前后位置关系。如果摄像机在三角形前面则显示该三角形;如果摄像机在三角形后面,且不在网格(mesh)(网格必须是封闭的)内部,那么必有三角形位于摄像机前面。您一定会察觉到速度变快了:三角形数量平均减少了一半! 最可喜的是这种检查十分简单。GPU计算出三角形的法线(用叉乘,还记得吗?),然后检查这个法线是否朝向摄像机。 不过这种方法是有代价的:三角形的方向是隐含的。这意味着如果在缓冲中翻转两个顶点,可能会产生孔洞。但一般来说,这一点额外工作是值得的。一般在3D建模软件中只需点击”反转法线”(实际是翻转两个顶点,从而翻转法线)就大功告成了。 开启背面剔除十分简单:
// Cull triangles which normal is not towards the camera glEnable(GL_CULL_FACE);
C++代码特殊之处:
1:替换了纹理
2:实现模型的自动变化(未实现绕行),具体方法是将相机的position设置为随时间变化的参量
// Include GLFW #include <glfw3.h> extern GLFWwindow* window; // The "extern" keyword here is to access the variable "window" declared in tutorialXXX.cpp. This is a hack to keep the tutorials simple. Please avoid this. // Include GLM #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> using namespace glm; #include "controls.hpp" glm::mat4 ViewMatrix; glm::mat4 ProjectionMatrix; glm::mat4 getViewMatrix(){ return ViewMatrix; } glm::mat4 getProjectionMatrix(){ return ProjectionMatrix; } //初始位置的设置 // Initial position : on +Z glm::vec3 position = glm::vec3( 0, 0, 5 ); // Initial horizontal angle : toward -Z float horizontalAngle = 5.0f; // Initial vertical angle : none float verticalAngle = 0.0f; // Initial Field of View float initialFoV = 45.0f; //鼠标属性的设定 float speed = 3.0f; // 3 units / second float mouseSpeed = 0.005f; //计算MVP矩阵的各分量 void computeMatricesFromInputs(){ // glfwGetTime is called only once, the first time this function is called static double lastTime = glfwGetTime(); // Compute time difference between current and last frame double currentTime = glfwGetTime(); float deltaTime = float(currentTime - lastTime); // Get mouse position double xpos, ypos; glfwGetCursorPos(window, &xpos, &ypos); // Reset mouse position for next frame glfwSetCursorPos(window, 1024/2, 768/2); // Compute new orientation 新视角的变化 horizontalAngle += mouseSpeed * float(1024/2 - xpos ); verticalAngle += mouseSpeed * float( 768/2 - ypos ); //verticalAngle += 0.0f ; 可限制verticalAngle // Direction : Spherical coordinates to Cartesian coordinates conversion 视线方向的向量 glm::vec3 direction( cos(verticalAngle) * sin(horizontalAngle), sin(verticalAngle), cos(verticalAngle) * cos(horizontalAngle) ); // Right vector glm::vec3 right = glm::vec3( sin(horizontalAngle - 3.14f/2.0f), 0, cos(horizontalAngle - 3.14f/2.0f) ); // Up vector glm::vec3 up = glm::cross( right, direction ); // Move forward // if (glfwGetKey( window, GLFW_KEY_UP ) == GLFW_PRESS){ // position += direction * deltaTime * speed; // } // Move backward // if (glfwGetKey( window, GLFW_KEY_DOWN ) == GLFW_PRESS){ // position -= direction * deltaTime * speed; // } // Strafe right // if (glfwGetKey( window, GLFW_KEY_RIGHT ) == GLFW_PRESS){ // position += right * deltaTime * speed; // } // Strafe left // if (glfwGetKey( window, GLFW_KEY_LEFT ) == GLFW_PRESS){ // position -= right * deltaTime * speed; // } position = glm::vec3(0.0f,0.0f,10.0f) + (5 * cos(float(currentTime)), 0, 5 * sin(float(currentTime))); float FoV = initialFoV; //- 5 * glfwGetMouseWheel(); // Now GLFW 3 requires setting up a callback for this. It's a bit too complicated for this beginner's tutorial, so it's disabled instead. // Projection matrix : 45?Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); // Camera matrix ViewMatrix = glm::lookAt( position, // Camera is here /*position+direction*/glm::vec3(0,2,0), // and looks here : at the same position, plus "direction" glm::vec3(0,1,0) // Head is up (set to 0,-1,0 to look upside-down) ); // For the next frame, the "last time" will be "now" lastTime = currentTime; }