一、将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字体跟随屏幕缩放时,需要做额外的算法处理。