当前位置:   article > 正文

Qt+OpenGL实现3D效果_opengl效果

opengl效果

坐标系

为了将坐标从一个坐标系变换到另一个坐标系,需要用到几个变换矩阵,最重要的几个分别是模型(Model)、观察(View)、投影(Projection)三个矩阵。顶点坐标起始于局部空间(Local Space),在这里它称为局部坐标(Local Coordinate),它在之后会变为世界坐标(World Coordinate),观察坐标(View Coordinate),裁剪坐标(Clip Coordinate),并最后以屏幕坐标(Screen Coordinate)的形式结束。下面的这张图展示了整个流程以及各个变换过程做了什么:

正交投影

正交投影矩阵定义了一个类似立方体的平截头箱,它定义了一个裁剪空间,在这空间之外的顶点都会被裁剪掉。创建一个正交投影矩阵需要指定可见平截头体的宽、高和长度。在使用正交投影矩阵变换至裁剪空间之后处于这个平截头体内的所有坐标将不会被裁剪掉。它的平截头体看起来像一个容器:

上面的平截头体定义了可见的坐标,它由宽、高、近(Near)平面和远(Far)平面所指定。任何出现在近平面之前或远平面之后的坐标都会被裁剪掉。正交平截头体直接将平截头体内部的所有坐标映射为标准化设备坐标,因为每个向量的w分量都没有进行改变;如果w分量等于1.0,透视除法则不会改变这个坐标。

正交投影矩阵直接将坐标映射到2D平面中,即你的屏幕,但实际上一个直接的投影矩阵会产生不真实的结果,因为这个投影没有将透视(Perspective)考虑进去。所以需要透视投影矩阵来解决这个问题。

透视投影

观察实际生活的景象,会注意到离的越远的东西看起来更小。这个奇怪的效果称之为透视(Perspective)。透视的效果看一条无限长的高速公路或铁路时尤其明显,正如下面图片显示的那样:

正如看到的那样,由于透视,这两条线在很远的地方看起来会相交。这正是透视投影想要模仿的效果,它是使用透视投影矩阵来完成的。这个投影矩阵将给定的平截头体范围映射到裁剪空间,除此之外还修改了每个顶点坐标的w值,从而使得离观察者越远的顶点坐标w分量越大。被变换到裁剪空间的坐标都会在-w到w的范围之间(任何大于这个范围的坐标都会被裁剪掉)。OpenGL要求所有可见的坐标都落在-1.0到1.0范围内,作为顶点着色器最后的输出。

  1. QMatrix4x4 projection;
  2. projection.perspective(45,(float )width()/height(),0.1,100);

.perspective所做的其实就是创建了一个定义了可视空间的大平截头体,任何在这个平截头体以外的东西最后都不会出现在裁剪空间体积内,并且将会受到裁剪。一个透视平截头体可以被看作一个不均匀形状的箱子,在这个箱子内部的每个坐标都会被映射到裁剪空间上的一个点。下面是一张透视平截头体的图片:

右手坐标系(Right-handed System)

按照惯例,OpenGL是一个右手坐标系。简单来说,就是正x轴在你的右手边,正y轴朝上,而正z轴是朝向后方的。想象你的屏幕处于三个轴的中心,则正z轴穿过你的屏幕朝向你。坐标系画起来如下:

为了理解为什么被称为右手坐标系,按如下的步骤做:

  • 沿着正y轴方向伸出你的右臂,手指着上方。
  • 大拇指指向右方。
  • 食指指向上方。
  • 中指向下弯曲90度。

如果动作正确,那么大拇指指向正x轴方向,食指指向正y轴方向,中指指向正z轴方向。如果用左臂来做这些动作,会发现z轴的方向是相反的。这个叫做左手坐标系,它被DirectX广泛地使用。注意在标准化设备坐标系中OpenGL实际上使用的是左手坐标系(投影矩阵交换了左右手)。

Z缓冲

