精华内容
下载资源
问答
  • 、用快速开发工具Qt Designer编写Qt程序 我们用的开发系统是Linux,开发工具Qt Designer.如果大家安装完整版的Linux,系统就带这工具了.大家可以点击应用程序->编程->Qt Designer来找到它. Qt ...

    [日期:2009-03-02] 来源:Linux社区  作者:QT编辑
    三、用快速开发工具Qt Designer编写Qt程序

    我们用的开发系统是Linux,开发工具是Qt Designer.如果大家安装完整版的Linux,系统就带这个工具了.大家可以点击应用程序->编程->Qt Designer来找到它.

    Qt Designer首先呈现给用户的是一个New/Open对话框。因为这里要创建一个C++程序,所以在此选择C++ Project,点击“OK”继续。

    选择一个想要保存文件的位置,并且给出一个文件名,注意这里文件名的扩展名一定要是.pro。点击“OK”后,到下面的界面.

     

     

    现在我们看到的就是Qt Designer主窗口,确保Property Editor可见。如果它是不可见的,用户可以通过Windows→Views→Property Editor/Signal Handlers选单选项来使其可见.

    下面我们建立一个如下图所示的对话框.

    通过选择File→New选单,然后选择Dialog来创建一个新的对话框。这时Qt Designer会创建一个新的空白对话框,用户可以在其上放置输入框和按钮。

    打开Property Editor,把name的值改为“MainForm”,把caption的值改为“Hello world”。 '

    然后我们在对话框上拖拽上一个lable,方法是在左面Toolbox的common widgets里的TextLabel,然后在空白对话框上画一下.改一下caption的值改为“Hello world”就得到如下的界面了.

    现在就已经基本完成这个应用程序了。不过在编译和运行此应用程序之前,还要创建一个main.cpp文件。方法是选择File→New→C++Main- File(main.cpp),只需接受缺省的配置即可。main.cpp会自动在Code Editor中打开。因为这里无需改变main.cpp中的 任何东西,所以直接将Code Editor窗口关闭,并且保存main.cpp。

    到此为止,在Qt Designer中的工作已经完成了。保存整个项目,下面来编译和运行这个程序。在编译程序之前,要首先生成它的Makefile文件。打开一个终端,然后切换至保存有项目的位置,使用以下命令来生成Makefile文件:

    #qmake -o Makefile hellopro

    现在,就可以运行make来编译程序了,根据系统的性能,这个步骤需要花费一点时间。当编译工作完成后,输入./hello来运行程序。如果一切正常,用户应该已经看到程序了。

    下面是我电脑上程序的界面:

    到这里我们完成了第一个Qt的程序,怎么样呢,是不是有点小成就感.

    这里我还有个建议:

    最好把每一个项目单独放在一个文件夹里.这样就可以让我们更方便的使用QT为我们提供的工具,例如qmake等。



    转载自:http://www.linuxidc.com/Linux/2009-03/18684p2.htm

    展开全文
  • QT开发之旅串口设备调试工具

    千次阅读 2013-12-20 13:48:54
    这里首先说明一下,这为什么叫串口设备调试工具而不是串口调试工具,是因为这个工具比网络上的串口调试工具多出了一些真实需要的用来调试设备的功能,首先一点就是大部分的串口调试工具收到数据都是立即返回,这样...

    这里首先说明一下,这个为什么叫串口设备调试工具而不是串口调试工具,是因为这个工具比网络上的串口调试工具多出了一些真实需要的用来调试设备的功能,首先一点就是大部分的串口调试工具收到数据都是立即返回,这样的数据都是连着的,头一条数据和后一条数据头尾相连,对于调试着来说要看数据非常麻烦,而且在不断有数据过来时,要停下来看数据除非关闭串口,而不能在打开串口的时候看数据,因为不断有数据过来冲掉前面的数据显示,甚至有些还不能最大化等等情况,这个工具是根据将近四年的与硬件通信这块开发工具的切身实际需求量身定做的,还能模拟设备立即回复数据。之前很多QT开发的版本源码分成两套,一套在windows下编译,一套在linux下编译,甚至麻烦,虽然QT5.1之后的版本自带了串口通信类,本人测试过,貌似还有小问题,在快速收发数据时容易卡住,这次带来的源码不用做任何改动即可在多个平台编译。

    项目名称:串口设备调试工具

    开发环境:WIN7+QT4.7+QT CREATOR2.8+MINGW

    已编译通过测试平台:XP、Win7、ubuntu、tiny210

    技术实现:通过第三方串口通信类,解析协议并作出处理

    基本功能:

    1:支持16进制数据发送与接收。

    2:支持windows下COM9以上的串口通信。

    3:自动加载对应操作系统串口号。

    4:实时显示收发数据字节大小以及串口状态。

    高级功能:

    1:可自由管理需要发送的数据,每次只要从下拉框中选择数据即可,无需重新输入数据。

    2:可模拟设备回复数据,需要在主界面开启模拟设备回复数据。当接收到设置好的指令时,立即回复设置的回复指令。例如指定收到0x16 0x00 0xFF 0x01需要回复0x16 0x00 0xFE 0x01,则只需要在SendData.txt中添加一条数据16 00 FF 01:16 00 FE 01即可。

    3:可定时发送数据和保存数据到文本文件:,默认间隔5秒钟,可更改间隔时间。

    4:在不断接收到大量数据时,可以暂停显示数据来查看具体数据,后台依然接收数据但不处理,无需关闭串口来查看已接收到的数据。

    5:每次收到的数据都是完整的一条数据,而不是脱节的,做了延时处理。

    6:一套源码随处编译,无需更改串口通信类,已在XP/WIN7/UBUNTU/ARMLINUX系统下成功编译并运行。

    如果有更好的建议或者意见,请Q我(517216493),谢谢!

    运行截图:

     

    粗略步骤:

    第一步:布局好界面,控件命名好,建议用pascal命名法。

    第二步:准备unix和windows串口通信第三方类qextserialport.h、qextserialport.cpp、qextserialport_global.h、qextserialport_p.h、qextserialport_unix.cpp、qextserialport_win.cpp。

    导入到工程,在pro文件中这样写:

    QT       += core gui

     

    TARGET = mySerialPortTools

    TEMPLATE = app

     

     

    SOURCES += main.cpp\

            qextserialport.cpp\

            frmmain.cpp

     

    HEADERS += frmmain.h \

            qextserialport_global.h \

            qextserialport.h \

            myhelper.h

     

    win32 { SOURCES += qextserialport_win.cpp }

    unix { SOURCES += qextserialport_unix.cpp }

     

    FORMS += frmmain.ui

     

    RESOURCES += \

        main.qrc

     

    MOC_DIR=temp/moc

    RCC_DIR=temp/rcc

    UI_DIR=temp/ui

    OBJECTS_DIR=temp/obj

    DESTDIR=bin

     

    win32:RC_FILE=main.rc

     

    CONFIG += warn_off      #关闭警告

     

    这样的话在不同平台下会自动加载对应平台的cpp实现文件来编译。

    第三步:初始化主界面,自动加载对应串口号波特率等信息。

    void frmMain::InitForm()
    {    
        ReceiveCount=0;
        SendCount=0;
        IsShow=true;
        IsAutoClear=false;
        IsHexSend=true;
        IsHexReceive=true;
        IsDebug=false;
    
        QStringList comList;//串口号
        QStringList baudList;//波特率
        QStringList parityList;//校验位
        QStringList dataBitsList;//数据位
        QStringList stopBitsList;//停止位
    
    #ifdef Q_OS_WIN//如果是windows系统
        comList<<"COM1"<<"COM2"<<"COM3"<<"COM4"<<"COM5"<<"COM6"
              <<"COM7"<<"COM8"<<"COM9"<<"COM10"<<"COM11"<<"COM12"
             <<"COM13"<<"COM14"<<"COM15";
    #else//如果是unix或者其他系统
        comList<<"ttyUSB0"<<"ttyUSB1"<<"ttyUSB2"<<"ttyUSB3"<<"ttyUSB4"<<"ttyUSB5"
              <<"ttyS0"<<"ttyS1"<<"ttyS2"<<"ttyS3"<<"ttyS4"<<"ttyS5"<<"ttyS6"
             <<"ttyS7"<<"ttyS8"<<"ttyS9";
    #endif
    
        ui->cboxPortName->addItems(comList);
        ui->cboxPortName->setCurrentIndex(0);
    
        baudList<<"50"<<"75"<<"100"<<"134"<<"150"<<"200"<<"300"
               <<"600"<<"1200"<<"1800"<<"2400"<<"4800"<<"9600"
              <<"14400"<<"19200"<<"38400"<<"56000"<<"57600"
             <<"76800"<<"115200"<<"128000"<<"256000";
    
        ui->cboxBaudRate->addItems(baudList);
        ui->cboxBaudRate->setCurrentIndex(12);
    
        parityList<<"无"<<"奇"<<"偶";
    
    #ifdef Q_OS_WIN//如果是windows系统
        parityList<<"标志";
    #endif
    
        parityList<<"空格";
    
        ui->cboxParity->addItems(parityList);
        ui->cboxParity->setCurrentIndex(0);
    
        dataBitsList<<"5"<<"6"<<"7"<<"8";
        ui->cboxDataBit->addItems(dataBitsList);
        ui->cboxDataBit->setCurrentIndex(3);
    
        stopBitsList<<"1";
    
    #ifdef Q_OS_WIN//如果是windows系统
        stopBitsList<<"1.5";
    #endif
    
        stopBitsList<<"2";
    
        ui->cboxStopBit->addItems(stopBitsList);
        ui->cboxStopBit->setCurrentIndex(0);
    
        //读取数据(采用定时器读取数据,不采用事件,方便移植到linux)
        myReadTimer=new QTimer(this);
        myReadTimer->setInterval(300);
        connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
    
        //发送数据
        mySendTimer=new QTimer(this);
        mySendTimer->setInterval(5000);
        connect(mySendTimer,SIGNAL(timeout()),this,SLOT(WriteMyCom()));
    
        //保存数据
        mySaveTimer=new QTimer(this);
        mySaveTimer->setInterval(5000);
        connect(mySaveTimer,SIGNAL(timeout()),this,SLOT(SaveMyCom()));
    
        //显示日期时间
        myTimer=new QTimer(this);
        myTimer->start(1000);
        connect(myTimer,SIGNAL(timeout()),this,SLOT(SetTime()));
    
        QDate dateNow=QDate::currentDate();
        ui->labDate->setText(QString("日期:%1").arg(dateNow.toString("yyyy年MM月dd日 dddd")));
    
        for (int i=1;i<=60;i++)
        {
            ui->cboxSend->addItem(QString::number(i)+"秒");
            ui->cboxSave->addItem(QString::number(i)+"秒");
        }
    
        ui->cboxSave->setCurrentIndex(4);
        ui->cboxSend->setCurrentIndex(4);
    
        ui->cboxSend->setEnabled(false);
        ui->cboxSave->setEnabled(false);
    
        this->ChangeEnable(false);
        this->ReadConfigData();//读取发送数据加载到下拉框
        this->ReadSendData();//读取数据转发文件
    
        ui->txtSend->installEventFilter(this);//安装监听器监听发送数据框回车响应
    }

    第四步:采用定时器读取串口数据,其实也可以采用事件机制,在几大平台也测试通过事件来收数据也可以,但是在windows下在快速收发大量数据时候居然会卡住,同样的代码在linux表现很好,不明原因,后面采用定时器读取机制,问题就没有了。

    void frmMain::ReadMyCom()
    {
        //这个判断尤为重要,否则的话直接延时再接收数据,空闲时和会出现高内存占用
        if (myCom->bytesAvailable()<=0){return;}
    
        myHelper::Sleep(100);//延时100毫秒保证接收到的是一条完整的数据,而不是脱节的
        QByteArray buffer=myCom->readAll();
    
        if (IsShow)
        {
            if (IsHexReceive)
            {
                QString tempDataHex=myHelper::ByteArrayToHexStr(buffer);
                ui->txtDataHex->append(QString("接收:%1 时间:%2")
                                       .arg(tempDataHex)
                                       .arg(QTime::currentTime().toString("HH:mm:ss")));
    
                if (IsDebug)//2013-8-6增加接收数据后转发数据,模拟设备
                {
                    foreach(QString tempData,SendDataList)
                    {
                        QStringList temp=tempData.split(';');
                        if (tempDataHex==temp[0])
                        {
                            //这里没有跳出循环,有可能一条数据会对应多条数据需要转发
                            myCom->write(myHelper::HexStrToByteArray(temp[1]));
                        }
                    }
                }
            }
            else
            {
                QString tempDataNormal=QString(buffer);
                ui->txtDataHex->append(QString("接收:%1 时间:%2")
                                       .arg(tempDataNormal)
                                       .arg(QTime::currentTime().toString("HH:mm:ss")));
    
                if (IsDebug)//2013-8-6增加接收数据后转发数据,模拟设备
                {
                    foreach(QString tempData,SendDataList)
                    {
                        QStringList temp=tempData.split(';');
                        if (tempDataNormal==temp[0])
                        {
                            //这里没有跳出循环,有可能一条数据会对应多条数据需要转发
                            myCom->write(temp[1].toAscii());
                        }
                    }
                }
            }
    
            ReceiveCount=ReceiveCount+buffer.size();
            ui->labReceive->setText(QString("接收:%1 字节").arg(ReceiveCount));
        }
    }
    void frmMain::WriteMyCom()
    {
        QString str=ui->txtSend->currentText();
        if (str==""){ui->txtSend->setFocus();return;}//发送数据为空
        if (!myCom->isOpen()) { return; }//串口没有打开
    
        QByteArray outData=str.toAscii();
        int size=outData.size();
    
        if (IsHexSend)//转化为16进制发送
        {
            outData=myHelper::HexStrToByteArray(str);
            size=outData.size();
            myCom->write(outData);
        }
        else
        {
            size=outData.size();
            myCom->write(outData);
        }
    
        ui->txtDataHex->append(QString("发送:%1 时间:%2")
                               .arg(str)
                               .arg(QTime::currentTime().toString("HH:mm:ss")));
    
        SendCount=SendCount+size;
        ui->labSend->setText(QString("发送:%1 字节").arg(SendCount));
    
        if (IsAutoClear)
        {
            ui->txtSend->setCurrentIndex(-1);
            ui->txtSend->setFocus();
        }
    }
    
    void frmMain::SaveMyCom()
    {
        QString tempData=ui->txtDataHex->toPlainText();
        if (tempData==""){return;}//如果没有内容则不保存
    
        QDateTime now=QDateTime::currentDateTime();
        QString name=now.toString("yyyyMMddHHmmss");
        QString fileName=name+".txt";
    
        QFile file(fileName);
        file.open(QFile::WriteOnly | QIODevice::Text);
        QTextStream out(&file);
        out<<tempData;
        file.close();
    }

    这里有个细节说下,就是在接收数据函数里面,增加了一个判断if (myCom->bytesAvailable()<=0){return;}如果不这样的话,每次定时读取都会延时,内存占用很高,当然,如果采用事件机制的话,这里不需要任何延时或者判断。

    可执行文件下载地址:http://download.csdn.net/detail/feiyangqingyun/6745003

    源码猛点这里:http://download.csdn.net/detail/feiyangqingyun/6745011

    展开全文
  • 本游戏使用Qt5.8开发 这是一款进制转换小工具,比如十二进制的源数据15,如果您选择目标进制7进制的话,那么按下转换按钮,程序会输出【23】;程序的逻辑原理大概如此:首先把输入通过一系列算法转换成bool数组,...

    本游戏使用Qt5.8开发
    这是一款进制转换小工具,比如十二进制的源数据15,如果您选择目标进制7进制的话,那么按下转换按钮,程序会输出【23】;程序的逻辑原理大概如此:首先把输入通过一系列算法转换成bool数组,bool的真假对应硬件中的高低电平,这样就可以模拟芯片电路的与或非原理实现软件层面的加减乘除,所以最后就可以成功的实现任意进制【二进制到三十六进制】的格式计算与转换了,目前暂不支持小数点格式。

     

    项目已开源,感兴趣这请移步这里:https://github.com/wenpinglaoyao?tab=repositories

    展开全文
  • 三个小东西一共花了我三天时间,接下来分别看一下这是三个软件的显示效果: 名字数据生成工具 ,点击Create可以生成名字数据(本人不姓李哈^v^) OpenGL名字渲染工具 , 使用Opengl渲染成3D图像,点击Load加载...


    本篇文章讲介绍三个软件:

    • 名字数据生成工具
    • OpenGL名字渲染工具
    • CPU名字渲染工具

    这三个小东西一共花了我三天时间,接下来分别看一下这是三个软件的显示效果:

    1. 名字数据生成工具 ,点击Create可以生成名字数据(本人不姓李哈^v^
      Qt趣味开发之打造一个3D名字渲染器
    2. OpenGL名字渲染工具 , 使用Opengl渲染成3D图像,点击Load加载之前生成的数据
      Qt趣味开发之打造一个3D名字渲染小工具
    3. CPU名字渲染工具 , 接下来是使用CPU实现的渲染同一个数据,显示效果如下:

    Qt趣味开发之打造一个3D名字渲染小工具


    1. 名字数据生成工具

    名字数据生成工具主要的功能就是生成数据,他主要实现了下面几个功能:

    • 鼠标点击创建多个或多边形
    • 将一个或多个多边形分割成三角形
    • 将2D的多边形变成3D的多边形
    • 生成数据(定点数据、法线、颜色数据)

    当点击鼠标左键创建多边形的点,点击鼠标右键实现三角形的填充(不知道为什么,gif录制软件会使窗口丢掉移动时候的效果)

    Qt趣味开发之打造一个3D名字渲染小工具
    其中鼠标点击和绘制相关的代码,如下:

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #include <QPolygon>
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = 0);
        ~Widget();
    
        void getCurrentPolygons(QVector<QPolygon>& points);
    
        void cleanAll(void);
    
    protected:
        void mousePressEvent(QMouseEvent* event) override;
        void mouseMoveEvent(QMouseEvent* event) override;
        void mouseReleaseEvent(QMouseEvent* event) override;
    
        void paintEvent(QPaintEvent* event) override;
    
    private:
        QPoint m_startPos;
        bool m_isDrawing = false;
    
        QVector<QPolygon> m_points;
        QVector<QPolygon> m_points2;
    };
    
    #endif // WIDGET_H
    
    
    #include "widget.h"
    #include <QMouseEvent>
    #include <QPainter>
    #include <QDebug>
    #include "PolygonTool.h"
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        this->setMouseTracking(true);
    }
    
    Widget::~Widget()
    {
    
    }
    
    void Widget::mousePressEvent(QMouseEvent* event)
    {
        QPoint pos = event->pos();
    
        if (event->button() == Qt::LeftButton)
        {
            if (!m_isDrawing)
            {
                QPolygon polygon;
                m_isDrawing = true;
    
                polygon.append(pos);
                polygon.append(pos);
                m_points.append(polygon);
            }
            else {
                int nCount = m_points.size();
                int nSize = m_points[nCount - 1].count();
    
                m_points[nCount - 1].replace(nSize - 1, pos);
                m_points[nCount - 1].append(pos);
            }
    
        }
        else if (event->button() == Qt::RightButton)
        {
            m_isDrawing = false;
    
            int nCount = m_points.size();
            int nSize = m_points[nCount - 1].count();
    
            m_points[nCount - 1].replace(nSize - 1, pos);
    
            // 更新填充数据
            m_points2 += g_PolygonTool->getTrianglePolygons(m_points[nCount - 1]);
            this->update();
        }
    
    
        qDebug() << m_points.size() << m_points;
    
        return QWidget::mousePressEvent(event);
    }
    
    void Widget::mouseMoveEvent(QMouseEvent* event)
    {
        if (m_isDrawing)
        {
            int nCount = m_points.count();
    
            QPoint pos = event->pos();
    
            int nSize = m_points[nCount - 1].count();
            m_points[nCount - 1].replace(nSize - 1, pos);
    
            this->update();
        }
    
        return QWidget::mouseMoveEvent(event);
    }
    
    void Widget::mouseReleaseEvent(QMouseEvent* event)
    {
        return QWidget::mouseReleaseEvent(event);
    }
    
    void Widget::paintEvent(QPaintEvent* event)
    {
        QPainter painter(this);
        painter.fillRect(this->rect(), QBrush(QColor(150, 150, 150)));
    
        QPen nPen;
        nPen.setColor(QColor(255, 0, 0));
        painter.setPen(nPen);
        painter.setBrush(QColor(0, 200, 200, 80));
    
        // 绘制矩形
        for (auto iter = m_points.begin(); iter != m_points.end(); ++iter)
        {
            painter.drawPolygon(*iter);
        }
    
        // 绘制分割三角形
        nPen.setColor(QColor(0, 255, 255));
        nPen.setWidth(4);
        painter.setPen(nPen);
        painter.setBrush(QColor(0, 0, 255));
        for (auto iter = m_points2.begin(); iter != m_points2.end(); ++iter)
        {
            painter.drawPolygon(*iter);
        }
    
        return QWidget::paintEvent(event);
    }
    
    void Widget::cleanAll(void)
    {
        m_points.clear();
        m_points2.clear();
        this->update();
    }
    
    void Widget::getCurrentPolygons(QVector<QPolygon>& points)
    {
        points = m_points;
    }
    
    

    其中 g_PolygonTool->getTrianglePolygons() 会将一个多边形分割成多个三角形。
    关于多边形分割个成多个三角形,可以参考如下链接:
    判断点是否在三角形内 和 点是否在矩形内
    多边形构建三角形

    实现的代码如下(按照上面的链接的做法,有时候分割凹多边形会有问题):

    头文件

    #ifndef POLYGONTOOL_H
    #define POLYGONTOOL_H
    
    #include <QObject>
    #include <QPolygon>
    #include <QVector>
    #include <QVector3D>
    
    #define g_PolygonTool PolygonTool::getInstance()
    
    class PolygonTool : public QObject
    {
        Q_OBJECT
    
    public:
        static PolygonTool* getInstance(void);
    
        // 获取多边形-三角形数组
        QVector<QPolygon> getTrianglePolygons(const QPolygon& polygon);
    
    private:
        PolygonTool(QObject* parent = nullptr);
        ~PolygonTool();
    
        // 获取凹角的索引
        int getConcaveIndex(const QPolygon& polygon);
        // 判断一个点是否在一个三角形中
        bool isInTriangle(QPolygon triangle, QPoint pos);
        // 获取凸多边形的三角形数组
        QVector<QPolygon> getConvexPolygonVec(const QPolygon& polygon);
        // 获取凹多边形的三角形数组
        QVector<QPolygon> getConcavePolygonVec(const QPolygon& polygon, int index);
    };
    
    #endif
    
    

    cpp文件

    #include "PolygonTool.h"
    #include <QDebug>
    
    PolygonTool::PolygonTool(QObject* parent)
        :QObject(parent)
    {
    
    }
    
    PolygonTool::~PolygonTool()
    {
    
    }
    
    PolygonTool* PolygonTool::getInstance(void)
    {
        static PolygonTool instance;
        return &instance;
    }
    
    QVector<QPolygon> PolygonTool::getTrianglePolygons(const QPolygon& polygon)
    {
        QVector<QPolygon> result;
    
        int index = this->getConcaveIndex(polygon);
        if (index >= 0){
            qDebug() << "ao Polygon" << index;
    
            result = this->getConcavePolygonVec(polygon, index);
        }
        else {
            qDebug() << "tu Polygon";
    
            result = this->getConvexPolygonVec(polygon);
        }
    
        return result;
    }
    
    int PolygonTool::getConcaveIndex(const QPolygon& polygon)
    {
        for (int i = 0; i<polygon.size(); ++i)
        {
            QPoint pos1 = polygon[i];
            QPoint pos2;
            QPoint pos3;
            if (i == polygon.size() - 1)
            {
                pos2 = polygon[0];
                pos3 = polygon[1];
            }
            else if (i == polygon.size() - 2){
                pos2 = polygon[i + 1];
                pos3 = polygon[0];
            }
            else {
                pos2 = polygon[i + 1];
                pos3 = polygon[i + 2];
            }
    
            QVector3D vec1(pos2.x() - pos1.x(), pos2.y() - pos1.y(), 1.0);
            QVector3D vec2(pos3.x() - pos2.x(), pos3.y() - pos2.y(), 1.0);
            QVector3D destVec = QVector3D::crossProduct(vec1, vec2);
            if (destVec.z() < 0)
            {
                int index = i + 1;
                if (index == polygon.size())
                    index = 0;
    
                return index;
            }
        }
    
        return -1;
    }
    
    QVector<QPolygon> PolygonTool::getConvexPolygonVec(const QPolygon& polygon)
    {
        QPolygon tempPolygon = polygon;
        QVector<QPolygon> result;
    
        while (tempPolygon.size() > 3)
        {
            QPolygon polygon;
            polygon << tempPolygon[0] << tempPolygon[1] << tempPolygon[2];
            result << polygon;
    
            tempPolygon.removeAt(1);
        }
        result << tempPolygon;
    
        return result;
    }
    
    bool PolygonTool::isInTriangle(QPolygon triangle, QPoint pos)
    {
        auto func = [&](QPoint A, QPoint B, QPoint C, QPoint P){
            QVector3D vecAB(B.x() - A.x(), B.y() - A.y(), 1.0);
            QVector3D vecAC(C.x() - A.x(), C.y() - A.y(), 1.0);
            QVector3D vecAP(P.x() -A.x(), P.y() - A.y(), 1.0);
    
            QVector3D v1 = QVector3D::crossProduct(vecAB, vecAC);
            QVector3D v2 = QVector3D::crossProduct(vecAB, vecAP);
    
            return QVector3D::dotProduct(v1, v2) >= 0;
        };
    
        return func(triangle[0], triangle[1], triangle[2], pos) && \
               func(triangle[1], triangle[2], triangle[0], pos) && \
               func(triangle[2], triangle[0], triangle[1], pos);
    }
    
    QVector<QPolygon> PolygonTool::getConcavePolygonVec(const QPolygon& polygon, int index)
    {
        QPolygon trianglePolygon;
    
        int pos2Index = (index - 1 + polygon.size()) % polygon.size();
        int pos3Index = (index - 2 + polygon.size()) % polygon.size();
    
        // 向上取三个点
        QPoint pos1 = polygon[index];
        QPoint pos2 = polygon[pos2Index];
        QPoint pos3 = polygon[pos3Index];
    
        trianglePolygon << pos1 << pos2 << pos3;
    
        // 判断三角形中是否包括多边形的点
        bool hasIncluded = false;
        for (int i = 0; i != polygon.size(); ++i)
        {
            if (i == index || i == pos2Index || i == pos3Index)
                continue;
    
            if (isInTriangle(trianglePolygon, polygon[i]))
            {
                hasIncluded = true;
                break;
            }
        }
    
        // 包括,向下取点
        if (hasIncluded)
        {
            pos2Index = (index + 1 + polygon.size()) % polygon.size();
            pos3Index = (index + 2 + polygon.size()) % polygon.size();
            trianglePolygon.clear();
    
            trianglePolygon << pos1 << polygon[pos2Index] << polygon[3];
        }
    
        // 分割多边形
        QPolygon tempPolygon = polygon;
        QVector<QPolygon> result;
    
        // 添加三角形并删除中间的点
        result << trianglePolygon;
        tempPolygon.remove(pos2Index);
    
        if (tempPolygon.size() == 3)
            result << tempPolygon;              // 剩余三个点
        else
        {
            int index = getConcaveIndex(tempPolygon);
            if (index < 0)
            {
                result += getConvexPolygonVec(tempPolygon);    // 添加凸多边形的结果
            }
            else {
                result += getConcavePolygonVec(tempPolygon, index);   // 添加凹多边形的结果
            }
        }
    
        return result;
    }
    
    

    接下来就是生成顶点数据了,我这里是将2D的每个多边形都扩展了他的Z坐标

    正面:多边形分割为多个三角形
    背面:背面的顶点与正面的顶点是相同的
    侧面:正面的相邻两个点和对应的背面的两个点,构成一个新的多边形,将其再分割为三角形

    完整代码如下:

    头文件:

    #ifndef MYWIDGET_H
    #define MYWIDGET_H
    
    #include <QWidget>
    #include <QVector3D>
    #include "widget.h"
    
    class MyWidget : public QWidget
    {
    public:
        MyWidget(QWidget* parent = nullptr);
        ~MyWidget();
    
    private:
        Widget* m_pWidget = nullptr;
        QString pointToString(QVector3D pos);
        QString colorToString(QColor color);
        QString polygonToString(QPolygon points, float zValue, QColor color);
    
        // 获取法线向量
        QVector3D getNormalVec(QVector3D aPos, QVector3D bPos, QVector3D cPos);
    
        float m_frontInterval = 1.0f;
        float m_frontInterval2 = 1.3f;
    
    private slots:
        void onClicedButton(void);
        void onClickedClean(void);
    
    private:
    };
    
    #endif
    

    cpp文件:

    #include "MyWidget.h"
    #include <QVBoxLayout>
    #include <QPushButton>
    #include <QFileDialog>
    #include <QTextStream>
    #include <QMessageBox>
    #include "PolygonTool.h"
    
    MyWidget::MyWidget(QWidget* parent)
        :QWidget(parent)
    {
        m_pWidget = new Widget;
        m_pWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
        QVBoxLayout* mainLayout = new QVBoxLayout(this);
        mainLayout->addWidget(m_pWidget);
    
        QWidget* pButtonWidget = new QWidget;
        mainLayout->addWidget(pButtonWidget);
        QHBoxLayout* pLayout = new QHBoxLayout(pButtonWidget);
    
        QPushButton* pButton = new QPushButton(tr("Create"));
        pLayout->addWidget(pButton);
        QObject::connect(pButton, &QPushButton::clicked, this, &MyWidget::onClicedButton);
    
        QPushButton* pCleanButon = new QPushButton(tr("Clean"));
        pLayout->addWidget(pCleanButon);
        QObject::connect(pCleanButon, &QPushButton::clicked, this, &MyWidget::onClickedClean);
    }
    
    MyWidget::~MyWidget()
    {
    
    }
    
    void MyWidget::onClicedButton(void)
    {
        QString fileName = QFileDialog::getSaveFileName(this, "Get File Name", "./");
        QFile nFile(fileName);
        if (nFile.exists())
            QFile::remove(fileName);
    
        nFile.open(QFile::ReadWrite);
        QTextStream stream(&nFile);
    
        QVector<QPolygon> polygons;
        m_pWidget->getCurrentPolygons(polygons);
    
        for (auto iter = polygons.begin(); iter != polygons.end(); ++iter)
        {
            // 正面
            stream << polygonToString(*iter, m_frontInterval, QColor(200, 100, 100));
    
            // 后面
            stream << polygonToString(*iter, m_frontInterval2, QColor(0, 150, 150));
    
            // 侧面
            for (int i=0; i<iter->size(); ++i)
            {
                QStringList strList;
                strList.clear();
    
                QVector3D pos1(iter->at(i).x(), iter->at(i).y(), m_frontInterval);
                QVector3D pos2(iter->at(i).x(), iter->at(i).y(), m_frontInterval2);
                QVector3D pos3;
                QVector3D pos4;
                if (iter->size() - 1 == i)
                {
                    pos3 = QVector3D(iter->at(0).x(), iter->at(0).y(), m_frontInterval2);
                    pos4 = QVector3D(iter->at(0).x(), iter->at(0).y(), m_frontInterval);
                }
                else
                {
                    pos3 = QVector3D(iter->at(i+1).x(), iter->at(i+1).y(), m_frontInterval2);
                    pos4 = QVector3D(iter->at(i+1).x(), iter->at(i+1).y(), m_frontInterval);
                }
    
                // 第一个三角形
                strList << pointToString(pos1);
                strList << pointToString(pos2);
                strList << pointToString(pos3);
                strList << pointToString(getNormalVec(pos1, pos2, pos3));
                strList << colorToString(QColor(180, 180, 180));
                QString destString = strList.join(";");
                destString += "\n\r";
                stream << destString;
    
                // 第二个三角形
                strList.clear();
                strList << pointToString(pos1);
                strList << pointToString(pos3);
                strList << pointToString(pos4);
                strList << pointToString(getNormalVec(pos1, pos3, pos4));
                strList << colorToString(QColor(180, 180, 180));
                destString = strList.join(";");
                destString += "\n\r";
                stream << destString;
    
            }
        }
    
        nFile.close();
        QMessageBox::warning(this, "Successed", "Write Successed");
    }
    
    void MyWidget::onClickedClean(void)
    {
        m_pWidget->cleanAll();
    }
    
    QString MyWidget::pointToString(QVector3D pos)
    {
        QString str = "%1,%2,%3";
        QPointF npos;
        npos.setX(pos.x() * 1.0 / m_pWidget->width() - 0.5);
        npos.setY(pos.y() * 1.0 / m_pWidget->height() - 0.5);
    
        str = str.arg(npos.x()).arg(-npos.y()).arg(pos.z());
    
        return str;
    }
    
    QString MyWidget::colorToString(QColor color)
    {
        // 颜色
        QString colorString = "%1,%2,%3";
        colorString = colorString.arg(color.red()).arg(color.green()).arg(color.blue());
    
        return colorString;
    }
    
    QString MyWidget::polygonToString(QPolygon points, float zValue, QColor color)
    {
        QVector<QPolygon> vector = g_PolygonTool->getTrianglePolygons(points);
        QString destString;
    
        for (auto iter = vector.begin(); iter != vector.end(); ++iter)
        {
            QStringList strList;
    
            // 三角形 - 点1
            QVector3D aPos(iter->at(0).x(), iter->at(0).y(), zValue);
            strList << pointToString(aPos);
    
            // 三角形 - 点2
            QVector3D bPos = QVector3D(iter->at(1).x(), iter->at(1).y(), zValue);
            strList << pointToString(bPos);
    
            // 三角形 - 点3
            QVector3D cPos = QVector3D(iter->at(2).x(), iter->at(2).y(), zValue);
            strList << pointToString(cPos);
    
            // 法线
            QVector3D normalPos = getNormalVec(aPos, bPos, cPos);
            strList << pointToString(normalPos);
    
            // 颜色
            strList << colorToString(color);
    
            destString += strList.join(";");
            destString += "\n\r";
        }
    
        return destString;
    }
    
    // 获取法线向量
    QVector3D MyWidget::getNormalVec(QVector3D aPos, QVector3D bPos, QVector3D cPos)
    {
        QVector3D ab = bPos - aPos;
        QVector3D ac = cPos - aPos;
    
        return QVector3D::crossProduct(ab, ac);
    }
    
    

    函数 onClicedButton 会生成顶点数据文件。文件格式如下:

    Qt趣味开发之打造一个3D名字渲染小工具
    这里每一行为一个三角形的数据:
    前三个表示三角形的顶点数据,(x, y z) 坐标
    接下来的数据为法线,(通过向量叉乘就可以得到法线)
    最后为颜色数据,使用0~255的RGB颜色数据

    目前的代码中存在的问题:

    1. 有些凹多边形分割有问题
    2. 三角形顶点的顺序可能不正确
    3. 因为顶点顺序不对,导致的法线计算的方向存在反向的问题

    2. OpenGL名字渲染工具

    OpenGL渲染就比较简单了,将顶点数据传到一个VBO中,然后显示出来。

    • 通过鼠标左键操作,修改模型矩阵的值实现旋转的效果。
    • 通过鼠标滚轮,修改眼睛的位置,实现将物体拉进或者拉远。

    完整代码如下:

    头文件:

    #ifndef OPENGLRENDERWIDGET_H
    #define OPENGLRENDERWIDGET_H
    
    #include <QOpenGLWidget>
    #include <QOpenGLShader>
    #include <QOpenGLShaderProgram>
    #include <QOpenGLFunctions_2_0>
    #include <QOpenGLFunctions_3_3_Core>
    #include <QMatrix4x4>
    
    class OpenglRenderWidget : public QOpenGLWidget, public QOpenGLFunctions_3_3_Core
    {
        Q_OBJECT
    public:
        struct VertexAttributeData
        {
            // Postion
            float pos[3];
            float normal[3];
    
            float color[3];
        };
    
    public:
        OpenglRenderWidget(QWidget* parent = nullptr);
        ~OpenglRenderWidget();
    
        void setPoints(const QVector<QVector<QVector3D>>& points);
    
    protected:
        void initializeGL() override;
        void resizeGL(int w, int h) override;
        void paintGL() override;
    
        void wheelEvent(QWheelEvent * event) override;
    
        void mousePressEvent(QMouseEvent* event) override;
        void mouseMoveEvent(QMouseEvent* event) override;
        void mouseReleaseEvent(QMouseEvent* event) override;
    
    private:
        bool initShaderProgram(void);
    
        GLuint m_shaderProgramId;
        QOpenGLShaderProgram* m_pShaderProgram = nullptr;
        QOpenGLShader* m_pVertexShader = nullptr;
        QOpenGLShader* m_pFragmentShader = nullptr;
    
        GLuint m_nVBOId;
    
        // Attribute Location
        GLint m_nPosAttrLocationId;
        GLint m_nNormalLocationId;
        GLint m_nColorAttrLocationId;
    
        GLint m_mLocalMat;
        GLint m_vLocalMat;
        GLint m_pLocalMat;
        GLint m_nmLocalMat;     // 法线矩阵,模型矩阵的逆矩阵的转置
    
        // Model Matrix
        QMatrix4x4 m_modelMatrix;
        QMatrix4x4 m_projectionMatrix;
        QMatrix4x4 m_viewMatrix;
        QMatrix4x4 m_nmMatrix;
    
        QVector3D m_eysPostion;
        qreal m_rotate = 0;
    
        QVector<QVector<QVector3D>> m_polygonF;
        QVector<VertexAttributeData> m_vertexData;
    
        bool m_isPressed = false;
        QPoint m_pressedPos;
    
    private:
        // 光相关
        QVector3D m_lightPoint;     // 光源的位置(世界坐标)
        QVector3D m_lightColor;     // 光源颜色
    
        GLint m_lightPointLocal;
        GLint m_lightColorLocal;
    };
    
    #endif
    

    cpp文件:

    #include "OpenGLPolygonRender.h"
    #include <QDebug>
    #include <QMatrix4x4>
    #include <QTimer>
    #include <QWheelEvent>
    #include <QTime>
    #include <ctime>
    
    OpenglRenderWidget::OpenglRenderWidget(QWidget* parent)
        :QOpenGLWidget(parent)
        , m_eysPostion(0, 0, 1)
        , m_lightPoint(20, 20, -20)
        , m_lightColor(1.0f, 1.0f, 1.0f)
    {
        QTimer* pTimer = new QTimer(this);
        QObject::connect(pTimer, &QTimer::timeout, [&]{
            this->update();
        });
        pTimer->setInterval(30);
        pTimer->start();
    }
    
    OpenglRenderWidget::~OpenglRenderWidget()
    {
    
    }
    
    void OpenglRenderWidget::initializeGL()
    {
        this->initializeOpenGLFunctions();
    
        // 初始化GPU程序
        bool result = initShaderProgram();
        if (!result)
            return;
    
        m_shaderProgramId = m_pShaderProgram->programId();
        // 获取位置和颜色的locationID
        m_nPosAttrLocationId = glGetAttribLocation(m_shaderProgramId, "pos");
        m_nNormalLocationId = glGetAttribLocation(m_shaderProgramId, "normal");
        m_nColorAttrLocationId = glGetAttribLocation(m_shaderProgramId, "color");
    
        m_mLocalMat = glGetUniformLocation(m_shaderProgramId, "M");
        m_vLocalMat = glGetUniformLocation(m_shaderProgramId, "V");
        m_pLocalMat = glGetUniformLocation(m_shaderProgramId, "P");
        m_nmLocalMat = glGetUniformLocation(m_shaderProgramId, "NM");
    
        // 光相关
        m_lightPointLocal = glGetUniformLocation(m_shaderProgramId, "LightPos");
        m_lightColorLocal = glGetUniformLocation(m_shaderProgramId, "LightColor");
    }
    
    void OpenglRenderWidget::resizeGL(int w, int h)
    {
        this->glViewport(0, 0, w, h);
    
        return QOpenGLWidget::resizeGL(w, h);
    }
    
    void OpenglRenderWidget::paintGL()
    {
    //    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glEnable(GL_DEPTH_TEST);
        glClearColor(51.0f / 255.0f, 76.0f / 255.0f, 76.0f / 255.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // 使用shader
        m_pShaderProgram->bind();
    
    
    //    m_rotate += 1;
        m_modelMatrix.setToIdentity();
        m_projectionMatrix.setToIdentity();
        m_viewMatrix.setToIdentity();
    
        m_viewMatrix.lookAt(m_eysPostion, QVector3D(0, 0, -50), QVector3D(0, 1.0, 0));
        glUniformMatrix4fv(m_vLocalMat, 1, GL_FALSE, m_viewMatrix.data());
    
        m_projectionMatrix.perspective(45.0f, this->width() * 1.0 / this->height(), 1.0f, 100.0f);
        glUniformMatrix4fv(m_pLocalMat, 1, GL_FALSE, m_projectionMatrix.data());
    
        glBindBuffer(GL_ARRAY_BUFFER, m_nVBOId);
    
        // 设置模型矩阵
        m_modelMatrix.translate(0, 0, -30);
        m_modelMatrix.rotate(m_rotate, QVector3D(0, 1, 0));
        glUniformMatrix4fv(m_mLocalMat, 1, GL_FALSE, m_modelMatrix.data());
    
        // 设置法线矩阵
        m_nmMatrix = m_modelMatrix.inverted().transposed();
        glUniformMatrix4fv(m_nmLocalMat, 1, GL_FALSE, m_nmMatrix.data());
    
        // 设置点光源的位置和颜色信息
        float lightPosData[3] = {m_lightPoint.x(), m_lightPoint.y(), m_lightPoint.z()};
        glUniform3fv(m_lightPointLocal, 1, lightPosData);
        float lightColorData[3] = {m_lightColor.x(), m_lightColor.y(), m_lightColor.z()};
        glUniform3fv(m_lightColorLocal, 1, lightColorData);
    
        // 绘制三角形
        int nIndex = 0;
        for (int i=0; i<m_polygonF.size(); ++i)
        {
            int count = m_polygonF[i].size() - 1;
            glDrawArrays(GL_TRIANGLES, nIndex, count);
            nIndex += count;
        }
    
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    
        m_pShaderProgram->release();
    }
    
    void OpenglRenderWidget::wheelEvent(QWheelEvent * event)
    {
        qreal value = event->delta() / 100.0;
        qreal zValue = m_eysPostion.z() - value;
        m_eysPostion.setZ(zValue);
        qDebug() << m_eysPostion;
    
        this->update();
    }
    
    void OpenglRenderWidget::mousePressEvent(QMouseEvent* event)
    {
        m_isPressed = true;
        m_pressedPos = event->pos();
    }
    
    void OpenglRenderWidget::mouseMoveEvent(QMouseEvent* event)
    {
        if (m_isPressed)
        {
            int interval = event->pos().x() - m_pressedPos.x();
            m_pressedPos = event->pos();
            m_rotate += interval * 1.0 / 50;
            this->update();
        }
    }
    
    void OpenglRenderWidget::mouseReleaseEvent(QMouseEvent* event)
    {
        m_isPressed = false;
    }
    
    bool OpenglRenderWidget::initShaderProgram(void)
    {
        m_pShaderProgram = new QOpenGLShaderProgram(this);
    
        // 加载顶点着色器
        QString vertexShaderStr(":/vertexshader.vert");
        m_pVertexShader = new QOpenGLShader(QOpenGLShader::Vertex, this);
        bool result = m_pVertexShader->compileSourceFile(vertexShaderStr);
        if (!result)
        {
            qDebug() << m_pVertexShader->log();
            return false;
        }
    
        // 加载片段着色器
        QString fragmentShaderStr(":/fragmentshader.frag");
        m_pFragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
        result = m_pFragmentShader->compileSourceFile(fragmentShaderStr);
        if (!result)
        {
            qDebug() << m_pFragmentShader->log();
            return false;
        }
    
        // 创建ShaderProgram
        m_pShaderProgram = new QOpenGLShaderProgram(this);
        m_pShaderProgram->addShader(m_pVertexShader);
        m_pShaderProgram->addShader(m_pFragmentShader);
        return m_pShaderProgram->link();
    }
    
    void OpenglRenderWidget::setPoints(const QVector<QVector<QVector3D>>& points)
    {
        m_polygonF = points;
        m_vertexData.clear();
    
        for (auto iter = points.begin(); iter != points.end(); ++iter)
        {
            // 获取颜色值
            QVector<QVector3D> pointVec = *iter;
            QColor color((int)pointVec[4].x(), (int)pointVec[4].y(), (int)pointVec[4].z());
            pointVec.pop_back();
    
            // 获取法线
            QVector3D normalVec = pointVec[3];
    
            foreach (QVector3D pos, pointVec)
            {
                VertexAttributeData nData;
                nData.pos[0] = pos.x();
                nData.pos[1] = pos.y();
                nData.pos[2] = pos.z();
    
                nData.normal[0] = normalVec.x();
                nData.normal[1] = normalVec.y();
                nData.normal[2] = normalVec.z();
    
                nData.color[0] = color.redF();
                nData.color[1] = color.greenF();
                nData.color[2] = color.blueF();
    
                m_vertexData.append(nData);
            }
        }
    
        this->makeCurrent();
    
        int totalSize = m_vertexData.size() * sizeof(VertexAttributeData);
    
        // 创建VBO
        glGenBuffers(1, &m_nVBOId);
    
        // 初始化VBO
        glBindBuffer(GL_ARRAY_BUFFER, m_nVBOId);
        glBufferData(GL_ARRAY_BUFFER, totalSize, m_vertexData.data(), GL_STATIC_DRAW);
    
        // 设置顶点信息属性指针
        glVertexAttribPointer(m_nPosAttrLocationId, 3, GL_FLOAT, GL_FALSE, sizeof(VertexAttributeData), (void*)0);
        glEnableVertexAttribArray(m_nPosAttrLocationId);
        // 设置法线信息属性指针
        glVertexAttribPointer(m_nNormalLocationId, 3, GL_FLOAT, GL_FALSE, sizeof(VertexAttributeData), (void*)(sizeof (float) * 3));
        glEnableVertexAttribArray(m_nNormalLocationId);
        // 设置原色信息属性指针
        glVertexAttribPointer(m_nColorAttrLocationId, 3, GL_FLOAT, GL_FALSE, sizeof(VertexAttributeData), (void*)(sizeof (float) * 6));
        glEnableVertexAttribArray(m_nColorAttrLocationId);
    
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    
        this->update();
    }
    
    

    VertexShader

    #version 330
    
    attribute highp vec3 color;
    attribute highp vec3 pos;
    attribute highp vec3 normal;
    
    varying vec3 M_Color;
    varying vec3 M_Normal;
    varying vec3 M_Postion;
    
    uniform mat4 M;
    uniform mat4 V;
    uniform mat4 P;
    uniform mat4 NM;
    
    void main(void)
    {
        gl_Position = P * V * M * vec4(pos, 1.0);
        M_Color = color;
    
        M_Postion = mat3(M) * pos;
    
        M_Normal = mat3(NM) * normal;
    }
    

    FragmentShader

    varying vec3 M_Color;
    varying vec3 M_Normal;
    varying vec3 M_Postion;
    
    uniform vec3 LightPos;
    uniform vec3 LightColor;
    
    void main(void)
    {
        // 环境光
        vec3 ambient = 0.1 * LightColor;
    
        // 漫反射
        vec3 norvec = normalize(M_Normal);      // 法线,单位向量
        vec3 lightDir = LightPos - M_Postion;   // 从光源到点的向量
        lightDir = normalize(lightDir);
        vec3 diffuse = max(abs(dot(norvec, lightDir)), 0.0) * LightColor;
    
        vec3 result = (ambient + diffuse) * M_Color;
    //    vec3 result = M_Color;
        gl_FragColor = vec4(result, 1.0);
    }
    
    

    因为我这里的法线方向可能不准确,所以再计算漫反射的时候,我取了绝对值。

    3. CPU名字渲染工具

    使用CPU渲染,就要考虑两件事

    1. 投影
    2. 面消隐算法

    (1)投影

    关于投影,我这边与OpenGL的做法是一样的,使用的投影矩阵(视锥体),一个世界坐标系转换成屏幕坐标需要两步

    1. 将世界坐标转换成标准设备坐标
    2. 标准设备坐标转换为屏幕坐标

    关键代码如下:

    // 处理世界坐标系坐标转化为屏幕坐标
    QVector3D SoftRenderContext::disposeWorldToScreen(const QVector3D& worldPos)
    {
        // 标准化设置坐标空间
        QVector3D ndcPos = m_projectionMatrix * m_viewMatrix * m_modeMatrix * worldPos;
    //    qDebug() << "ndc Pos :" << worldPos << "; " << ndcPos;
    
        // 标准设备空间转成屏幕坐标
        QVector3D viewPos = toViewPort(ndcPos);
    //    qDebug() << "view Pos :" << viewPos;
    
        return viewPos;
    }
    
    1. 将模型坐标的点,经过乘以模型矩阵,得到世界坐标
    2. 再将世界坐标 * 视图矩阵 * 投影矩阵 等到标准设备坐标系中的点(取值在[0,1])
    3. 再将设备坐标转换成屏幕坐标,代码如下
    QVector3D SoftRenderContext::toViewPort(const QVector3D& pos)
    {
        QMatrix4x4 mat;
    
        mat.scale(m_nWidth * 1.0 / 2, m_nHeight * 1.0 / 2, 1.0f);
        mat.translate(1.0f, 1.0f, 0.0f);
    
    //    qDebug() << mat;
        //mat.translate(0.0f, 1.0f, 0.0f);
    
        QVector3D tempPos = pos;
        tempPos.setY(-1.0 * tempPos.y());
        return mat * tempPos;
    }
    

    (2)面消隐算法

    常见的面消隐算法有很多,有 Z-Buffer算法画家算法BSP树算法

    这里我们用的是 Z-Buffer算法 这种算法最简单,但是效率不高。
    遍历窗口的每一个像素,判断是否与多边形相交,如果相交则取Z值最小的颜色。

    这种算法的缺点就是渲染速度太慢(比乌龟还慢)

    具体细节可参考这篇文章
    https://blog.csdn.net/Jurbo/article/details/75007260
    https://www.cnblogs.com/wkfvawl/p/11911441.html

    关键代码如下:

    void SoftRenderContext::render(QPainter* painter)
    {
        m_isRendering = true;
        QRectF rect(0, 0, m_nWidth, m_nHeight);
        painter->fillRect(rect, QBrush(QColor(120, 120, 120)));
    
        painter->setPen(m_color);
    
        // 先转化 成屏幕坐标
        converToViewPos();
        if (m_destTriangleData.size() <= 0){
            m_isRendering = false;
            emit renderFinised();
            return;
        }
    
        qDebug() << "Start Render...";
    
        int number = 0;
        for (int x = 0; x<m_nWidth; ++x)
        {
            for (int y = 0; y < m_nHeight; ++y)
            {
                int value = number * 1.0 / (m_nWidth * m_nHeight) * 100;
                number++;
                updateRenderProgress(value);
    
                IntersectRayTriangle::Point p1(x, y, 20000);
                IntersectRayTriangle::Point p2(x, y, -20000);
    
                // 设置射线,平行于Z轴
                IntersectRayTriangle::Ray ray(p1, p2);
    
                float zIndex = 8000000;
                bool needShow = false;      // 是否显示
                QColor color;               // 颜色
    
                // 遍历所有的三角形, Z-Buffer (改进版本)
                for (int i=0; i<m_srcTriangleData.size(); ++i)
                {
                    IntersectRayTriangle::Point result;
    
                    // 获取处理后的三角形
                    IntersectRayTriangle::Triangle triangle = m_destTriangleData[i];
    
                    // 判断是否穿透该三角形
                    int isIntersec = IntersectRayTriangle::testIntersectRayTriangle(ray, triangle, &result);
                    if (isIntersec == 1){
                        needShow = true;
                        if (result.z < zIndex)
                        {
                            QColor c;
                            c.setRedF(m_srcTriangleData[i].p1.color[0]);
                            c.setGreenF(m_srcTriangleData[i].p1.color[1]);
                            c.setBlueF(m_srcTriangleData[i].p1.color[2]);
    
                            color = c;
                            zIndex = result.z;
                        }
                    }
                }
    
                if (needShow){
                    painter->setPen(color);
                    painter->drawPoint(QPointF(x, y));
                }
            }
        }
    
        m_isRendering = false;
        emit renderFinised();
    }
    

    完整代码下载:

    链接:https://pan.baidu.com/s/1wzlXdAoIu58IGLsqjPfh4w
    提取码:zd78

    展开全文
  • Qt(发音为“ cute”,而不是“ cu-tee”)是一跨平台框架,通常用作图形...更多Qt开发工具前往慧都网搜索QtQt广泛使用子类,尤其是在Widgets模块中。下图显示了其中一些继承关系: QObject是Qt中最基本的类。
  • Qt(发音为“ cute”,而不是“ cu-tee”)是一跨平台框架,通常用作图形工具包,它不仅创建CLI应用程序中也非常有用。而且它也可以在种主要的台式机操作系统以及移动操作系统(如Symbian,Nokia Belle,Meego ...
  • VS2015+QT5.9.2开发一个adb_tool.exe工具(二) 前言 上一次做完adb_tool.exe的demo之后,本来想...首先是直接使用adb命令操作并且实验,验证上面所讲的三个需求能否使用adb命令实现,然后再在QT界面实现功能,实验的结
  • 文章目录Qt5开发安卓调试工具(adb shell input以及adb shell screencap应用)一、前言二、技术点、技术点实现及坑点记录1、QProcess执行命令2、adb shell input发送安卓控制命令3、adb shell sreencap截取屏幕4、...
  • Qt(发音为“ cute”,而不是“ cu-tee”)是一跨平台框架,通常用作图形工具包,它不仅创建CLI应用程序中也非常有用。而且它也可以在种主要的台式机操作系统以及移动操作系统(如Symbian,Nokia Belle,Meego ...
  • QT开发之旅一DS7400主机调试工具

    千次阅读 2013-12-13 12:27:13
    接触QT三年有余,期间因为工作需要断断续续学习过,2010年开始接触,当时好像是4.7版本,现在都已经到5.2版本了,更新真快,前阵子安装了下5.2版本,还是有很多变化的,不过感觉好像编译速度慢了很多,而且编译出来...
  • Qt(发音为“ cute”,而不是“ cu-tee”)是一跨平台框架,通常用作图形工具包,它不仅创建CLI应用程序中也非常有用。而且它也可以在种主要的台式机操作系统以及移动操作系统(如Symbian,Nokia Belle,Meego ...
  • Windows下开发环境一般使用的Visual Studio,本人使用的是Visual ...在这里推荐一快捷的方式,使用微软的vcpkg来管理第方库,可以免去下载安装各种第方库的烦恼,Qt也可以使用这个工具进行下载安装。 如.
  • Qt之Android开发环境的配置

    千次阅读 2019-01-23 16:32:57
    文章目录前言上干货一、Qt 环境:Qt Creator 5.10.11、打开 Qt 的维护工具遇到第一问题2、Qt 安装完毕之后二、JDK路径配置1、JDK版本2、配置 Qt 的 JDK、Android的SDK路径配置1、Android Studio下载2、Qt 中 SDK...
  • 无力吐槽,这是我第遍写这了,到底是什么意思???我只要一贴代码,浏览器直接崩溃,呵呵了,我也是,我现在只要写完一段字我就保存,尼玛在掉我就不写了,写到word里面,再贴上来。 效果 ...
  • 发现onvif协议也具备了修改 亮度、色彩度、饱和度这三个参数,当然这三个参数我见过的摄像机厂家(主流的十几种)都具备,还有些大厂做的设备还提供了其他详细图片参数的设置比如ICAT。 通过万能的超级牛逼的抓包...
  • QTQt的自文档化工具qdoc

    千次阅读 2015-04-08 13:14:02
    在《人月神话》一书中,...在Qt开发中,就有这样的自文档化工具:qdoc。使用qdoc,通常分步走。 第一步,添加QDoc注释到必要的.cpp文件或.qdoc文件,在.h文件中添加QDoc注释是无效的。 第二步,编辑.qdocconf配置文件
  • Vtk,(visualization toolkit)是一开源的免费软件系统,主要用于维计算机图形学、图像处理和可视化。Vtk是在面向对象原理的基础上设计和实现的,它的内核是用C++构建的,包含有大约250,000行代码,2000多类...
  • 将编译出来的两qmake添加到qtcreater的工具链中 会发现出现错误 解决方法呢; 为arm版本的qmake建立一个工具链即可 这样问题就解决了。 qtcreater应用起来非常的方便, 不知到你
  • Qt库提供的只是最基本的组件功能,使用这些组件开发出来的软件基本上个性可言。如果开发的产品只讲究实用性,那么UI体验尚可搁置一边。如果要面向客户推广部署,那么改善一下UI视觉效果对于产品的推广也会有莫大的...
  • 原文标题: Snipaste - 开发年的截图工具,但不只是截图 原文作者: @levie
  • 近期做一播放器的项目,界面采用qt开发,需要用到第方库ffmpeg和sdl库。由于以前没有qt添加第方库的经验,因此在环境配置上费了不少功夫,因此在这里总结一下      在QT中,自动化编译过程,是通过qmake...
  • 在linux 下开发桌面应用,最常见的开发工具就是QT,而Python是开源世界的轻骑兵,学习简单,开发高效,因此有了 pyqt 这包用 python 来做 QT 开发。下面详细介绍其步骤。 一、基础环境配置 二、在centos 下...
  • 一、如何忽略C/C++红色错误波浪线? 答:打开vscode的设置选项(File ->...、C/C++老实自动更新还更新不成功,烦死,咋办? 答:离线下载安装包,然后Ctrl+shift+P ,Extensions:install fr...

空空如也

空空如也

1 2 3 4 5 ... 16
收藏数 313
精华内容 125
关键字:

qt三个开发工具