最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Qt使用QPainter繪制3D立方體

 更新時(shí)間:2020年08月20日 15:00:49   作者:龔建波  
這篇文章主要為大家詳細(xì)介紹了Qt使用QPainter繪制3D立方體,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

本文實(shí)例為大家分享了使用QPainter繪制3D立方體的具體代碼,供大家參考,具體內(nèi)容如下

1.實(shí)現(xiàn)思路

(網(wǎng)上有另一篇類(lèi)似的,不過(guò)他不是用的 Qt 自帶的矩陣運(yùn)算類(lèi))

實(shí)現(xiàn)思路有點(diǎn)類(lèi)似使用 OpenGL 畫(huà)立方體,先準(zhǔn)備頂點(diǎn)數(shù)據(jù):

//立方體前后四個(gè)頂點(diǎn),從右上角開(kāi)始順時(shí)針
vertexArr=QVector<QVector3D>{
 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} };
 
 //六個(gè)面,一個(gè)面包含四個(gè)頂點(diǎn)
 elementArr=QVector<QVector<int>>{
 {0,1,2,3},
 {4,5,6,7},
 {0,4,5,1},
 {1,5,6,2},
 {2,6,7,3},
 {3,7,4,0} };

然后再和旋轉(zhuǎn)矩陣、透視矩陣進(jìn)行運(yùn)算,得到 3D 頂點(diǎn)坐標(biāo)在 2D 平面上的 xy 值。根據(jù)頂點(diǎn) xy 值,得到每個(gè)面的路徑,然后繪制表面的路徑。

這里面比較麻煩的是判斷哪些是表面,單個(gè)立方體還好,可以遍歷比較 z 值,如果是多個(gè)物體運(yùn)算量就大了,還是直接 OpenGL 吧,畢竟我這個(gè)只是畫(huà)著玩的。

2.實(shí)現(xiàn)代碼

代碼 github 鏈接

實(shí)現(xiàn)效果 GIF 動(dòng)圖:

主要代碼:

#ifndef MYCUBE_H
#define MYCUBE_H
 
#include <QWidget>
#include <QMouseEvent>
 
#include <QVector3D>
#include <QMatrix4x4>
 
class MyCube : public QWidget
{
  Q_OBJECT
public:
  explicit MyCube(QWidget *parent = nullptr);
 
protected:
  void paintEvent(QPaintEvent *event) override;
  void mousePressEvent(QMouseEvent *event) override;
  void mouseMoveEvent(QMouseEvent *event) override;
  void mouseReleaseEvent(QMouseEvent *event) override;
 
  QPointF getPoint(const QVector3D &vt,int w) const;
 
private:
  QVector<QVector3D> vertexArr;   //八個(gè)頂點(diǎn)
  QVector<QVector<int>> elementArr; //六個(gè)面
  QMatrix4x4 rotateMat;   //旋轉(zhuǎn)矩陣
  QPoint mousePos;      //鼠標(biāo)位置
  bool mousePressed=false;  //鼠標(biāo)按下標(biāo)志位
};
 
#endif // MYCUBE_H
#include "MyCube.h"
 
#include <QPainter>
#include <QtMath>
#include <QDebug>
 
