OpenGL(十三)——Qt OpenGL绘制三维图形

一、二维图形

之前的文章介绍的都是二维的图形。在旋转的多面体那篇文章中绘制的也是一个三维的图形,本篇介绍三维的图形绘制。

二、三维图形

在之前VS2010版本下介绍过三维图形的绘制,见OpenGL(六)——创建三维图形,在这篇文章提到了绘制三维的大致步骤:

3、可能有点绕,没关系,至少你记住下面这几个流程:

OpenGL渲染3D物体到屏幕上的过程其实类似我们平时用照相机拍照的过程,这个步骤大致如下:
一、把照相机固定在三脚架并让它对准场景(视图变换)

二、把场景中的物体调整摆放好(模型变换)

三、选择照相机的镜头,并调整放大倍数(投影变换)

四、确定最终照片的大小(视口变换)

其中视图变换必须要在模型变换之前,其它可以在任何时候。

其实三维的绘制,就是二维图形的叠加,以正四面体为例,正四面体就是六个正方形,在一起绘制,叠加在了一起就是一个正方体(正四面体)。

三、代码实现

在上一篇的内容上作些扩展,我们现在开始生成真正的三维对象,而不是象前两节课中那样在三维世界中的二维对象。
我们给三角形增加一个左侧面,一个右侧面,一个后侧面来生成一个金字塔(四棱锥)。
给正方形增加左、右、上、下及背面生成一个立方体。
我们混合金字塔上的颜色,创建一个平滑着色的对象。给立方体的每一面则来个不同的颜色。
其实只需在上节课的代码上增加几行就可以了。

现在把代码贴上:

头文件:

#include <QObject>
#include <QWidget>
#include <qgl.h>/*
*绘制三维.
*/class NeHe_5_Widget : public QGLWidget
{Q_OBJECT
public:NeHe_5_Widget(QWidget *parent = 0);~NeHe_5_Widget();protected:void initializeGL(); void paintGL();     void resizeGL( int width, int height ); private:GLfloat rTri;GLfloat rQuad;
};

cpp文件:

