博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
<五>初探opengl,编写我们的镜头
阅读量:4603 次
发布时间:2019-06-09

本文共 7130 字,大约阅读时间需要 23 分钟。

  现在我们尝试编写一个镜头类,有了这个类,我们能上下左右前后移动,感觉在玩fps游戏,很不错,下面开始看看怎么写。

 

  初次接触镜头类是我在魔兽地图编辑中,当时创建一个镜头的步骤就是放到某个位置,调节角度,分别有3个角度可以调节,一个是类似高度一样的东西,一个是环绕着某个点的旋转角度,还有就是镜头的旋转。opengl镜头其实跟这个是差不多的。

  1.首先我们需要定一个摄像机位置 ,也就是把摄像机放到什么位置去

  2.然后我们要定一个目标位置,这个决定我们摄像机观察的方向

  3.然后就是一个上向量,这个一般为(0,1,0)

  从这几个变量,我们可以获得摄像机的右轴,我们用摄像机方向叉乘上向量,就能得到一条垂直于摄像机方向与上向量构成的一个平面的法向量。有了这条轴,我们可以左右平移摄像机。还能得到计算机的上轴 ,就方向和右轴叉乘下就可以得到了,用来上下移动。

  接下来我们可以使用函数lookAt来建构这个观察模型

glm::mat4 view;view = glm::lookAt(vec3Pos, vec3Target, vec3Up); //提供摄像机位置,摄像机目标,上向量,他会根据上面的方法得出其他东西,构建矩阵

这样作为视图转换中的观察矩阵穿进去就好。貌似忘了记录这个:

转换过程如上,模型变换->视图变换->投射变换->裁剪。

 

首先我们写一段代码,创建一个立方体

float vertices[] = {        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,   // 右上        0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,   // 右下        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,   // 左下        -0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,    // 左上        0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,   // 右上        0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,   // 右下        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,   // 左下        -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f    // 左上    };    unsigned int indices[] = {        3, 2, 1, 0, 1, 3,        7, 6, 5, 4, 5, 7,        0, 1, 4, 1, 4, 5,        0, 3, 4, 3, 4, 7,        2, 3, 6, 3, 6, 7,        1, 2, 6, 1, 5, 6,    };    glGenVertexArrays(1, &vao);    glBindVertexArray(vao);    glGenBuffers(1, &vbo);    glBindBuffer(GL_ARRAY_BUFFER, vbo);    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);    glGenBuffers(1, &ebo);    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);    //链接顶点属性    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);    glEnableVertexAttribArray(0);    //颜色链接    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));    glEnableVertexAttribArray(1);    glEnable(GL_DEPTH_TEST);

我们定义了立方体的8个顶点和具体面的索引,其他按照之前做法来就可以了,最后是开启深度测试,不然面都画到前面来,没有了层次就不像立方体了。

绘图代码:

glClearColor(0, 0, 0, 0);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

现在应该立方体画出来了,这样还是看不出他是立方体,我们需要转换一下观察的位置。

     glm::mat4 model = glm::mat4(1.0f);        model = glm::rotate(model, glm::radians(0.0f), glm::vec3(0.5f, 1.0f, 0.0f)); //模型矩阵,可以实现模型基本的操作,例如缩放,位移等        model = glm::translate(model, cubePositions[i]);        glm::mat4 view = glm::mat4(1.0f);        view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); //观察矩阵,用来调整观察的视野        glm::mat4 projection = glm::mat4(1.0f);  //投影矩阵,分为正射投影和透视投影。2d一般正射,3d一般透视投影,这里使用了透视投影,营造3d效果        projection = glm::perspective(glm::radians(fov), (float)800 / 600, 0.1f, 100.0f); //透视投影矩阵生成,fov为镜头的开口角度,     //创建位置,目标,上向量        glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);         glm::vec3 target = glm::vec3(0, 0, 0);        glm::vec3 cameraDir = glm::normalize(cameraPos - target);        glm::vec3 up = glm::vec3(0, 1, 0);        glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDir));        glm::vec3 cameraUp = glm::cross(cameraDir, cameraRight);        float radius = 10.0f;        float camX = sin(glfwGetTime())*radius;        float camZ = cos(glfwGetTime())*radius;        //view = glm::lookAt(glm::vec3(camX, 5, camZ), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));        //view = glm::lookAt(cameraPos, target, up);        view = glm::lookAt(pos, pos + front, up); //创建观察矩阵,传入了位置,目标,和上向量,这里的目标恒定为摄像机前面的一点,由front控制       //给着色器传入几个矩阵,供给他们计算        unsigned int transformLoc = glGetUniformLocation(shader->ID, "model");        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(model));        unsigned int transformLoc2 = glGetUniformLocation(shader->ID, "view");        glUniformMatrix4fv(transformLoc2, 1, GL_FALSE, glm::value_ptr(view));        unsigned int transformLoc3 = glGetUniformLocation(shader->ID, "projection");        glUniformMatrix4fv(transformLoc3, 1, GL_FALSE, glm::value_ptr(projection));      glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

因为懒惰,我们没有写这些进阶过来的过程,具体可以看一下opengl教程

几个矩阵分别控制的东西我们已经知道了,接下来就是顶点着色器的编写。

#version 330 corelayout(location = 0) in vec3 aPos;layout(location = 1) in vec3 aColor;out vec4 vertexColor;uniform mat4 model;uniform mat4 view;uniform mat4 projection;void main(){    gl_Position = projection * view*model* vec4(aPos, 1.0);    vertexColor = vec4(aColor, 1);}

可以看到,我们从代码里已经传递进来的3个矩阵,通过一定的顺序来相乘来进行转换

我们能看到一个类似的效果,立方体的观察,随着你的调整会从不同角度的观察。

 

  此时,我们要加入点更加游戏的元素,就是使用键盘鼠标来操作镜头移动。

  我们在主循环里加入一个键盘操作处理函数的调用,然后编写我们的方法

while (!glfwWindowShouldClose(window)){    handleInput(window);    draw();    glfwSwapBuffers(window);    glfwPollEvents();}
void Camera::handleInput(GLFWwindow* window){    float speed = 0.005f;    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)        pos += speed * front;    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)        pos -= speed * front;    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)        pos -= glm::normalize(glm::cross(front, up)) * speed;    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)        pos += glm::normalize(glm::cross(front, up)) * speed;    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)        glfwSetWindowShouldClose(window, GL_TRUE);}