OpenGL存储它的所有深度信息于一个Z缓冲(Z-buffer)中,也被称为深度缓冲(Depth Buffer)。GLFW会自动为你生成这样一个缓冲(就像它也有一个颜色缓冲来存储输出图像的颜色)。深度值存储在每个片段里面(作为片段的z值),当片段想要输出它的颜色时,OpenGL会将它的深度值和z缓冲进行比较,如果当前的片段在其它片段之后,它将会被丢弃,否则将会覆盖。这个过程称为深度测试(Depth Testing),它是由OpenGL自动完成的。

然而,如果想要确定OpenGL真的执行了深度测试,首先要告诉OpenGL想要启用深度测试;它默认是关闭的。可以通过glEnable函数来开启深度测试。glEnable和glDisable函数允许启用或禁用某个OpenGL功能。这个功能会一直保持启用/禁用状态,直到另一个调用来禁用/启用它。现在我们想启用深度测试,需要开启GL_DEPTH_TEST:

    glEnable(GL_DEPTH_TEST);

因为使用了深度测试,也想要在每次渲染迭代之前清除深度缓冲(否则前一帧的深度信息仍然保存在缓冲中)。就像清除颜色缓冲一样,可以通过在glClear函数中指定DEPTH_BUFFER_BIT位来清除深度缓冲:

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

先确定各个箱子的顶点坐标

  1. float vertices_data[180]{
  2. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
  3. 0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
  4. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  5. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  6. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
  7. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
  8. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  9. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
  10. 0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
  11. 0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
  12. -0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
  13. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  14. -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  15. -0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  16. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  17. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  18. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  19. -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  20. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  21. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  22. 0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  23. 0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  24. 0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  25. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  26. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  27. 0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
  28. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
  29. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
  30. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
  31. -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
  32. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
  33. 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
  34. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  35. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
  36. -0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
  37. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
  38. };
  39. QVector<QVector3D> Positions = {
  40. QVector3D(0.0f,0.0f,-0.5f),
  41. QVector3D(1.0f,4.0f,-10.0f),
  42. QVector3D(-2.6f,0.2f,-5.3f),
  43. QVector3D(-2.6f,-1.5f,-2.0f),
  44. QVector3D(1.5f,1.8f,-3.0f),
  45. QVector3D(2.5f,-0.5f,-3.0f),
  46. QVector3D(-1.2f,1.0f,-4.0f),
  47. };

设置顶点着色器:

  1. #version 330 core
  2. layout(location = 0) in vec3 aPos;
  3. layout(location = 1) in vec2 aTexCord;
  4. out vec2 TexCord;
  5. uniform mat4 model;
  6. uniform mat4 view;
  7. uniform mat4 projection;
  8. void main()
  9. {
  10. gl_Position =projection * view * model * vec4(aPos.x, aPos.y, aPos.z, 1.0f);
  11. TexCord=aTexCord;
  12. }

绘制多个3D箱子:

  1. QMatrix4x4 model;
  2. QMatrix4x4 view;
  3. unsigned int time=QTime::currentTime().msec();
  4. view.translate(0.0,0.0,-4);
  5. QMatrix4x4 projection;
  6. projection.perspective(45,(float )width()/height(),0.1,100);
  7. // 2.initializeOpenGLFunctions();执行后,下面的函数才有执行的意义
  8. // 设置窗口颜色
  9. glClearColor(0.5f, 0.5f, 0.5f, 0.1f);
  10. glEnable(GL_DEPTH_TEST);
  11. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  12. shaderProgram.bind();
  13. // 【画一个图形时需要说使用哪个着色器】
  14. // 使用时还需要再绑定一次
  15. glBindVertexArray(VAO_id);
  16. // 开始绘制
  17. //glDrawArrays(GL_TRIANGLES, 0, 36);
  18. textureWall->bind(0);
  19. shaderProgram.setUniformValue("view",view);
  20. shaderProgram.setUniformValue("projection",projection);
  21. foreach (auto item, Positions) {
  22. //重置为默认值
  23. model.setToIdentity();
  24. model.translate(item);
  25. model.rotate(time,1.0f,5.0f,0.5f);
  26. shaderProgram.setUniformValue("model",model);
  27. glDrawArrays(GL_TRIANGLES, 0, 36);
  28. };

实际运行结果如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/976796
推荐阅读
相关标签
  

闽ICP备14008679号