#include "nehe_5_widget.h"#include <GL/glu.h>NeHe_5_Widget::NeHe_5_Widget(QWidget *parent):QGLWidget(parent)
{setGeometry(0,0, 640, 480);rTri = 0.0;rQuad = 0.0;
}NeHe_5_Widget::~NeHe_5_Widget()
{}void NeHe_5_Widget::initializeGL()
{glShadeModel( GL_SMOOTH );glClearColor( 0.0, 0.0, 0.0, 0.0 );glClearDepth( 1.0 );//设置深度缓存。glEnable( GL_DEPTH_TEST );//启用深度测试。glDepthFunc( GL_LEQUAL );//所作深度测试的类型。//上面这三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。\我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将\一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );}
void NeHe_5_Widget::resizeGL( int width, int height )
{if ( height == 0 ){height = 1;}//防止height为0。glViewport( 0, 0, (GLint)width, (GLint)height );//重置当前的视口(Viewport)。glMatrixMode( GL_PROJECTION );//选择投影矩阵。glLoadIdentity();//重置投影矩阵。gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0 );//建立透视投影矩阵。glMatrixMode( GL_MODELVIEW );//选择模型观察矩阵。glLoadIdentity();//重置模型观察矩阵。}void NeHe_5_Widget::paintGL()
{glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );//清楚屏幕和深度缓存。glLoadIdentity();//重置当前的模型观察矩阵。glTranslatef( -1.5,  0.0, -6.0 );glRotatef( rTri, 0.0, 1.0, 0.0);//! 有些人可能早已在上节课中的代码上尝试自行创建3D对象了。但经常有人来信问我:“我的对象怎么不会绕着其自身的轴旋转?看起来总是在满屏乱转。”\//! 要让您的对象绕自身的轴旋转,您必须让对象的中心坐标总是( 0.0, 0,0, 0,0 )。//! 下面的代码创建一个绕者其中心轴旋转的金字塔。金字塔的上顶点离中心一个单位,底面离中心也是一个单位。上顶点在底面的投影位于底面的中心。//! 注意所有的面-三角形都是逆时针次序绘制的。这点十分重要,在以后的课程中我会作出解释。现在,您只需明白要么都逆时针,要么都顺时针,\//! 但永远不要将两种次序混在一起,除非您有足够的理由必须这么做。glBegin( GL_TRIANGLES );glColor3f( 1.0, 0.0, 0.0 );glVertex3f(  0.0,  1.0,  0.0 ); //前侧面.glColor3f( 0.0, 1.0, 0.0 );glVertex3f( -1.0, -1.0,  1.0 );glColor3f( 0.0, 0.0, 1.0 );glVertex3f(  1.0, -1.0,  1.0 );//上面是我们绘制的金字塔的前侧面。因为所有的面都共享上顶点,我们将这点在所有的三角形中都设置为红色。底边上的两个顶点的颜色则是互斥的。\前侧面的左下顶点是绿色的,右下顶点是蓝色的。这样相邻右侧面的左下顶点是蓝色的,右下顶点是绿色的。这样四边形的底面上的点的颜色都是间隔排列的。\//还应注意到后面的三个侧面和前侧面处于同一个glBegin( GL_TRIANGLES )和glEnd()语句中间。因为我们是通过三角形来构造这个金字塔的。\OpenGL知道每三个点构成一个三角形。当它画完一个三角形之后,如果还有余下的点出现,它就以为新的三角形要开始绘制了。OpenGL在这里并不会将\四点画成一个四边形,而是假定新的三角形开始了。所以千万不要无意中增加任何多余的点。glColor3f( 1.0, 0.0, 0.0 );glVertex3f(  0.0,  1.0,  0.0 ); //右侧面.glColor3f( 0.0, 0.0, 1.0 );glVertex3f(  1.0, -1.0,  1.0 );glColor3f( 0.0, 1.0, 0.0 );glVertex3f(  1.0, -1.0, -1.0 );//绘制右侧面。注意其底边上的两个顶点的X坐标位于中心右侧的一个单位处。顶点则位于Y轴上的一单位处,且Z坐标正好处于底边的两顶点的Z坐标中心。\右侧面从上顶点开始向外侧倾斜至底边上。//这次的左下顶点用蓝色绘制,以保持与前侧面的右下顶点的一致。蓝色将从这个角向金字塔的前侧面和右侧面扩展并与其他颜色混合。glColor3f( 1.0, 0.0, 0.0 );glVertex3f(  0.0,  1.0,  0.0 );glColor3f( 0.0, 1.0, 0.0 );glVertex3f(  1.0, -1.0, -1.0 ); //后侧面glColor3f( 0.0, 0.0, 1.0 );glVertex3f( -1.0, -1.0, -1.0 );//后侧面。再次切换颜色。左下顶点又回到绿色,因为后侧面与右侧面共享这个角。glColor3f( 1.0, 0.0, 0.0 );glVertex3f(  0.0,  1.0,  0.0 ); //左侧面.glColor3f( 0.0, 0.0, 1.0 );glVertex3f( -1.0, -1.0, -1.0 );glColor3f( 0.0, 1.0, 0.0 );glVertex3f( -1.0, -1.0,  1.0 );//最后画左侧面。又要切换颜色。左下顶点是蓝色,与后侧面的右下顶点相同。右下顶点是蓝色,与前侧面的左下顶点相同。//到这里金字塔就画完了。因为金字塔只绕着Y轴旋转,我们永远都看不见底面,因而没有必要添加底面。如果您觉得有经验了,\尝试增加底面(正方形),并将金字塔绕X轴旋转来看看您是否作对了。确保底面四个顶点的颜色与侧面的颜色相匹配。glEnd();//接下来开始画立方体。他由六个四边形组成。所有的四边形都以逆时针次序绘制。就是说先画右上角,然后左上角、左下角、最后右下角。\您也许认为画立方体的背面的时候这个次序看起来好像顺时针,但别忘了我们从立方体的背后看背面的时候,\与您现在所想的正好相反。(译者注:您是从立方体的外面来观察立方体的。)glLoadIdentity();glTranslatef(  1.5,  0.0, -7.0 );//注意到这次我们将立方体移地更远离屏幕了。因为立方体的大小要比金字塔大,同样移入6个单位时,立方体看起来要大的多。这是透视的缘故。越远的对象看起来越小 :) 。glRotatef( rQuad,  1.0,  1.0,  1.0 );//旋转四边形。glBegin( GL_QUADS );glColor3f( 0.0, 1.0, 0.0 );glVertex3f(  1.0,  1.0, -1.0 );glVertex3f( -1.0,  1.0, -1.0 );glVertex3f( -1.0,  1.0,  1.0 );glVertex3f(  1.0,  1.0,  1.0 );//先画立方体的顶面。从中心上移一单位,注意Y坐标始终为一单位,表示这个四边形与Z轴平行。先画右上顶点,向右一单位,再屏幕向里一单位。\然后左上顶点,向左一单位,再屏幕向里一单位。然后是靠近观察者的左下和右下顶点。就是屏幕往外一单位。glColor3f( 1.0, 0.5, 0.0 );glVertex3f(  1.0, -1.0,  1.0 );glVertex3f( -1.0, -1.0,  1.0 );glVertex3f( -1.0, -1.0, -1.0 );glVertex3f(  1.0, -1.0, -1.0 );//底面的画法和顶面十分类似。只是Y坐标变成了-1。如果我们从立方体的下面来看立方体的话,您会注意到右上角离观察者最近,\因此我们先画离观察者最近的顶点。然后是左上顶点最后才是屏幕里面的左下和右下顶点。//如果您真的不在乎绘制多边形的次序(顺时针或者逆时针)的话,您可以直接拷贝顶面的代码,将Y坐标从1改成-1,也能够工作。\但一旦您进入象纹理映射这样的领域时,忽略绘制次序会导致十分怪异的结果。glColor3f( 1.0, 0.0, 0.0 );glVertex3f(  1.0,  1.0,  1.0 );glVertex3f( -1.0,  1.0,  1.0 );glVertex3f( -1.0, -1.0,  1.0 );glVertex3f(  1.0, -1.0,  1.0 );//立方体的前面。保持Z坐标为一单位,前面正对着我们。glColor3f( 1.0, 1.0, 0.0 );glVertex3f(  1.0, -1.0, -1.0 );glVertex3f( -1.0, -1.0, -1.0 );glVertex3f( -1.0,  1.0, -1.0 );glVertex3f(  1.0,  1.0, -1.0 );//立方体后面的绘制方法与前面类似。只是位于屏幕的里面。注意Z坐标现在保持-1不变。glColor3f( 0.0, 0.0, 1.0 );glVertex3f( -1.0,  1.0,  1.0 );glVertex3f( -1.0,  1.0, -1.0 );glVertex3f( -1.0, -1.0, -1.0 );glVertex3f( -1.0, -1.0,  1.0 );//还剩两个面就完成了。您会注意到总有一个坐标保持不变。这一次换成了X坐标。因为我们在画左侧面。glColor3f( 1.0, 0.0, 1.0 );glVertex3f(  1.0,  1.0, -1.0 );glVertex3f(  1.0,  1.0,  1.0 );glVertex3f(  1.0, -1.0,  1.0 );glVertex3f(  1.0, -1.0, -1.0 );//立方体的最后一个面了。X坐标保持为一单位。逆时针绘制。您愿意的话,留着这个面不画也可以,这样就是一个盒子:)glEnd();glTranslatef(  -1.5,  0.0,  0.0 );glColor3f( 1.0, 1.0, 0.0 );glBegin( GL_LINES );glVertex3f(0.0, 1.0, 0.0);glVertex3f(0.0, -1.0, 0.0);glEnd();rTri += 0.2;rQuad -= 0.15;}

到这里您应该已经较好的掌握了在三维空间创建对象的方法。必须将OpenGL屏幕想象成一张很大的画纸,后面还带着许多透明的层。

差不多就是个由大量的点组成的立方体。这些点从左至右、从上至下、从前到后的布满了这个立方体。如果您能想象的出在屏幕的深度方向,应该在设计新三维对象时没有任何问题。

注:上面的代码中有这样一句注释,大家一定要注意:

有些人可能早已在上节课中的代码上尝试自行创建3D对象了。但经常有人来信问我:“我的对象怎么不会绕着其自身的轴旋转?看起来总是在满屏乱转。要让您的对象绕自身的轴旋转,您必须让对象的中心坐标总是( 0.0, 0,0, 0,0 )。
下面的代码创建一个绕者其中心轴旋转的金字塔。金字塔的上顶点离中心一个单位,底面离中心也是一个单位。上顶点在底面的投影位于底面的中心。
注意所有的面-三角形都是逆时针次序绘制的。这点十分重要,在以后的课程中我会作出解释。现在,您只需明白要么都逆时针,要么都顺时针,但永远不要将两种次序混在一起,除非您有足够的理由必须这么做。

四、运行

代码写完之后,我们可以看一下运行的效果:

上一篇:OpenGL(十二)——Qt OpenGL绕着坐标轴旋转多边形

下一篇:OpenGL(十四)——Qt OpenGL纹理

本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载。

OpenGL(十三)——Qt OpenGL绘制三维图形相关推荐

  1. Android开发笔记(一百五十三)OpenGL绘制三维图形的流程

    从这篇文章开始,接下来会连载一系列的OpenGL相关博文,好好探讨如何在Android中进行OpenGL开发. OpenGL的全称是"Open Graphics Library", ...

  2. android绘制过程3d图形,Android开发之OpenGL绘制三维图形的流程

    从这篇文章开始,接下来会连载一系列的OpenGL相关博文,好好探讨如何在Android中进行OpenGL开发. OpenGL的全称是"Open Graphics Library", ...

  3. opengl三维图形绘制_Python matplotlib绘图示例 - 绘制三维图形

    Python matplotlib模块是扩展的MATLAB的一个绘图工具库.它可以绘制各种图形,下面就学习了下Python中的matplotlib模块,如何绘制三维图形. 示例代码一: # codin ...

  4. python绘制3d图-Python matplotlib绘图示例 - 绘制三维图形

    Python matplotlib模块是扩展的MATLAB的一个绘图工具库.它可以绘制各种图形,下面就学习了下Python中的matplotlib模块,如何绘制三维图形. 示例代码一: # codin ...

  5. python画图三维-Python使用matplotlib绘制三维图形示例

    本文实例讲述了Python使用matplotlib绘制三维图形.分享给大家供大家参考,具体如下: 用二维泡泡图表示三维数据 泡泡的坐标2维,泡泡的大小三维,使用到的函数 plt.scatter(P[: ...

  6. python绘制3d图形-Python基于matplotlib实现绘制三维图形功能示例

    本文实例讲述了Python基于matplotlib实现绘制三维图形功能.分享给大家供大家参考,具体如下: 代码一: # coding=utf-8 import numpy as np import m ...

  7. python代码示例图形-Python使用matplotlib绘制三维图形示例

    本文实例讲述了Python使用matplotlib绘制三维图形.分享给大家供大家参考,具体如下: 用二维泡泡图表示三维数据 泡泡的坐标2维,泡泡的大小三维,使用到的函数 plt.scatter(P[: ...

  8. python画三维立体图-Python+matplotlib绘制三维图形5个精选案例

    如果要绘制三维图形,首先需要使用下面的语句导入相应的对象: from mpl_toolkits.mplot3d import Axes3D 然后使用下面的两种方式之一声明要创建三维子图: ax = f ...

  9. matlab绘制三维图形现状,MATLAB绘制三维图形

    1.画椭球 1.1 圆心在(0,0,0) [x,y,z]=sphere(30);%30是画出来的球面的经纬分面数...30的话就是30个经度, 30个纬度 surf(x,y,z) 图片.png 1.2 ...

  10. python matplotlib模块——绘制三维图形、三维数据散点图

    2019独角兽企业重金招聘Python工程师标准>>> 分类: 计算机视觉 python matplotlib模块,是扩展的MATLAB的一个绘图工具库.他可以绘制各种图形,可是最近 ...

最新文章

  1. C++11 unordered_map详细介绍
  2. foo、bar、baz三兄弟
  3. prototype中顶层元素的测试
  4. 贪心 Codeforces Round #236 (Div. 2) A. Nuts
  5. Spring 获取 request 的几种方法及其线程安全性分析
  6. h.323 服务器 作用,Cisco CallManager服务器冗余的H.323 网关拨号对端配置
  7. SUV 个人收藏汽车
  8. Android开发:菜单栏Menu用法讲解
  9. 极客大学架构师训练营 - 同城快递业务架构设计 - 大作业一
  10. 固高运动控制卡学习6 --编码器
  11. es - elasticsearch search - missing value and unmapped fields
  12. Cisco ISO 计划命令 kron实现配置定时自动备份
  13. Syzmlw 蜗居大结局
  14. 数组转这种格式 county_list:{ 110101: “东城区“, 110102: “西城区“, 110105: “朝阳区“, 110106: “丰台区“,
  15. 解决:dependencies.dependency.version' for com.google.guava:guava:jar is missing.
  16. 360校招笔试题总结3
  17. Dijkstra——最短路径路由算法java实现
  18. HCIP(华为高级网络安全工程师)(第五天)(OSPF协议1)
  19. 算法笔记(11)逻辑回归算法及Python代码实现
  20. 在Dreamweaver中运行JSP页面

热门文章

  1. 机器学习之逻辑回归原理
  2. 跨网段共享服务器文件夹,跨网段文件共享
  3. 直播软件测试用例,【专家专栏】李雪 | 直播通用测试方法—如何进行直播测试...
  4. 科技的终极目标是什么
  5. 使用python做王者荣耀挂机刷金币脚本
  6. 深度学习的GPU型号和参数选择
  7. 华为荣耀3c手机语言设置在哪个文件夹,(科普)详解Android系统SD卡各类文件夹名称...
  8. ASP.NET Web程序设计 第四章 系统对象
  9. Git Commit 规范以及emoji 使用指南
  10. 用计算机怎么计算税率表,个税计算器2016年税率表公式