OpenGL摄像机

  • 1. 摄像机/观察空间
  • 2. Look At
  • 3. lookAt 矩阵案例

1. 摄像机/观察空间

观察矩阵将所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。当定义一个摄像机时需要它在世界空间中的位置、观察的方向、一个指向它右侧的向量以及一个指向它上方的向量。实际上创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。如下图所示:

图1中的Position摄像机位置

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);

正z轴是从屏幕指向你的,如果我们希望摄像机向后移动,我们就沿着z轴的正方向移动

图1中的Direction摄像机方向
摄像机方向是指摄像机指向场景原点:(0, 0, 0)的方向,用摄像机位置向量减去场景原点向量得到摄像机方向,也就是图中蓝色的指向

glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);

图1中的Right右向量
它代表摄像机空间的x轴的正方向。为获取右向量我们需要先使用一个小技巧:先定义一个上向量(Up Vector),接下来把上向量和第二步得到的方向向量进行叉乘。两个向量叉乘的结果会同时垂直于两向量,因此我们会得到指向x轴正方向的那个向量(如果我们交换两个向量叉乘的顺序就会得到相反的指向x轴负方向的向量):

glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));

图1中的Up上轴
已经有了x轴向量和z轴向量,获取一个指向摄像机的正y轴向量就相对简单了:只需把右向量和方向向量进行叉乘:

glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);

2. Look At

可以用这3个轴外加一个平移向量来创建一个矩阵(LookAt矩阵)

其中R是右向量,U是上向量,D是方向向量P是摄像机位置向量。注意,位置向量是相反的,因为我们最终希望把世界平移到与我们自身移动的相反方向。把这个LookAt矩阵作为观察矩阵可以很高效地把所有世界坐标变换到刚刚定义的观察空间。LookAt矩阵就像它的名字表达的那样:它会创建一个看着(Look at)给定目标的观察矩阵。

在GLM库的支持下我们可以很方便创建一个LookAt矩阵

glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

lookAt矩阵中传入的三个参数解释:

  • 第一个为摄像机位置
  • 第二个为目标位置
  • 第三个为世界空间中的上向量(用来计算右向量使用的那个上向量)

3. lookAt 矩阵案例

摄像机在场景中旋转,构建lookAt矩阵

 glm::mat4 view = glm::mat4(1.0f);
float radius = 10.0f;
float camX = sin(glfwGetTime()) * radius;
float camZ = cos(glfwGetTime()) * radius;
glm::mat4 view;
view = glm::lookAt(glm::vec3(camX, 2![请添加图片描述](https://img-blog.csdnimg.cn/a1212946586548c78f54b2dbfd13384a.gif)
.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));

单一模型实现的情况

多个模型旋转
这边不放动图了,原本应该动起来

实现代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>#include <learnopengl/filesystem.h>
#include <learnopengl/shader_m.h>#include <iostream>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;int main()
{// glfw: initialize and configure// ------------------------------glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif// glfw window creation// --------------------GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// glad: load all OpenGL function pointers// ---------------------------------------if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// configure global opengl state// -----------------------------glEnable(GL_DEPTH_TEST);// build and compile our shader zprogram// ------------------------------------Shader ourShader("7.1.camera.vs", "7.1.camera.fs");// set up vertex data (and buffer(s)) and configure vertex attributes// ------------------------------------------------------------------float 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};// world space positions of our cubesglm::vec3 cubePositions[] = {glm::vec3( 0.0f,  0.0f,  0.0f),glm::vec3( 2.0f,  5.0f, -15.0f),glm::vec3(-1.5f, -2.2f, -2.5f),glm::vec3(-3.8f, -2.0f, -12.3f),glm::vec3 (2.4f, -0.4f, -3.5f),glm::vec3(-1.7f,  3.0f, -7.5f),glm::vec3( 1.3f, -2.0f, -2.5f),glm::vec3( 1.5f,  2.0f, -2.5f),glm::vec3( 1.5f,  0.2f, -1.5f),glm::vec3(-1.3f,  1.0f, -1.5f)};unsigned int VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// position attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// texture coord attributeglVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// load and create a texture // -------------------------unsigned int texture1, texture2;// texture 1// ---------glGenTextures(1, &texture1);glBindTexture(GL_TEXTURE_2D, texture1);// set the texture wrapping parametersglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);// set texture filtering parametersglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// load image, create texture and generate mipmapsint width, height, nrChannels;stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.unsigned char *data = stbi_load(FileSystem::getPath("resources/textures/hutao.png").c_str(), &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);// texture 2// ---------glGenTextures(1, &texture2);glBindTexture(GL_TEXTURE_2D, texture2);// set the texture wrapping parametersglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);// set texture filtering parametersglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// load image, create texture and generate mipmapsdata = stbi_load(FileSystem::getPath("resources/textures/awesomeface.png").c_str(), &width, &height, &nrChannels, 0);if (data){// note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBAglTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);}else{std::cout << "Failed to load texture" << std::endl;}stbi_image_free(data);// tell opengl for each sampler to which texture unit it belongs to (only has to be done once)// -------------------------------------------------------------------------------------------ourShader.use();ourShader.setInt("texture1", 0);ourShader.setInt("texture2", 1);// pass projection matrix to shader (as projection matrix rarely changes there's no need to do this per frame)// -----------------------------------------------------------------------------------------------------------glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);ourShader.setMat4("projection", projection); // render loop// -----------while (!glfwWindowShouldClose(window)){// input// -----processInput(window);// render// ------glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // bind textures on corresponding texture unitsglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, texture1);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, texture2);// activate shaderourShader.use();// camera/view transformationglm::mat4 view = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix firstfloat radius = 8.0f;float camX = static_cast<float>(sin(glfwGetTime()*0.5) * radius);float camZ = static_cast<float>(cos(glfwGetTime()*0.5) * radius);view = glm::lookAt(glm::vec3(0, 1.0f, 5.0), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));ourShader.setMat4("view", view);// render boxesglBindVertexArray(VAO);for (unsigned int i = 0; i < 10; i++){// calculate the model matrix for each object and pass it to shader before drawingglm::mat4 model = glm::mat4(1.0f);model = glm::translate(model, cubePositions[i]);float angle = 20.0f * i;model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));ourShader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);}// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)// -------------------------------------------------------------------------------glfwSwapBuffers(window);glfwPollEvents();}// optional: de-allocate all resources once they've outlived their purpose:// ------------------------------------------------------------------------glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);// glfw: terminate, clearing all previously allocated GLFW resources.// ------------------------------------------------------------------glfwTerminate();return 0;
}// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{// make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays.glViewport(0, 0, width, height);
}

