• 使用Ogre快速渲染视频纹理


    基本思路

    测试时可使用和视频画面同大小的全屏四边形Rectangle2D,该rect使用动态纹理材质。

    渲染时按帧率动态替换该材质的纹理单元为当前帧图像

    视频读取

    读取每帧视频画面我使用的是OpenCV,类似如下:

        CvCapture* mCapture = cvCreateFileCapture(mFileName.c_str());

        int totalFrames = (int)cvGetCaptureProperty(mCapture, CV_CAP_PROP_FRAME_COUNT);

        int fps = cvGetCaptureProperty(mCapture, CV_CAP_PROP_FPS);

     

       IplImage* frame = cvQueryFrame(mCapture);

    frame即为捕捉到的画面,其中最需要的三个属性:

      width 图像宽

      height 图像高

      imageData RGB格式的数据区

     你可以使用任意数据源,最终的Ogre处理都是一样的

    第一次尝试

    首先想到的是直接使用已有的材质,通过卸载、加载纹理单元的方式:

    //卸载原纹理

    Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName("testMat");

    Ogre::TexturePtr textureOld = mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->_getTexturePtr(0);

    textureOld->unload();//可选

    Ogre::TextureManager::getSingleton().remove(static_cast<Ogre::ResourcePtr>(textureOld)); 

     

    //加载新纹理

    Ogre::DataStreamPtr pDataStream(new Ogre::MemoryDataStream(frame->imageData, frame->width * frame->height * 3, false, true));

    Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().loadRawData("testTexture",

        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, pDataStream, frame->width, frame->height,

        Ogre::PF_R8G8B8, Ogre::TEX_TYPE_2D);

    mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("testTexture");

     

    因为我们有原始内存数据frame->imageData,因此可以使用内存数据流,并调用loadRawData方法加载纹理

    按理说这种方法应该是很快的,因此只需担心能不能显示正确的画面

     

    运行之后,画面是显示出来的,但灰突突的,没有原先的光泽度

    想来大概是需要启用gamma矫正,即将纹理创建改写为:

    Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().loadRawData("testTexture",

        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, pDataStream, frame->width,

        frame->height, Ogre::PF_R8G8B8, Ogre::TEX_TYPE_2D, 0, 1.0f, true);

    最后三个参数,分别设置mipmap为0, 采用硬件gamma校正(如果用软件校正,即将1.0f改为2.2,将会惊人的慢)

    这一次,图像完全和原始视频相同了,但是帧率非常的低

    改进

    因为每次切换画面使用的纹理格式、大小都完全相同

    显然可以考虑直接替换已有纹理单元的内存数据区,而不是先销毁再创建,代码如下:

        Ogre::PixelBox box(frame->width, frame->height, 1, Ogre::PF_R8G8B8, frame->imageData);

        texture->getBuffer(0, 0)->blitFromMemory(box);

    这一次想来应该是足够快了,然而结果仍不理想

    跟踪下去,发现blitFromMemory 的类似实现最终都调用了D3DXLoadSurfaceFromMemory,性能的瓶颈都出在这里

    因为已经是D3D API,这条路也就不能继续走下去了

    另外虽然HardwarePixelBuffer的接口声明了writeData接口,但并没有实现,也是不能使用

     

    看来只剩下一种办法,通过lock复制数据,希望这一次可以成功了:

    void* dst = texture->getBuffer(0, 0)->lock(Ogre::HardwareBuffer::HBL_DISCARD);

    memcpy(dst, frame->imageData, frame->width * frame->height * 3);

    texture->getBuffer(0, 0)->unlock();

     

    这么做发现帧速确实上去了,但图像变成了重影的灰度图,显然是因为内存格式不一致

    好在看显示的图像高度好像正好是原来的3/4

    再考虑我们用的是RGB格式,许是因为硬件使用4字节RGBA存储像素的原因

    于是修改代码为:

    char* dst =(char*)( texture->getBuffer(0, 0)->lock(Ogre::HardwareBuffer::HBL_DISCARD));

    char* src = frame->imageData;

    for(int i=0; i<frame->width; ++i)

    {

      for(int j=0; j<frame->height; ++j)

      {

        *dst++=*src++;

        *dst++=*src++;

        *dst++=*src++;

        *dst++=0; //填充

      }

    }

    texture->getBuffer(0, 0)->unlock();

    啊哦,这一次终于成功了

    进一步改进

    使用的默认纹理单元时,缓冲区用途默认为TU_DEFAULT

    对于需要快速刷新的场景应使用TU_DYNAMIC_WRITE_ONLY_DISCARDABLE

    因此可以将纹理的创建改写为:

    texture = Ogre::TextureManager::getSingleton().createManual(textureName,

      Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, frame->width, frame->height, 0,

      Ogre::PF_R8G8B8, Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE, 0, true);

    这可以进一步提高性能

    附:在测试opengl渲染时,这么处理性能不能满足预期要求,大概是Ogre的实现问题

    我没有进行太多追究,如果谁正好知道,可以跟帖说一下

  • 相关阅读:
    例行更新,防止被踢
    C语言 遍历磁盘目录
    析构函数的调用
    数组学习笔记
    函数学习笔记
    c++语言 纯虚函数的使用
    c++语言 内联方法
    复制构造函数
    c++语言 覆盖成员函数
    面向对象程序设计
  • 原文地址:https://www.cnblogs.com/wiki3d/p/4677467.html
Copyright © 2020-2023  润新知