显示文字_大规模文字快速生成和显示方法

综述

  在引擎中常用的汉字文字显示分二维或者三维文字显示,不论二维或者三维文字基本思路都是先找到汉字字体库,然后把字形提取出来进一步处理,方法基本分两大类,一种是直接剖分成网格显示,这种方法生成和显示速度慢一些。另一种是把字形做成纹理,这种相对于网格方法会快一点。

2:OSG中显示文字方法

  先用字体库生成相关的字形,然后把字形贴到四边形网格上形成纹理,然后绘制。主要代码利用的osgText库,这个库网上有相关的使用方法,具体了解这个库查阅相关资料和源代码,这个不多说。


  这个库提供了边框,补白,阴影等显示效果,在显示文字比较少的情况下是够用了。但是如果显示文字多的话,每一次都要生成字形,每一渲染都要上传纹理,生成速度,速度都比较慢。

3:大规模文字生成和显示

  目前市面上的引擎或者专门显示字体的库,一般都着重于显示效果,显示大数据量的文字就很慢了,基本都会卡死。本人针对大数量文字的显示提出了一个基本思路,基本方法就是用空间换时间,具体方法如下:


  做一个工具把所有的常用的汉字字形提取出来,然后把这些字形按照行列排成正方形成一个PNG文件,然后保存。等使用的时候把这个PNG文件读入内存,再一次性上传到GPU,显示具体文字的时候再去查找文字在PNG中的位置,然后把文字纹理从PNG图片中抠出来,再贴到四边形上显示出来。


  相关的实现类有QFontImplementation, 这个类主要用于生成字形。OSGBigTextImag, 这个类主要实现生成PNG的工具,显示文字。ShareTexture2D,这个类主要实现所有的图片共享纹理。


  这种方法有两个优点,第一个就是把所有的字形事先生成一个大PNG文件,所以生成文字速度块,第二个就是共享纹理大图片,省去了每次上传字形纹理的过程,所以显示速度快。

5129b12f03598400e29d11d3ded8acc7.png
这个是程序生成的常用汉字的字形组成的大纹理

4:源码解析

下面是部分代码注释:

这个静态函数就是生成工具,调用后直接生成所有文字的排列的PNG文件,下面分别
简单注释一下相关函数,更详细的注释可以直接看源代码。
void OSGBigTextImag::createTextImageTool()
{
    OSGBigTextImag tool;
    //32
    tool.getTxtText(32);
    tool.gridImage();
    tool.writeTextRowCol();
}

//把常用文字读进来,提取字形并保存。
void OSGBigTextImag::getTxtText(float size)
{
     std::ifstream ifs(text_path"text.txt");
     m_font.setPixelSize(size);

     while(!ifs.eof())
     {
         std::string text;
         ifs>>text;
        //生成字形。
         createTextImage(QString(text.c_str()));
     }

     ifs.close();
}

//把所有的文字排列成正方形,形成Image
void OSGBigTextImag::gridImage()
{
    if(m_images.size() == 0)
    {
        return;
    }
    //算出文字排列的正方形的边长
    m_gridrc = getTotalGrid();

    //写出文字在纹理图中的行列,用TXT文件保存
    std::map<QString, QImage*>::iterator it = m_images.begin();
    int index = 0;

    for(; it != m_images.end(); it++)
    {
      index++;

       m_images_vec.push_back(it->second);
       m_text_pos.insert(std::make_pair(it->first.toStdString(), index));
    }

    QImage *markerImage = new QImage(m_gridrc * m_images[0]->width(), m_gridrc * m_images[0]->height(), m_images[0]-     >format());
    QPainter *painter = new QPainter(markerImage);

    QImage* temp = NULL;

    for(size_t i = 0; i < m_gridrc ; i++)
    {
        for(size_t j = 0; j < m_gridrc; j++)
        {
            if(i * m_gridrc + j < m_images_vec.size())
            {
                temp = m_images_vec[i * m_gridrc + j];
            }
            else
            {
                std::map<QString, QImage*>::iterator tempIt = m_images.begin();
                temp = tempIt->second;
            }
            //32 48
            painter->drawImage(32 * j, 48 * i, *temp);
        }
    }

    std::ofstream ofs(text_path"bigTextTexture.dat", std::ofstream::binary);
    ofs.write(((char*)markerImage->bits()), markerImage->byteCount());
    ofs.close();

    std::ofstream infos(text_path"info.dat");
    infos<<markerImage->byteCount()<<" ";
    infos<<markerImage->width()<<" "    ;
    infos<<markerImage->height()<<" "   ;
    infos<<m_gridrc<<" "   ;

    infos.close();

    //写出文字及行列
    writeTextRowCol();
}

显示三维文字,points是文字显示的位置和向量,text就要显示的文字,width宽,height高,color颜色
osg::ref_ptr<osg::MatrixTransform> OSGBigTextImag::create3DtextFast(std::vector<osg::Vec3f> points, QString text, float width, float height, osg::Vec4 color)
{
    //生成矩阵,决定文字的位置和方向
    osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform();

    osg::Vec2f vecy = osg::Vec2f(points[1][0], points[1][2]) - osg::Vec2f(points[0][0], points[0][2]);
    vecy.normalize();

    osg::Matrixf trs;
    trs.makeTranslate(points[0]);

    osg::Matrixf rot;
    rot.makeRotate(osg::Vec3f(0, 0, 1), osg::Vec3f(vecy[0], 0, vecy[1]));
    osg::Matrixf mat = rot * trs;
    transform->setMatrix(mat);

    std::map<QString, osg::ref_ptr<osg::Geode> >::iterator itGeode = m_global_texts.begin();

    if(itGeode != m_global_texts.end())
    {
        transform->addChild(itGeode->second);

        return transform;
    }

    //先查找是否已经生成
    std::map<std::string, int>::iterator it =  m_text_pos.find(text.toStdString());

    float num = 0.0;
    if(it != m_text_pos.end())
    {
      num = it->second;
    }

    //计算文字在图片中所占的位置
   osg::ref_ptr<osg::Vec3Array> pos = new osg::Vec3Array();

    pos->push_back(osg::Vec3f(-width, 0, 0))    ;
    pos->push_back(osg::Vec3f(width, 0, 0))     ;
    pos->push_back(osg::Vec3f(width, 0.0, height)) ;
    pos->push_back(osg::Vec3f(-width, 0.0, height));

    osg::ref_ptr<osg::Vec2Array> tex = new osg::Vec2Array();

    tex->push_back(osg::Vec2(0, 0));
    tex->push_back(osg::Vec2(1, 0));
    tex->push_back(osg::Vec2(1, 1));
    tex->push_back(osg::Vec2(0, 1));

    color = osg::Vec4(num, 1.0, 0.0, 1.0);

    //生成四边形,把文字贴上去。
    osg::ref_ptr<osg::Geode> geode = createGeode(pos, osg::PrimitiveSet::QUADS, color);
    ( (osg::Geometry*)geode->getDrawable(0) )->setTexCoordArray(0, tex, osg::Array::BIND_PER_VERTEX);

    //所有文字共享的stateset
    ( (osg::Geometry*)geode->getDrawable(0) )->setStateSet( m_global_stateset );

    transform->addChild(geode);

    return transform;
}

5:性能分析


  目前的这种加速的优化方法,只用事先生成的一个大纹理,纹理只上传一次且用shader编程实现,生成和显示文件的速度非常快。


  这种方法每个文字只绘制一个四边形,越是绘制文字数量多,越能显示出优势, 预计显示几十万个文字甚至上百万的文字没有问题,已经达到了文字显示的最大速度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值