OpenGL摄像机(Look At矩阵)相关推荐

  1. OpenGL 摄像机Camera

    OpenGL 摄像机Camera 摄像机Camera简介 摄像机/观察空间 摄像机位置 摄像机方向 右轴 上轴 Look At 自由移动 移动速度 视角移动 欧拉角 鼠标输入 缩放 摄像机类 摄像机C ...

  2. 计算机视觉和图形学中的摄像机内参数矩阵详解

    在计算机视觉和图形学中都有"摄像机内参数矩阵"这个概念,其含义大致相同,但在实际使用过程中,这两个矩阵却相差甚远.在增强现实中,为了使计算机绘制的虚拟物体和真实环境图像对其,需要令 ...

  3. OpenGL 摄像机

    文章目录 1.摄像机坐标系 2.Look At 矩阵: 3.控制摄像机移动 4.控制移动速度 5.控制视角 6.鼠标控制角度 7.缩放 8.源码 参考:https://learnopengl-cn.g ...

  4. LearnOpenGL学习笔记——OpenGL摄像机

    摄像机/观察空间 当我们讨论摄像机的观察空间(Camera/View Space)的时候,是在讨论以摄像机的视角作为场景原点时场景中所有的顶点坐标,通俗来讲,就是眼睛的位置,角度. 要在三维的世界中确 ...

  5. OpenGL学习脚印: 投影矩阵和视口变换矩阵

    OpenGL中的视图可以利用照相机来进行比拟.产生目标的场景的变过过程类似于相机拍照.此步骤大概分为三个: 1)把相机固定在三角架上,并让它对准场景(视图变换) 2)对场景安排,使得各个物体在招片中的 ...

  6. 旁观OpenGL里的透视投影矩阵

    OpenGL里的代码已经形成和使用了好多年,从程序员的角度,对OpenGL底层代码做升级或修改的实际意义并不大,而且风险高.所以,修改底层算法的想法是有些太狂了.但是,从教学的角度.提供一种新的视角去 ...

  7. Android游戏开发之OpenGL之视图-投影矩阵 杂谈

    本文的内容有: 1.控制观察角度和观察位置. 2.模型中不需要的部分从场景中裁剪出去. 3.熟练操控良好的矩阵栈,这些矩阵栈控制着模型转换,到屏幕的映射. 4.联合多个转换来模拟复杂的运动系统,例如: ...

  8. Opengl中的TBN矩阵的计算和使用

    TBN矩阵 T:切线向量 B:副切线向量 N:法线向量 法线向量不用我们计算,我们的主要任务是根据三角形的坐标计算出T和B向量. 公式推导 如图,E2向量与纹理坐标的差ΔU2.ΔV2构成一个三角形.Δ ...

  9. OpenGL摄像机键盘交互

    摄像机交互 对于摄像机基础实现内容较为生疏的可以参考该文章

最新文章

  1. 未来,中国空间站将成为怎样的“太空科研站”?
  2. Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.el6.i686
  3. 体验使用node.js创建vue+Element-UI项目
  4. ORACLE多表查询优化(引)
  5. oracle_sid只能有一个吗_第一次考教资!这些问题你都了解了吗?
  6. django-后台管理-编辑页的选项
  7. 一个程序员一月的开销统计分析、(附上PC端和移动端android源码)
  8. 移植oprofile到海思
  9. 金蝶oracle用鼎信诺取数,取数软件 审计取数软件?
  10. 漫画小程序支持流量主更新修复接口,自动采集资源,漫画源码漫画小程序源码简单即可发布
  11. 怎么在局域网中设置共享文件夹?
  12. 半导体物理学学习资源
  13. iOS SceneDelegate使用总结
  14. 决策树案例:是否打网球
  15. Node.js中的npm与包
  16. mac book pro高清录屏教程(obs录屏+麦克风录制+soundflower电脑声录制)三合一
  17. 音乐心理学 | 《聆听心声》笔记
  18. 大学计算机excel函数课件,《Excel函数教程》PPT课件.ppt
  19. 实现点击选择按钮时候选中对应选中的行,当点击某一行单元格时候就清除其他选中行然后选中对应点击的那一行
  20. Win7关闭防火墙的脚本

热门文章

  1. IBM李永辉:从人工智能到大数据的终点
  2. 音视频怎样入门?带你入门基础+学习思路
  3. 天蝎项目整机柜服务器技术规范,天蝎整机柜服务器技术规范25.doc
  4. 网易 UI 自动化工具 Airtest
  5. 如何在微信中打开app及Schema VS Universal Link
  6. Wangle源码分析:Service
  7. Redhat Linux 8.3 安装方法
  8. 学习记录657@python计算股价的回撤与收盘价回撤率组合图实现
  9. java实现排兵布阵(回溯法)
  10. 公司电脑重装经验 ThinkPad E480 win7重装 电脑重装