当前位置: 首页 > news >正文

大学生做网站网站速成

大学生做网站,网站速成,网站制作代理平台,安徽网络推广新手说明#xff1a;跟着learnopengl的内容学习#xff0c;不是纯翻译#xff0c;只是自己整理记录。 强烈推荐原文#xff0c;无论是内容还是排版。 原文链接 本文地址#xff1a;http://blog.csdn.net/aganlengzi/article/details/50448469 摄像机 Camera 在前面的教程中… 说明跟着learnopengl的内容学习不是纯翻译只是自己整理记录。  强烈推荐原文无论是内容还是排版。 原文链接  本文地址http://blog.csdn.net/aganlengzi/article/details/50448469 摄像机 Camera 在前面的教程中我们讨论了视口矩阵和我们可以怎样利用视口矩阵让绘制的场景移动我们在上次教程中成功将那个二维平面稍稍向后移动了一点。OpenGL本身对摄像机这个概念并不熟悉但是我们可以通过移动场景中所有的对象就好像反方向移动一个摄像机一样来模拟一个。 在本次教程中我们将会讨论我们怎样在OpenGL中建立摄像机。我们将会创建一个帧率摄像机允许你在三维场景中自由移动。在这个教程中我们还会讨论一些关于键盘和鼠标输入的只是。最终会形成一个定制的摄像机类。 摄像机/视口坐标系 三维场景中的一个摄像机主要由它在世界坐标系中的位置、它的朝向一个指向右侧和一个指向上方的向量来做决定。细心的你可能已经发现了我们实际上在利用这些量创建一个三个坐标轴相互垂直以所在位置为原点的坐标系。 1.摄像机的位置 得到一个摄像机的位置是十分简单的。摄像机的位置从根本上来说就是一个指向摄像机位置的向量。我们通过如下代码将摄像机的位置也就是我们想要创建的坐标系的原点设置在和我们上次教程相同的位置。 glm::vec3 cameraPos glm::vec3(0.0f, 0.0f, 3.0f); 不要忘记z轴的正方向是垂直于屏幕并且指向我们的。所以当我们想把摄像机向后移动实际上就是把摄像机向z轴的正方向移动也就是增大摄像机z轴的坐标值。 2.摄像机的方向 确定了摄像机的位置即坐标系的原点接下来需要确定的是摄像机的朝向。目前我们先默认摄像机指向我们世界坐标系的原点也就是(0,0,0)。实际上我们是知道摄像机应该指向z轴的负方向的那么它的相反方向实际上就是其位置坐标和世界坐标系原点的插值两个向量之差决定了其方向,我们定义这个向量值为方向向量。如下面的代码所示 glm::vec3 cameraTarget glm::vec3(0.0f, 0.0f, 0.0f); glm::vec3 cameraDirection glm::normalize(cameraPos - cameraTarget); 实际上方向向量这个说法是不太合适的因为这个方向向量命名是摄像机正面朝向的相反方向 3.向右的坐标轴 按照一开始的说法接下来需要的向量是向右的方向向量。它实际上代表的是我们想要创建的摄像机坐标系的x轴正方向。怎样获得向右指向的方向向量呢还记的前面教程中关于向量的叉乘的内容吗两个向量的叉乘得到的是垂直于这两个向量决定的平面的向量满足右手坐标系。所以我们利用2中得到的z轴正向向量和世界坐标系中的向上的方向向量做叉乘便得到了指向右侧的方向向量它就是我们想创建的摄像机坐标系的x轴正向。如下面代码所示 glm::vec3 up glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 cameraRight glm::normalize(glm::cross(up, cameraDirection)); 4.向上的坐标轴 通过以上三步我们已经得到了建立一个坐标系所需要的原点坐标、x轴z轴向量。现在就是缺少y轴方向的向量了我们通过将x轴和z轴的方向向量做叉乘的方式得到向上的坐标轴方向也就是我们的y轴方向向量。如下面的代码所示 glm::vec3 cameraUp glm::cross(cameraDirection, cameraRight); 利用上面得到的摄像机坐标系我们现在可以创建我们的LookAt矩阵了这个矩阵对于创建一个摄像机是十分有用和必要的。 Look At 使用矩阵的一大好处是当你利用三个相互垂直的坐标轴建立了一个坐标系你可以通过这三个坐标轴加上一个转换向量创建一个矩阵这个矩阵可以将任何向量转换到你所定义的那个坐标系空间中。而具体转换的方式就是左乘这个矩阵。这也正是我们的Look At矩阵将要做的事情。我们现在就用上面得到的三个相互垂直的坐标轴和一个位置向量来定义一个摄像机空间摄像机坐标系。并通过定义一个矩阵Look At矩阵来将世界坐标系中的坐标值转换到这个空间中。如下所示 其中R表示右向向量U表示向上的向量D代表方向向量P是摄像机的位置向量。需要注意的是这里的位置向量是上面讲的伪“方向向量”的反方向也就是摄像机朝向的方向向量。为什么要这么做因为我们向左移动摄像机那么世界坐标系中的对象看上去实际上是向右移动的。使用这个Look At矩阵作为我们的视口矩阵能够高效地将世界坐标系中的坐标转换到我们刚刚定义的视口坐标系中。这样Look At矩阵也就名符其实了它创建了一个朝向给定目标的视口矩阵。 幸运的是GLM早就为我们封装了以上的过程。我们只需要指定一个摄像机的位置一个目标位置和一个代表世界坐标系中向上方向的向量作为参数就可以创建这个Look At矩阵了。我们使用这个创建好的Look At矩阵作为我们的视口矩阵 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)); 函数glm::LookAt的三个参数分别就是上面提到的三个参数得到的view和我们上面通过手工一步步创建得到的矩阵是一模一样的。 在深入研究用户输入之前让我们首先利用摄像机的旋转来产生一些酷炫的效果。我们设置场景的目标向量是(0,0,0)。 我们将我们的摄像机放在一个半径为10.0f的圆周上这个圆周在世界坐标系的z轴和x轴决定的平面上并且随着时间的推移摄像机的位置不断改变通过三角函数实现主要是要保证摄像机总是在圆周上。这样在每次改变后得到的矩阵作为我们的视口矩阵。然后来看效果。 GLfloat radius 10.0f; GLfloat camX sin(glfwGetTime()) * radius; GLfloat camZ cos(glfwGetTime()) * radius; glm::mat4 view; view glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0)); 得到的效果应该是这个样子的显示不了动画应该看上起是围绕中心旋转的样子 可以通过设置不同圆的半径值来得到不同的效果。 漫游 Walk around 转动摄像机来得到变化角度的场景是有趣的但是我们还可以让它更加有趣那就是我们自己来移动摄像机想什么时候移动就什么时候移动想移动到哪儿就移动到哪儿。不过我们还是像上面一样应该建立摄像机系统。为了灵活性先来定义一些必要的变量吧 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); 现在我们上面说到的Look At矩阵编程了这个样子 view glm::lookAt(cameraPos, cameraPos cameraFront, cameraUp); 还是和上面一样。我们首先向glm::lookAt函数传递进事先定义的摄像机位置向量方向是当前的位置与事先定义的方向向量的和这保证了无论我们怎样移动这个摄像机都会指向目标。下面让我们通过将摄像机的位置变量cameraPos和我们的键盘输入相关联以达到按键改变相机位置的目的。 在很早的教程中我们就已经知道了怎样使用键盘响应函数。不过之前只是实现了我们的程序应该如何相应”ESC”键现在让我们为这个键盘响应函数添加更多的键值处理 void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {...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; } 每当我们按下WASD键的时候cameraPos就会被相应改变。当我们想要前后移动的时候我们将摄像机的位置在指向目标的连线上进行加减。当我们想要左右移动的时候我们通过叉乘的方式获得要移动方向的方向向量然后也是做相应的加减操作。 需要注意的是我们在代码中对叉乘的结果进行了归一化处理如果不这样做的话因为我们叉乘得到的向量的模可能不一致可能造成按一下键移动的距离不一样的效果这不是我们想要的。 如果我们完成上面讲的相关代码我们应该得到的效果应该是下面这个样子额没法呈现出来。下图是我按了几下w键的效果 就是键盘上的WASD分别代表向前向左向后向右如果按下这些键的话可以看到可见场景内的所有对象向前向左向后向右。 在初步试验了摄像机系统你可能已经注意到了我们不能同时在两个方向上移动也就是在对角线上移动摄像机而且当我们按下某个方向上的按键的时候画面会“迟疑”一下然后才会按照应该移动的方向进行移动好奇怪有没有。原因是大多数的事件输入系统每次只能处理一个按键并且它们的函数也只能在我们激活一个按键的时候才会被调用。我们的系统虽然也是这样但是我们可以通过一些小技巧来克服这个缺点。 这个技巧的原理是我们在按键响应函数中只对按键的按下和释放做处理。在game loop中我们检查哪一个按键被按下/释放了并且完成相应的动作。所以我们需要存储哪一个按键被按下或者释放的状态信息并且在游戏循环中对这些状态进行反应。首先让我们来创建一个bool类型的数组来保存所有可能按键的状态信息 bool keys[1024]; 然后我们需要在按键响应函数key_callback中根据按键的情况来动态改变这些值 if(action GLFW_PRESS)keys[key] true; else if(action GLFW_RELEASE)keys[key] false; 还有就是要创建响应函数当我们检查到键的状态改变的时候做出相应的动作 void do_movement() {// Camera controlsGLfloat cameraSpeed 0.01f;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; } 最后我们在game loop中调用我们上面实现的两个函数来完成之前只用一个函数完成的功能 while(!glfwWindowShouldClose(window)) {// Check and call eventsglfwPollEvents();do_movement(); // Render stuff... } 现在试试吧我们应该能够同时在两个方向上移动了并且也应该没有”迟疑“了吧。 移动速度 Movement speed 目前看上去我们设置了一个恒定的值来作为我们移动的速度在理论上来看这是正确的但是在实际上人们的处理器是不相同的这就造成了了有些人能够在一秒内绘制很多帧而有些人却只能绘制比较少的帧每秒内绘制的帧的数量叫做帧率。当帧率比较大的时候那么对do_movement的调用次数也就会变多最终造成的是性能较好的机器能够产生的动作要多于或者快于性能较差的机器。这显然不是我们希望的我们希望我们的程序能够在任何配置的机器上都能够对相同的动作产生相同的响应。 我们通过下面的方法来保证我们的程序在不同性能机器上都能产生相同的体验效果  主要的原理是对每帧中产生的动作的速度和帧渲染时间进行同步关联。那么移动速度是以帧处理时间为参照的多以并不会受到机器性能的影响了。 我们记录下面这两个量作为程序的全局变量 GLfloat deltaTime 0.0f; // 上一帧开始和当前帧开始的时间间隔 GLfloat lastFrame 0.0f; // 上一帧开始时刻 在每帧内我们利用上面这两个量来计算新的deltaTime供当前帧使用。 GLfloat currentFrame glfwGetTime(); deltaTime currentFrame - lastFrame; lastFrame currentFrame; 现在我们可以利用deltatime来计算渲染速度了 void Do_Movement() {GLfloat cameraSpeed 5.0f * deltaTime;... } 当我们上面关于移动速度的代码加上后得到的应该就是能够明显感觉比较平滑的效果了。我修改的代码main.cpp。 环视 Look around 只用键盘进行四个方向上的移动并不是那么有趣尤其是我们不能够让我们环顾类似于我们是地球环绕太阳一周那样场景而只能够在某个方向上平移是时候加入鼠标来达到我们想要的这个效果了 为了能够环顾我们的场景我们需要根据鼠标的输入来改变摄像机的cameraFront向量。但是这并不是十分简单的因为要达到较好的效果其中需要三角函数相关的计算。 欧拉角度 Euler angles 欧拉角的基本思想是将角位移分解为绕三个互相垂直轴的三个旋转组成的序列。这听起来复杂其实它是非常直观的。之所以有“角位移”的说法正是因为欧拉角能用来描述任意旋转但最有意义的是使用笛卡尔坐标系并按照一定顺序所组成的旋转序列。最常用的约定即所谓“heading-pitch-bank”约定。在这个系统中一个方位被定义为一个heading角一个pitch角和一个bank角。它的基本思想就是让物体开始于“标准”方位——就是物体坐标轴和惯性坐标轴对齐。在标准方位上让物体作heading,pitch,bank旋转最后物体到达我们想要描述的方位。 欧拉角由三个可以表示三维中任意旋转角度的部分组成是由Leonhard Euler在1700s左右定义的。这三个欧拉角度分别是pitchyaw和roll。下图分别直观展示了这三个量 第一个图中显示的是pitch它代表了我们向上或者向下看的角度。第二个图显示的是yaw值它代表了我们向左或者向右看的大小。第三个量是roll代表了我们的转动量通常在航天飞机摄像机中使用。每一个欧拉角都是一个数量值它们三个进行组合就能够表示三维空间中的任何一个转动角度。 在我们的摄像机系统中我们只关心yaw和pitch值所以我们不会讨论roll值的改变。基于任何给定的pitch和yaw值我们可以将它们转换到三维空间中得到一个方向向量。这个转换过程需要相应的三角函数的的知识 如果我们定义直角三角形的斜边为1三角函数变得简单cos x/hcos x/1cos xsin y/hsin y/1sin y。利用这两个公式我们可以根据已知的角度值得到x和y方向的长度。我们下面就是要用这种方法来计算我们方向向量的分量。 我们的目标是把欧拉角转换成我们的三维坐标。实际上看懂了下面这张图就可以了 上图中斜边是1pitch和yaw已经给出那么pitch和yaw以及斜边决定的点的坐标就是如图所示的样子这样我们也就得到了我们的方向向量。它的计算方式可以通过glm的函数来完成 direction.x cos(glm::radians(pitch)) * cos(glm::radians(yaw)); direction.y sin(glm::radians(pitch)); direction.z cos(glm::radians(pitch)) * sin(glm::radians(yaw)); 以上就是我们将给定的欧拉角转换到三维坐标的方式。但是我们要怎样获得欧拉角的值呢 鼠标输入 Mouse input Pitch和yaw值的获取就是通过鼠标啊~鼠标的水平移动改变yaw值垂直移动改变pitch值就是这么简单。我们的想法是保存上一帧鼠标的位置并且在当前帧中我们计算鼠标值的该表。根据其在水平和竖直方向上改变大小来更新我们的pitch值和yaw值。然后也就决定了我们的摄像机的移动。 那么试一下吧~  首先我们需要设置GLFW设置我们的程序能够在鼠标进入我们的程序窗口的时候捕获它并且使它隐藏起来。我们可以通过以下简单的方式来设置 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 在这个函数调用之后我们在程序窗口中移动鼠标的时候它不可见并且不会超出我们的程序窗口。 为了按照上述方式计算鼠标的pitch和yaw值我们需要设置GLFW使其监听鼠标移动事件这和前面我们已经使用的键盘监听函数的实现实际上是差不多的。我们只要先实现一个函数然后把这个函数通过注册函数注册到程序中就可以了。1 void mouse_callback(GLFWwindow* window, double xpos, double ypos); 其中 xpos和ypos表示鼠标的当前位置。一旦我们注册了这个函数这个函数就会在鼠标移动的时候被调用。 glfwSetCursorPosCallback(window, mouse_callback); 上面的过程完成了鼠标数据的输入。接下来还有一些步骤要做 计算鼠标从上一帧到这一帧的位移根据位移更新摄像机的yaw和pitch值限定yaw和pitch的最大最小值计算方向向量 为了完成步骤1我们设置了两个坐标值用于表示坐标在程序窗口的位置实际上是一个二维坐标初始化为窗口中心的位置。 GLfloat lastX 400, lastY 300; 然后在鼠标的回调函数中我们来计算鼠标两帧之间的位移 GLfloat xoffset xpos - lastX; GLfloat yoffset lastY - ypos; // Reversed since y-coordinates range from bottom to top lastX xpos; lastY ypos;GLfloat sensitivity 0.05f; xoffset * sensitivity; yoffset * sensitivity; 需要注意的是我们对位移量做了一个灵敏度加权的操作如果我们不使用这个权值的话鼠标的移动可能会太大这不是我们想要的效果。我们可以根据程序的实际运行结果对这个灵敏度权值进行调整。 接下来我们将偏移量更新到欧拉角pitch和yaw上 yaw xoffset; pitch yoffset; 下面我们想要对这个摄像机系统加上必要的限制以防止用户得到奇怪的坐标值和奇怪的效果。下面设置的限定是向上看不能看到大于89度或者小于-89度的情况就是不能把脖子仰断或者头向下看到屁股 if(pitch 89.0f)pitch 89.0f; if(pitch -89.0f)pitch -89.0f; 我们并没有对yaw值进行限制如果加上也是十分方便的。原理同上。 最后一步是根据yaw和pitch计算作用后的方向向量 glm::vec3 front; front.x cos(glm::radians(pitch)) * cos(glm::radians(yaw)); front.y sin(glm::radians(pitch)); front.z cos(glm::radians(pitch)) * sin(glm::radians(yaw)); cameraFront glm::normalize(front); 最后得到的结果存放在cameraFront变量中这是一个向量我们在前面已经在函数中进行调用传参。 如果直接运行上面的代码我们得到的结果是在每次鼠标最开始进入到我们程序的窗口的时候我们窗口中的场景会有一个比较大的跳跃。产生这种现象的原因是我们将鼠标的位置初始化为我们窗口的中心点了还记得前面的初始化吧但是我们鼠标进入到窗口的时候一般不会恰好是窗口的中心店这样就会造成在第一次计算偏移量的时候的一大步跳跃。改进的方法就是将鼠标最开始进入到我们程序窗口的坐标作为鼠标的初始坐标 if(firstMouse) // this bool variable is initially set to true {lastX xpos;lastY ypos;firstMouse false; } 最终的鼠标响应函数为 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;GLfloat sensitivity 0.05;xoffset * sensitivity;yoffset * sensitivity;yaw xoffset;pitch yoffset;if(pitch 89.0f)pitch 89.0f;if(pitch -89.0f)pitch -89.0f;glm::vec3 front;front.x cos(glm::radians(yaw)) * cos(glm::radians(pitch));front.y sin(glm::radians(pitch));front.z sin(glm::radians(yaw)) * cos(glm::radians(pitch));cameraFront glm::normalize(front); } Ok这样就好了用我们的鼠标就可以让我们的场景不断地旋转了代码. 缩放 Zoom 我们还想要为我们的摄像机系统再加入一点缩放功能的接口。前面的教程中我们讲到可以利用fov来定义我们可见程序窗口的尺寸。当视口变小那么投影出来的对象也就变小了看上去就是缩小的效果。我们想要利用鼠标转动滚轮来达到缩放的功能。像上面的键盘函数和鼠标移动函数我们首先定义一个鼠标滚轮滚动的响应函数 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {if(fov 1.0f fov 45.0f)fov - yoffset;if(fov 1.0f)fov 1.0f;if(fov 45.0f)fov 45.0f; } 当滚轮滚动的时候yoffset代表了我们垂直滚动量。我们借用这个值来修改我们全局设定的fov值因为45.0ffov的默认值。我们设定缩放范围是1.0f到45.0f。 我们将fov作为参数传递进我们的透视投影矩阵生成函数中 projection glm::perspective(fov, (GLfloat)WIDTH/(GLfloat)HEIGHT, 0.1f, 100.0f); 最后别忘了将鼠标滚轮函数注册上。 glfwSetScrollCallback(window, scroll_callback); 这样应该就能够实现鼠标滚轮滚动来缩放场景的效果了。 原作者指出利用欧拉角实现摄像机系统还是比较low的他还有更好的方法好吧我原来用的方法还没有欧拉角好。只能先膜拜了好消息是后面他会讲到更好的方法。 摄像机类 Camera class 在后面的教程中我们会经常使用改变摄像机的参数来达到我们想要的效果。但是正像这次教程讲的这整个过程摄像机系统的建立还是比较复杂的。所以原作者封装了一个摄像机类以方便后面的使用。这是代码。 像前面的shader类一样这个摄像机类也是只有一个头文件我们想要使用的时候直接包含这个头文件就好了其中的代码相信完整看过上面步骤的人都能够很轻松看懂。 利用这个摄像机类头文件完成的上面相同效果的整个代码在这儿。
http://www.pierceye.com/news/657412/

