试着创建你自己的LookAt函数,其中你需要手动创建一个我们在一开始讨论的观察矩阵。用你的函数实现来替换GLM的LookAt函数,看看它是否还能一样地工作
1 glm::mat4 LookAt(glm::vec3 pos, glm::vec3 r, glm::vec3 u, glm::vec3 d){ 2 glm::mat4 a = glm::mat4(1.0f); 3 a[0][0] = r.x; a[1][0] = r.y; a[2][0] = r.z; 4 a[0][1] = u.x; a[1][1] = u.y; a[2][1] = u.z; 5 a[0][2] = -d.x; a[1][2] = -d.y; a[2][2] = -d.z; 6 glm::mat4 b = glm::mat4(1.0f); 7 b = glm::translate(b, glm::vec3(-pos.x, -pos.y, -pos.z)); 8 return a*b; 9 }
根据定义,由方向向量、右轴和上轴构成的矩阵与由摄像机坐标构成的矩阵相乘就得到LookAt矩阵了
注意:
- 方向向量和我们定义的Front(摄像机的朝向)是相反的
- glm中矩阵的存储和现实中的矩阵是不同的,glm中矩阵的列对应着现实中矩阵的行
最后还需调用glm::mat4 view = LookAt(camera.Position, camera.Right, camera.Up, camera.Front);即可
1 #include <glad/glad.h> 2 #include <GLFW/glfw3.h> 3 #define STB_IMAGE_IMPLEMENTATION 4 #include <stb/stb_image.h> 5 6 #include <glm/glm.hpp> 7 #include <glm/gtc/matrix_transform.hpp> 8 #include <glm/gtc/type_ptr.hpp> 9 10 #include <Shader/shader.h> 11 #include <Camera/camera.h> 12 13 #include <iostream> 14 15 void framebuffer_size_callback(GLFWwindow* window, int width, int height); 16 void mouse_callback(GLFWwindow* window, double xpos, double ypos); 17 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); 18 void processInput(GLFWwindow *window); 19 20 // settings 21 const unsigned int SCR_WIDTH = 800; 22 const unsigned int SCR_HEIGHT = 600; 23 24 // camera 25 Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); 26 float lastX = SCR_WIDTH / 2.0f; 27 float lastY = SCR_HEIGHT / 2.0f; 28 bool firstMouse = true; 29 30 //timeing 31 float deltaTime = 0.0f; // 当前帧与上一帧的时间差 32 float lastFrame = 0.0f; // 上一帧的时间 33 34 glm::mat4 LookAt(glm::vec3 pos, glm::vec3 r, glm::vec3 u, glm::vec3 d){ 35 glm::mat4 a = glm::mat4(1.0f); 36 a[0][0] = r.x; a[1][0] = r.y; a[2][0] = r.z; 37 a[0][1] = u.x; a[1][1] = u.y; a[2][1] = u.z; 38 a[0][2] = -d.x; a[1][2] = -d.y; a[2][2] = -d.z; 39 glm::mat4 b = glm::mat4(1.0f); 40 b = glm::translate(b, glm::vec3(-pos.x, -pos.y, -pos.z)); 41 return a*b; 42 } 43 44 int main() 45 { 46 // glfw: initialize and configure 47 // ------------------------------ 48 glfwInit(); 49 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 50 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 51 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 52 53 #ifdef __APPLE__ 54 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X 55 #endif 56 57 // glfw window creation 58 // -------------------- 59 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); 60 if (window == NULL) 61 { 62 std::cout << "Failed to create GLFW window" << std::endl; 63 glfwTerminate(); 64 return -1; 65 } 66 glfwMakeContextCurrent(window); 67 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 68 glfwSetCursorPosCallback(window, mouse_callback); 69 glfwSetScrollCallback(window, scroll_callback); 70 71 // tell GLFW to capture our mouse 72 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 73 74 // glad: load all OpenGL function pointers 75 // --------------------------------------- 76 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 77 { 78 std::cout << "Failed to initialize GLAD" << std::endl; 79 return -1; 80 } 81 82 // configure global opengl state 83 // ----------------------------- 84 glEnable(GL_DEPTH_TEST); 85 86 // build and compile our shader zprogram 87 // ------------------------------------ 88 Shader ourShader("vs.in", "fs.in"); 89 90 // set up vertex data (and buffer(s)) and configure vertex attributes 91 // ------------------------------------------------------------------ 92 float vertices[] = { 93 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 94 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 95 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 96 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 97 -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 98 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 99 100 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 101 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 102 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 103 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 104 -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 105 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 106 107 -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 108 -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 109 -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 110 -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 111 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 112 -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 113 114 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 115 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 116 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 117 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 118 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 119 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 120 121 -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 122 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 123 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 124 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 125 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 126 -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 127 128 -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 129 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 130 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 131 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 132 -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 133 -0.5f, 0.5f, -0.5f, 0.0f, 1.0f 134 }; 135 // world space positions of our cubes 136 glm::vec3 cubePositions[] = { 137 glm::vec3(0.0f, 0.0f, 0.0f), 138 glm::vec3(2.0f, 5.0f, -15.0f), 139 glm::vec3(-1.5f, -2.2f, -2.5f), 140 glm::vec3(-3.8f, -2.0f, -12.3f), 141 glm::vec3(2.4f, -0.4f, -3.5f), 142 glm::vec3(-1.7f, 3.0f, -7.5f), 143 glm::vec3(1.3f, -2.0f, -2.5f), 144 glm::vec3(1.5f, 2.0f, -2.5f), 145 glm::vec3(1.5f, 0.2f, -1.5f), 146 glm::vec3(-1.3f, 1.0f, -1.5f) 147 }; 148 unsigned int VBO, VAO; 149 glGenVertexArrays(1, &VAO); 150 glGenBuffers(1, &VBO); 151 152 glBindVertexArray(VAO); 153 154 glBindBuffer(GL_ARRAY_BUFFER, VBO); 155 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 156 157 // position attribute 158 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); 159 glEnableVertexAttribArray(0); 160 // texture coord attribute 161 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); 162 glEnableVertexAttribArray(1); 163 164 165 // load and create a texture 166 // ------------------------- 167 unsigned int texture1, texture2; 168 // texture 1 169 // --------- 170 glGenTextures(1, &texture1); 171 glBindTexture(GL_TEXTURE_2D, texture1); 172 // set the texture wrapping parameters 173 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 174 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 175 // set texture filtering parameters 176 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 177 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 178 // load image, create texture and generate mipmaps 179 int width, height, nrChannels; 180 stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis. 181 unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0); 182 if (data) 183 { 184 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); 185 glGenerateMipmap(GL_TEXTURE_2D); 186 } 187 else 188 { 189 std::cout << "Failed to load texture" << std::endl; 190 } 191 stbi_image_free(data); 192 // texture 2 193 // --------- 194 glGenTextures(1, &texture2); 195 glBindTexture(GL_TEXTURE_2D, texture2); 196 // set the texture wrapping parameters 197 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 199 // set texture filtering parameters 200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 202 // load image, create texture and generate mipmaps 203 data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0); 204 if (data) 205 { 206 // note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA 207 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 208 glGenerateMipmap(GL_TEXTURE_2D); 209 } 210 else 211 { 212 std::cout << "Failed to load texture" << std::endl; 213 } 214 stbi_image_free(data); 215 216 // tell opengl for each sampler to which texture unit it belongs to (only has to be done once) 217 // ------------------------------------------------------------------------------------------- 218 ourShader.use(); 219 ourShader.setInt("texture1", 0); 220 ourShader.setInt("texture2", 1); 221 222 // pass projection matrix to shader (as projection matrix rarely changes there's no need to do this per frame) 223 // ----------------------------------------------------------------------------------------------------------- 224 //glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); 225 //ourShader.setMat4("projection", projection); 226 227 228 // render loop 229 // ----------- 230 while (!glfwWindowShouldClose(window)) 231 { 232 float currentFrame = glfwGetTime(); 233 deltaTime = currentFrame - lastFrame; 234 lastFrame = currentFrame; 235 236 // input 237 // ----- 238 processInput(window); 239 240 // render 241 // ------ 242 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 243 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 244 245 // bind textures on corresponding texture units 246 glActiveTexture(GL_TEXTURE0); 247 glBindTexture(GL_TEXTURE_2D, texture1); 248 glActiveTexture(GL_TEXTURE1); 249 glBindTexture(GL_TEXTURE_2D, texture2); 250 251 // activate shader 252 ourShader.use(); 253 254 // pass projection matrix to shader (note that in this case it could change every frame) 255 glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); 256 ourShader.setMat4("projection", projection); 257 258 // camera/view transformation 259 260 //glm::mat4 view = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first 261 //float radius = 10.0f; 262 //float camX = sin(glfwGetTime()) * radius; 263 //float camZ = cos(glfwGetTime()) * radius; 264 //view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); 265 266 //glm::mat4 view = camera.GetViewMatrix(); 267 glm::mat4 view = LookAt(camera.Position, camera.Right, camera.Up, camera.Front); 268 ourShader.setMat4("view", view); 269 270 // render boxes 271 glBindVertexArray(VAO); 272 for (unsigned int i = 0; i < 10; i++) 273 { 274 // calculate the model matrix for each object and pass it to shader before drawing 275 glm::mat4 model = glm::mat4(1.0f); 276 model = glm::translate(model, cubePositions[i]); 277 float angle = 20.0f * i; 278 model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); 279 ourShader.setMat4("model", model); 280 281 glDrawArrays(GL_TRIANGLES, 0, 36); 282 } 283 284 // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) 285 // ------------------------------------------------------------------------------- 286 glfwSwapBuffers(window); 287 glfwPollEvents(); 288 } 289 290 // optional: de-allocate all resources once they've outlived their purpose: 291 // ------------------------------------------------------------------------ 292 glDeleteVertexArrays(1, &VAO); 293 glDeleteBuffers(1, &VBO); 294 295 // glfw: terminate, clearing all previously allocated GLFW resources. 296 // ------------------------------------------------------------------ 297 glfwTerminate(); 298 return 0; 299 } 300 301 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly 302 // --------------------------------------------------------------------------------------------------------- 303 void processInput(GLFWwindow *window) 304 { 305 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) 306 glfwSetWindowShouldClose(window, true); 307 308 if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) 309 camera.ProcessKeyboard(FORWARD, deltaTime); 310 if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) 311 camera.ProcessKeyboard(BACKWARD, deltaTime); 312 if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) 313 camera.ProcessKeyboard(LEFT, deltaTime); 314 if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) 315 camera.ProcessKeyboard(RIGHT, deltaTime); 316 } 317 318 // glfw: whenever the window size changed (by OS or user resize) this callback function executes 319 // --------------------------------------------------------------------------------------------- 320 void framebuffer_size_callback(GLFWwindow* window, int width, int height) 321 { 322 // make sure the viewport matches the new window dimensions; note that width and 323 // height will be significantly larger than specified on retina displays. 324 glViewport(0, 0, width, height); 325 } 326 327 void mouse_callback(GLFWwindow* window, double xpos, double ypos){ 328 if (firstMouse) 329 { 330 lastX = xpos; 331 lastY = ypos; 332 firstMouse = false; 333 } 334 335 float xoffset = xpos - lastX; 336 float yoffset = lastY - ypos; 337 //std::cout << ypos << std::endl; 338 lastX = xpos; 339 lastY = ypos; 340 341 camera.ProcessMouseMovement(xoffset, yoffset); 342 } 343 344 // glfw: whenever the mouse scroll wheel scrolls, this callback is called 345 // ---------------------------------------------------------------------- 346 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) 347 { 348 camera.ProcessMouseScroll(yoffset); 349 }