Qt 实现3D字体,并字体始终朝向屏幕

2 篇文章 0 订阅

一、将QString转成3D字体

基本思路:获取字体的轮廓,使用OpenGL轮廓绘制
使用的类和api:QPainterPath,toSubpathPolygons;

static QMap<QString, QList<QPolygonF>> m_3DtextBuffer; //缓存,减少耗时
if (!m_3DtextBuffer.contains(m_Text)) {
        QPainterPath path;
        path.addText(0, 0, QFont(), m_Text);
        QTransform T;
        T.scale(1, -1); //Qt窗口坐标转OpenGL屏幕坐标
        T.translate(-path.boundingRect().width() / 2.0, 0); //移至中间
        m_3DtextBuffer[m_Text] = path.toSubpathPolygons(T);
    }

二、字体朝向屏幕做的一些矩阵变换

基本思路:1、将字体的朝向转成相机的朝向;2、字体的高的方向转向屏幕Y轴的方向(或者宽的方向转向屏幕X轴的方向),这里屏幕Y轴(X轴)的方向实际上也就是相机的up方向和right方向;

//GLWidget继承自QOpenGLExtraFunctions; myPaint为自定义实现;
//projection透视矩阵,view视图矩阵
void GLWiget::myPaint(const QMatrix4x4& projection, const QMatrix4x4& view){
	QMatrix4x4 mt, mt2, mt3;
    mt.setToIdentity();
    mt2.setToIdentity();
    mt3.setToIdentity();

	//字体导入场景时的初始值,需要具体情况具体分析
    QVector3D NText { 0, 0, 1 }; //字体朝向 (字体厚度)
    // QVector3D WText{1,0,0}; //字体的宽方向
    QVector3D HText { 0, 1, 0 }; //字体的高方向

    QVector3D NCamera = m_CameraDir; //m_CameraDir相机的朝向,传入的值

    //求字体法线旋转
    QVector3D Naxis = QVector3D::crossProduct(NText, NCamera).normalized();
    auto theta = acos(QVector3D::dotProduct(NText, NCamera));
    mt.rotate(RADIAN_TO_DEGREE(theta), Naxis);

    //摆正字体
    QVector3D cameraUp = QVector3D::crossProduct(NCamera, QVector3D::crossProduct({ 0, 1, 0 }, NCamera));
    HText = (mt * HText);
    auto thetaH = acos(QVector3D::dotProduct(HText.normalized(), cameraUp.normalized()));
    auto angle = RADIAN_TO_DEGREE(thetaH);
    auto dir = QVector3D::dotProduct({ 1, 0, 0 }, cameraUp.normalized());
    mt2.rotate(angle * (dir < 0 ? 1 : -1), NCamera); //angel值始终都是正的,需要计算逆时针和顺时针下的旋转

    //往相机方向偏移一点
    mt3.translate(NCamera * 15);

    bool isDraw = true;
    QVector<QVector<QVector3D>> tempPolygons;

    for (auto polygon : m_3DtextBuffer.value(m_Text)) {
        if (polygon.isClosed()) {
            QVector<QVector3D> temp;
            for (int i = 0; i < polygon.size(); i++) {
                QVector3D oriantPos = QVector3D { (float)polygon[i].x(), (float)polygon[i].y(), 0 };
                QVector3D pos3d = mt3 * (mt2 * (mt * oriantPos)) + this->getPos();

                pos3d = projection * view * pos3d;
                if (pos3d.x() > 1 || pos3d.x() < -1 || pos3d.y() > 1 || pos3d.y() < -1) {
                    isDraw = false;
                    break;
                }
                temp.push_back(pos3d);
            }
            tempPolygons << temp;
        }
    }

    if (isDraw) {
        for (auto temp : tempPolygons) {
            glBegin(GL_LINE_LOOP);
            for (auto pos : temp) {
                glVertex3f(pos.x(), pos.y(), pos.z());
            }
            glEnd();
        }
    }
}

虽然2D渲染文字即可实现相关效果(始终朝向屏幕),但是3D字体的方式,可以轻松利用场景的深度测试,将不渲染被遮住的字体;
且2D字体跟随屏幕缩放时,需要做额外的算法处理。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在Qt实现记事本设置字体,可以使用QFontDialog类。以下是实现步骤: 1. 在Qt Creator中创建一个新的Qt Widgets应用程序项目。 2. 在mainwindow.h文件中添加一个QTextEdit控件。 ```cpp #include <QTextEdit> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); private: QTextEdit *textEdit; }; ``` 3. 在mainwindow.cpp文件中创建一个菜单项,并将其与QFontDialog类连接。 ```cpp #include <QMenuBar> #include <QMenu> #include <QAction> #include <QFontDialog> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // Create a text edit control textEdit = new QTextEdit(this); setCentralWidget(textEdit); // Create a menu bar QMenuBar *menuBar = new QMenuBar(this); setMenuBar(menuBar); // Create a menu QMenu *menu = new QMenu("Format", this); // Create a font action QAction *fontAction = new QAction("Font", this); connect(fontAction, &QAction::triggered, [=]() { bool ok; QFont font = QFontDialog::getFont(&ok, this); if (ok) { textEdit->setFont(font); } }); menu->addAction(fontAction); // Add the menu to the menu bar menuBar->addMenu(menu); } ``` 4. 运行程序,单击“Format”菜单,然后单击“Font”菜单项,将打开QFontDialog对话框,可以选择字体。 注意:在调用QFontDialog::getFont方法时,第一个参数是一个bool类型的指针,用于指示用户是否单击了“OK”按钮。如果用户单击了“OK”按钮,将返回所选字体;否则返回默认字体
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值