MyCube::MyCube(QWidget *parent)
  : QWidget(parent)
{
  //     7------------------4
  //    /         / |
  //   3------------------0  |
  //   |         |  |
  //   |         |  |
  //   |         |  |
  //   |         |  |
  //   |  6       |  5
  //   |         | /
  //   2------------------1
  //立方體前后四個(gè)頂點(diǎn),從右上角開(kāi)始順時(shí)針
  vertexArr=QVector<QVector3D>{
      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} };
 
  //六個(gè)面,一個(gè)面包含四個(gè)頂點(diǎn)
  elementArr=QVector<QVector<int>>{
  {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默認(rèn)沒(méi)有焦點(diǎn)
}
 
void MyCube::paintEvent(QPaintEvent *event)
{
  Q_UNUSED(event)
  QPainter painter(this);
  //先畫(huà)一個(gè)白底黑框
  painter.fillRect(this->rect(),Qt::white);
  QPen pen(Qt::black);
  painter.setPen(pen);
  painter.drawRect(this->rect().adjusted(0,0,-1,-1)); //右下角會(huì)超出范圍
 
  //思路,找到z值最高的頂點(diǎn),然后繪制該頂點(diǎn)相鄰的面
  // 根據(jù)z值計(jì)算,近大遠(yuǎn)小
  //(此外,Qt是屏幕坐標(biāo)系,原點(diǎn)在左上角)
 
  //矩形邊框參考大小
  const int cube_width=(width()>height()?height():width())/4;
 
  //投影矩陣
  //(奇怪,為什么只是平移了z軸,沒(méi)用perspective函數(shù)就有遠(yuǎn)小近大的效果,
  //在我的想象中默認(rèn)不該是正交投影么)
  QMatrix4x4 perspective_mat;
  perspective_mat.translate(0.0f,0.0f,-0.1f);
 
  //計(jì)算頂點(diǎn)變換后坐標(biāo),包含z值max點(diǎn)就是正交表面可見(jiàn)的,
  //再計(jì)算下遠(yuǎn)小近大的透視投影效果齊活了
  QList<QVector3D> vertex_list; //和矩陣運(yùn)算后的頂點(diǎn)
  QList<int> vertex_max_list; //top頂點(diǎn)在arr的位置
  float vertex_max_value;   //top值
  //根據(jù)旋轉(zhuǎn)矩陣計(jì)算每個(gè)頂點(diǎn)
  for(int i=0;i<vertexArr.count();i++)
  {
    QVector3D vertex=vertexArr.at(i)*rotateMat*perspective_mat;
    vertex_list.push_back(vertex);
    //找出z值max的頂點(diǎn)
    if(i==0){
      vertex_max_list.push_back(0);
      vertex_max_value=vertex.z();
    }else{
      if(vertex.z()>vertex_max_value){
        vertex_max_list.clear();
        vertex_max_list.push_back(i);
        vertex_max_value=vertex.z();
      }else if(abs(vertex.z()-vertex_max_value)<(1E-7)){
        vertex_max_list.push_back(i);
      }
    }
  }
 
  //把原點(diǎn)移到中間來(lái)
  painter.save();
  painter.translate(width()/2,height()/2);
  //繪制front和back六個(gè)面,先計(jì)算路徑再繪制
  QList<QPainterPath> element_path_list; //每個(gè)面路徑
  QList<float> element_z_values;  //每個(gè)面中心點(diǎn)的z值
  QList<QPointF> element_z_points; //每個(gè)面中心點(diǎn)在平面對(duì)應(yīng)xy值
  QList<int> element_front_list;  //elementArr中表面的index
  for(int i=0;i<elementArr.count();i++)
  {
 
    const QVector3D vt0=vertex_list.at(elementArr.at(i).at(0));
    const QVector3D vt1=vertex_list.at(elementArr.at(i).at(1));
    const QVector3D vt2=vertex_list.at(elementArr.at(i).at(2));
    const QVector3D vt3=vertex_list.at(elementArr.at(i).at(3));
 
    //單個(gè)面的路徑
    QPainterPath element_path;
    element_path.moveTo(getPoint(vt0,cube_width));
    element_path.lineTo(getPoint(vt1,cube_width));
    element_path.lineTo(getPoint(vt2,cube_width));
    element_path.lineTo(getPoint(vt3,cube_width));
    element_path.closeSubpath();
 
    //包含zmax點(diǎn)的就是正交表面可見(jiàn)的
    bool is_front=true;
    for(int vertex_index:vertex_max_list){
      if(!elementArr.at(i).contains(vertex_index)){
        is_front=false;
        break;
      }
    }
    if(is_front){
      element_front_list.push_back(i);
    }
    element_path_list.push_back(element_path);
    element_z_values.push_back((vt0.z()+vt2.z())/2);
    element_z_points.push_back((getPoint(vt0,cube_width)+getPoint(vt2,cube_width))/2);
  }
 
  //遠(yuǎn)小近大,還要把包含max但是被近大遮蓋的去掉
  QList<int> element_front_remove;
  for(int i=0;i<element_front_list.count();i++)
  {
    for(int j=0;j<element_front_list.count();j++)
    {
      if(i==j)
        continue;
      const int index_i=element_front_list.at(i);
      const int index_j=element_front_list.at(j);
      if(element_z_values.at(index_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(int index:element_front_remove){
    element_front_list.removeOne(index);
  }
 
  //根據(jù)計(jì)算好的路徑繪制
  painter.setRenderHint(QPainter::Antialiasing,true);
  //畫(huà)表面
  for(auto index:element_front_list){
    painter.fillPath(element_path_list.at(index),Qt::green);
  }
  //畫(huà)被遮蓋面的邊框虛線
  painter.setPen(QPen(Qt::white,1,Qt::DashLine));
  for(int i=0;i<element_path_list.count();i++){
    if(element_front_list.contains(i))
      continue;
    painter.drawPath(element_path_list.at(i));
  }
  //畫(huà)表面邊框
  painter.setPen(QPen(Qt::black,2));
  for(auto index:element_front_list){
    painter.drawPath(element_path_list.at(index));
  }
  painter.restore();
 
  painter.drawText(20,30,"Drag Moving");
}
 
void MyCube::mousePressEvent(QMouseEvent *event)
{
  mousePressed=true;
  mousePos=event->pos();
  QWidget::mousePressEvent(event);
}
 
void MyCube::mouseMoveEvent(QMouseEvent *event)
{
  if(mousePressed){
    const QPoint posOffset=event->pos()-mousePos;
    mousePos=event->pos();
    //旋轉(zhuǎn)矩陣 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);
}
 
void MyCube::mouseReleaseEvent(QMouseEvent *event)
{
  mousePressed=false;
  QWidget::mouseReleaseEvent(event);
}
 
QPointF MyCube::getPoint(const QVector3D &vt,int w) const
{
  //可以用z來(lái)手動(dòng)計(jì)算遠(yuǎn)小近大,也可以矩陣運(yùn)算
  //const float z_offset=vt.z()*0.1;
  //return QPointF{ vt.x()*w*(1+z_offset), vt.y()*w*(1+z_offset) };
  return QPointF{ vt.x()*w, vt.y()*w };
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Visual C++ 常用數(shù)據(jù)類(lèi)型轉(zhuǎn)換方法詳解

    Visual C++ 常用數(shù)據(jù)類(lèi)型轉(zhuǎn)換方法詳解

    本文純粹是總結(jié)一下有關(guān)類(lèi)型轉(zhuǎn)換的貼子,需要的朋友可以參考下
    2017-06-06
  • 利用Matlab仿真實(shí)現(xiàn)圖像煙霧識(shí)別(k-means聚類(lèi)圖像分割+LBP+PCA+SVM)

    利用Matlab仿真實(shí)現(xiàn)圖像煙霧識(shí)別(k-means聚類(lèi)圖像分割+LBP+PCA+SVM)

    本文主要介紹了利用k-means聚類(lèi)實(shí)現(xiàn)圖像分割+LBP算法進(jìn)行特征提取+PCA算法進(jìn)行特征降維+SVM算法訓(xùn)練二分類(lèi)模型從而實(shí)現(xiàn)煙霧識(shí)別。文中介紹很詳細(xì),感興趣的朋友可以了解一下
    2021-12-12
  • C++字符串和數(shù)字的去重操作和鞍點(diǎn)的尋找

    C++字符串和數(shù)字的去重操作和鞍點(diǎn)的尋找

    大家好,本篇文章主要講的是C++字符串和數(shù)字的去重操作和鞍點(diǎn)的尋找,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • C語(yǔ)言由淺入深講解文件的操作上篇

    C語(yǔ)言由淺入深講解文件的操作上篇

    C語(yǔ)言具有操作文件的能力,比如打開(kāi)文件、讀取和追加數(shù)據(jù)、插入和刪除數(shù)據(jù)、關(guān)閉文件、刪除文件等。與其他編程語(yǔ)言相比,C語(yǔ)言文件操作的接口相當(dāng)簡(jiǎn)單和易學(xué)
    2022-04-04
  • C++單鏈表實(shí)現(xiàn)大數(shù)加法

    C++單鏈表實(shí)現(xiàn)大數(shù)加法

    這篇文章主要為大家詳細(xì)介紹了C++單鏈表實(shí)現(xiàn)大數(shù)加法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Windows環(huán)境給FFmpeg集成AVS3解碼器

    Windows環(huán)境給FFmpeg集成AVS3解碼器

    libuavs3d是AVS3標(biāo)準(zhǔn)的解碼器,支持windows/linux/arm/ios等所有常用平臺(tái),在移動(dòng)端最高支持4K/30fps視頻實(shí)時(shí)解碼,解碼速度大幅領(lǐng)先AV1開(kāi)源解碼器dav1d和aomdec,由于FFmpeg默認(rèn)未啟用libuavs3d,因此需要重新配置FFmpeg,標(biāo)明啟用libuavs3d,然后重新編譯安裝FFmpeg
    2024-05-05
  • QT實(shí)現(xiàn)多文件拖拽獲取路徑的方法

    QT實(shí)現(xiàn)多文件拖拽獲取路徑的方法

    這篇文章主要為大家詳細(xì)介紹了QT實(shí)現(xiàn)多文件拖拽獲取路徑的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單計(jì)算器程序

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單計(jì)算器程序

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單計(jì)算器程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • Qt教程之QSqlQueryModel的使用詳解

    Qt教程之QSqlQueryModel的使用詳解

    QSqlQueryModel是QSqlTableModel的父類(lèi),它封裝了執(zhí)行SELECT語(yǔ)句從數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù)的功能,本文將為大家介紹一下QSqlQueryModel的使用,需要的可以參考一下
    2022-11-11
  • C++?右值引用與?const?關(guān)鍵字詳解

    C++?右值引用與?const?關(guān)鍵字詳解

    C++中的const關(guān)鍵字的用法非常靈活,而使用const將大大改善程序的健壯性,const關(guān)鍵字是一種修飾符,這篇文章主要介紹了C++?右值引用與?const?關(guān)鍵字,需要的朋友可以參考下
    2022-10-10

最新評(píng)論

华宁县| 康保县| 信丰县| 泌阳县| 霍林郭勒市| 科尔| 芦山县| 新郑市| 基隆市| 砚山县| 无为县| 大新县| 崇阳县| 临沂市| 青河县| 日土县| 离岛区| 简阳市| 陆丰市| 宁都县| 沧源| 台江县| 漳平市| 泗洪县| 晋中市| 和平县| 大名县| 济宁市| 宜章县| 鄂尔多斯市| 荣成市| 青河县| 东至县| 习水县| 奉贤区| 永昌县| 潢川县| 府谷县| 内黄县| 璧山县| 武邑县|