Qt使用QPainter绘制3D立方体
本文实例为大家分享了使用QPainter绘制3D立方体的具体代码,供大家参考,具体内容如下
1.实现思路
(网上有另一篇类似的,不过他不是用的Qt自带的矩阵运算类)
实现思路有点类似使用OpenGL画立方体,先准备顶点数据:
//立方体前后四个顶点,从右上角开始顺时针 vertexArr=QVector{ QVector3D{1,1,1}, QVector3D{1,-1,1}, QVector3D{-1,-1,1}, QVector3D{-1,1,1}, QVector3D{1,1,-1}, QVector3D{1,-1,-1}, QVector3D{-1,-1,-1}, QVector3D{-1,1,-1}}; //六个面,一个面包含四个顶点 elementArr=QVector >{ {0,1,2,3}, {4,5,6,7}, {0,4,5,1}, {1,5,6,2}, {2,6,7,3}, {3,7,4,0}};
然后再和旋转矩阵、透视矩阵进行运算,得到3D顶点坐标在2D平面上的xy值。根据顶点xy值,得到每个面的路径,然后绘制表面的路径。
这里面比较麻烦的是判断哪些是表面,单个立方体还好,可以遍历比较z值,如果是多个物体运算量就大了,还是直接OpenGL吧,毕竟我这个只是画着玩的。
2.实现代码
代码github链接
实现效果GIF动图:
主要代码:
#ifndefMYCUBE_H #defineMYCUBE_H #include#include #include #include classMyCube:publicQWidget { Q_OBJECT public: explicitMyCube(QWidget*parent=nullptr); protected: voidpaintEvent(QPaintEvent*event)override; voidmousePressEvent(QMouseEvent*event)override; voidmouseMoveEvent(QMouseEvent*event)override; voidmouseReleaseEvent(QMouseEvent*event)override; QPointFgetPoint(constQVector3D&vt,intw)const; private: QVector vertexArr;//八个顶点 QVector >elementArr;//六个面 QMatrix4x4rotateMat;//旋转矩阵 QPointmousePos;//鼠标位置 boolmousePressed=false;//鼠标按下标志位 }; #endif//MYCUBE_H
#include"MyCube.h" #include#include #include MyCube::MyCube(QWidget*parent) :QWidget(parent) { //7------------------4 ////| //3------------------0| //||| //||| //||| //||| //|6|5 //||/ //2------------------1 //立方体前后四个顶点,从右上角开始顺时针 vertexArr=QVector { QVector3D{1,1,1}, QVector3D{1,-1,1}, QVector3D{-1,-1,1}, QVector3D{-1,1,1}, QVector3D{1,1,-1}, QVector3D{1,-1,-1}, QVector3D{-1,-1,-1}, QVector3D{-1,1,-1}}; //六个面,一个面包含四个顶点 elementArr=QVector >{ {0,1,2,3}, {4,5,6,7}, {0,4,5,1}, {1,5,6,2}, {2,6,7,3}, {3,7,4,0}}; setFocusPolicy(Qt::ClickFocus);//Widget默认没有焦点 } voidMyCube::paintEvent(QPaintEvent*event) { Q_UNUSED(event) QPainterpainter(this); //先画一个白底黑框 painter.fillRect(this->rect(),Qt::white); QPenpen(Qt::black); painter.setPen(pen); painter.drawRect(this->rect().adjusted(0,0,-1,-1));//右下角会超出范围 //思路,找到z值最高的顶点,然后绘制该顶点相邻的面 //根据z值计算,近大远小 //(此外,Qt是屏幕坐标系,原点在左上角) //矩形边框参考大小 constintcube_width=(width()>height()?height():width())/4; //投影矩阵 //(奇怪,为什么只是平移了z轴,没用perspective函数就有远小近大的效果, //在我的想象中默认不该是正交投影么) QMatrix4x4perspective_mat; perspective_mat.translate(0.0f,0.0f,-0.1f); //计算顶点变换后坐标,包含z值max点就是正交表面可见的, //再计算下远小近大的透视投影效果齐活了 QList vertex_list;//和矩阵运算后的顶点 QList vertex_max_list;//top顶点在arr的位置 floatvertex_max_value;//top值 //根据旋转矩阵计算每个顶点 for(inti=0;i vertex_max_value){ vertex_max_list.clear(); vertex_max_list.push_back(i); vertex_max_value=vertex.z(); }elseif(abs(vertex.z()-vertex_max_value)<(1E-7)){ vertex_max_list.push_back(i); } } } //把原点移到中间来 painter.save(); painter.translate(width()/2,height()/2); //绘制front和back六个面,先计算路径再绘制 QList element_path_list;//每个面路径 QList element_z_values;//每个面中心点的z值 QList element_z_points;//每个面中心点在平面对应xy值 QList element_front_list;//elementArr中表面的index for(inti=0;i element_front_remove; for(inti=0;i element_z_values.at(index_j) &&element_path_list.at(index_i).contains(element_z_points.at(index_j))){ element_front_remove.push_back(index_j); } } } for(intindex:element_front_remove){ element_front_list.removeOne(index); } //根据计算好的路径绘制 painter.setRenderHint(QPainter::Antialiasing,true); //画表面 for(autoindex:element_front_list){ painter.fillPath(element_path_list.at(index),Qt::green); } //画被遮盖面的边框虚线 painter.setPen(QPen(Qt::white,1,Qt::DashLine)); for(inti=0;i pos(); QWidget::mousePressEvent(event); } voidMyCube::mouseMoveEvent(QMouseEvent*event) { if(mousePressed){ constQPointposOffset=event->pos()-mousePos; mousePos=event->pos(); //旋转矩阵x和y分量 //rotateMat.rotate(posOffset.x(),QVector3D(0.0f,-0.5f,0.0f)); //rotateMat.rotate(posOffset.y(),QVector3D(0.5f,0.0f,0.0f)); rotateMat.rotate(1.1f,QVector3D(0.5f*posOffset.y(),-0.5f*posOffset.x(),0.0f)); update(); } QWidget::mouseMoveEvent(event); } voidMyCube::mouseReleaseEvent(QMouseEvent*event) { mousePressed=false; QWidget::mouseReleaseEvent(event); } QPointFMyCube::getPoint(constQVector3D&vt,intw)const { //可以用z来手动计算远小近大,也可以矩阵运算 //constfloatz_offset=vt.z()*0.1; //returnQPointF{vt.x()*w*(1+z_offset),vt.y()*w*(1+z_offset)}; returnQPointF{vt.x()*w,vt.y()*w}; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。