相关文章:

  • 网站上传 文件夹结构国内响应式网站
  • 做logo图标的网站自助建站系统网站建设开发
  • 韩国站群服务器网络推广公司网站
  • 网站公司设计公司设计上海展会2021门票
  • 做微网站的公司哪家好刷百度关键词排名优化
  • php网站建设一流程胶南网站建设多少钱
  • 网站开发证书网站推广应该怎么做?
  • 网站规划与网页设计案例网站建设php招聘
  • 网站内容页优化阿里巴巴做网站么
  • 网站百度收录秒收方法网页制作员厂家
  • 免费做网站怎么做网站619去加网 wordpress
  • 网站建设基本资料信息流优化师是干什么的
  • 网站开发项目经理招聘高级网站设计效果图
  • 上海建网站社交型网站开发
  • 西安建网站做自动化设备哪个网站
  • 深圳优化网站关键词wordpress如何拖移小工具
  • 优秀网站设计欣赏国内网站后期
  • 计算机应用技术php网站开发php网站进后台
  • asp网站安全小x导航正品
  • 陕西省建设监理协会网站成绩查询如何用网站做课件
  • 帝国网站怎么仿站个人做旅游网站的意义
  • 网站建设白沟做公众号策划的网站
  • 站长工具怎么用知名做网站哪家好
  • 做视频网站怎么备案企业内网
  • 建设网站南沙区建湖做网站找哪家好
  • 做网站应该会什么问题视频网站做app
  • 南阳做网站费用企业品牌维护
  • 分形科技做网站怎么样网站常用的js效果
  • 做企业展示网站网站建设与制作段考试题
  • 设计网站哪个好用网站建设策划方案t