一、什么是相机坐标系?
相机坐标系,又称为相机空间坐标系,是指相机内部的坐标系,通常原点位于相机的光心位置。在相机坐标系中,其它的参数都是针对相机而言的,例如相机的焦距、光圈、透视投影矩阵等。
在OpenGL等图形学API中,我们常用3个向量表示相机坐标系的状态,即相机位置向量(eye)、相机视线向量(center)和相机上方向量(up)。根据这三个向量,可以构建出一个正交或投影的相机坐标系,以此来描述相机内部场景的设置和视角。
视图变换部分代码示例: mat4 view = lookAt(eye, center, up);
二、什么是世界坐标系?
世界坐标系是指整个场景的坐标系,所有的物体都是相对于这个坐标系而言的。在OpenGL等图形学API中,我们常用世界坐标系来描述场景的配置和物体的定位。在这个坐标系中,相机的位置和姿态通常是已知的,所以我们在建立物体的模型坐标系时,可以很方便地将其附加在世界坐标系上。
模型矩阵变换部分代码示例: mat4 model = translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.0f)); model = scale(model, vec3(1.0f, 1.0f, 1.0f)); model = rotate(model, radians(angle), axis);
三、相机坐标系与世界坐标系的关系
相机坐标系与世界坐标系之间的转换是图形学应用中非常重要的一个问题。相机坐标系通常需要通过透视矩阵(Perspective Matrix)或正交矩阵(Orthogonal Matrix)来进行转换,而世界坐标系则通常需要用到模型矩阵(Model Matrix)。
在OpenGL中,我们通过将不同的矩阵相乘,来实现对相机坐标系和世界坐标系的转换。具体的计算方式可以参考下面的代码示例:
视图变换部分代码示例: mat4 view = lookAt(eye, center, up); 透视投影矩阵部分代码示例: mat4 proj = perspective(radians(45.0f), (float)width / (float)height, 0.1f, 100.0f); 模型矩阵变换部分代码示例: mat4 model = translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.0f)); model = scale(model, vec3(1.0f, 1.0f, 1.0f)); model = rotate(model, radians(angle), axis); 最终变换矩阵部分代码示例: mat4 mvp = proj * view * model;
四、相机坐标系与世界坐标系的应用
相机坐标系与世界坐标系的概念在计算机图形学中有着非常广泛的应用。例如,我们可以利用相机坐标系的变换来动态地改变场景的视角,并在屏幕上渲染出预期的效果;而在计算机视觉处理中,我们也可以将相机坐标系和世界坐标系用于图像的校正、姿态的计算等。
下面是一个例子,演示了如何利用OpenGL中的相机坐标系和世界坐标系,来绘制出一个基本的纹理立方体:
// 创建立方体的顶点和纹理坐标 float vertices[] = { // 顶点坐标 // 纹理坐标 -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, // 更多的顶点和纹理坐标,省略... }; // 创建立方体的VAO和VBO unsigned int VAO, VBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // 加载立方体的纹理 unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); 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); int width, height, nrChannels; unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0); if (data) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); } else { std::cout << "Failed to load texture" << std::endl; } stbi_image_free(data); // 图形渲染循环 while (!glfwWindowShouldClose(window)) { // 处理输入事件 processInput(window); // 渲染背景色和立方体 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 应用相机坐标系和世界坐标系的变换 mat4 view = lookAt(eye, center, up); mat4 proj = perspective(radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); mat4 model = mat4(1.0f); mat4 mvp = proj * view * model; ourShader.setMat4("mvp", mvp); // 绘制立方体 glBindTexture(GL_TEXTURE_2D, texture); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 36); // 交换缓冲区并检查事件 glfwSwapBuffers(window); glfwPollEvents(); }
五、总结
相机坐标系与世界坐标系的概念在计算机图形学和计算机视觉领域有着广泛的应用,尤其对于那些需要对物体姿态、视角、纹理等进行处理的应用来说,更是不可或缺的基础概念。因此,深入理解这两个坐标系的原理和应用,对于图形学和视觉处理领域的学习和应用有着重要的启发意义。