pos是我们摄像机的位置,front是表示一个方向,让我们镜头保持看向某一个方向,当我们前后移动,我们和front处理,当我们需要左右移动,我们求出右轴后左右变换即可。

然后我们需要修改我们lookAt参数,使其适配移动

view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); //cameraPos+cameraFront让镜头目标一致处理front方向上

现在,应该可以进行前后左右的移动了。此刻,我们还要加上鼠标的操作,就完美了。

 

  首先我们得了解一个叫欧拉角的东西,这个是可以表示3d空间中任何旋转的3个值,分别是俯视角(pitch),偏航角(yaw)和滚转角(roll)

用这三个角,我们就能控制我们的镜头各种移动了,我们先看看着3个角怎么实现操作

俯视角pitch是镜头方向和xz平面构成的夹角,假设我们距离原点为1,那y的高度就是sin pitch,x和z是cos pitch。

偏航角yaw是方向偏离x轴的角度,可见z为sin yaw, x为是cos yaw

所以根据上面所看,当我们知道pitch和yaw角度后,即可计算出一个方向向量

direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); // 译注:direction代表摄像机的前轴(Front),这个前轴是和本文第一幅图片的第二个摄像机的方向向量是相反的direction.y = sin(glm::radians(pitch));direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));

这几个角度,我们可以通过鼠标的操作来获取。

首先我们做一些设置

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetCursorPosCallback(window, mouse_callback);

设置glfwSetInputMode,鼠标就会锁定在窗口里,鼠标也看不到了,这时候关闭窗口就很麻烦,我在键盘操作里加个了关闭热键。然后添加鼠标操作的回调

void mouse_callback(GLFWwindow* window, double xpos, double ypos){    if(firstMouse) //为上次鼠标点设置默认值    {        lastX = xpos;        lastY = ypos;        firstMouse = false;    }    float xoffset = xpos - lastX; //x方向的偏移    float yoffset = lastY - ypos; //y方向偏移,因为是鼠标向下移动,鼠标位置越大    lastX = xpos;    lastY = ypos;    float sensitivity = 0.05; //灵敏度    xoffset *= sensitivity;    yoffset *= sensitivity;    yaw   += xoffset;    pitch += yoffset;    if(pitch > 89.0f) //这里俯视角不能高于89度,否则视角会发生逆转        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);}

这里就能计算出摄像机新的朝向角度,配合上wads的移动,我们就能实现看起来像3d游戏里的自由移动了。

 

那么入门级别的教程笔记就写到这里了。

转载于:https://www.cnblogs.com/usp10/p/9304181.html

你可能感兴趣的文章
在c++中,静态数据成员可以被非静态成员函数调用吗?如果可以调用的话那为什么还要定义静态成员函数呢...
查看>>
查看Sql Server所有表占用的空间大小
查看>>
CSS重置(css reset)【转载】
查看>>
Elasticserach 配置文件详解
查看>>
【案例】大型摩托制造企业如何高效排产?看APS系统如何帮忙
查看>>
NTCIR-13 We Want Web 任务概述
查看>>
模版include的用法
查看>>
LotusScript_导出数据库路径和名称
查看>>
String ,StringBuffer 与S tringBuilder的区别??
查看>>
PgSQL · 追根究底 · WAL日志空间的意外增长
查看>>
struts2笔记之struts:property标签
查看>>
Threejs.教程
查看>>
超闩锁和子闩锁如何工作的
查看>>
ZendStudio快捷键
查看>>
[ovs] ovs开启日志debug
查看>>
Eclipse插件项目中读取文件
查看>>
jquery定义链接跳转的高亮显示
查看>>
CheckListBox怎样得到多选值?
查看>>
2370 小机房的树
查看>>
三道题(关于虚表指针位置/合成64位ID/利用栈实现四则运算)
查看>>