`
jgsj
  • 浏览: 963332 次
文章分类
社区版块
存档分类
最新评论

一种让程序支持多渲染器的方法

 
阅读更多

一种让程序支持多渲染器的方法

在实际开发中,我们往往倾向于使用新的渲染器API进行图形的绘制,但是担心当部署到目标机器上时,由于硬件和显卡配置的限制而无法使用新的渲染器API。这可真让开发者们头疼。由于不同模型的格式不一样,渲染的方法也不尽相同,再加上渲染器的千变万化,让我们的工作量加大了很多!

一种解决方案是使用游戏引擎来解决问题。本文并不是介绍究竟如何使用这些引擎,而是提出了一种自己的方案,通过为每一个渲染器写一个渲染处理器(render handler)来化解这性能和兼容性的矛盾。

实例:我在开发的时候层考虑如果使用了在OpenGL1.5中提出来的VBO,那么移植到目标机器上时可能会抛弃那些并不支持OpenGL1.5规范的机器(因为可能显卡驱动没有安装等原因而无法支持OpenGL1.5规范)。不过使用glBegin()、glEnd()这样一个顶点一个顶点地添加的确会降低性能,想用VBO来提高性能。怎么办呢?

使用C++的多态机制可以解决问题。根据多次游戏程序编写的经验,我发现一般资源都会经过初始化、使用和回收三个过程,所以我将这三个过程作为纯虚函数提炼出来。成为了IRenderHandler接口。

class IRenderHandler
{
public:
   virtual void Init( void ) = 0;
   virtual void Render( void ) = 0;
   virtual void Release( void ) = 0;
};

IRenderHandler是一个接口类,它抽象了一段渲染过程,子类或实现可以重写这三个纯虚函数,做出不同的渲染效果。

一个很简单的继承例子是,如果应用程序需要DirectX和OpenGL的支持,那么简单地继承这个接口成为两个顶级接口即可。

class IGLRenderHandler: publicIRenderHandler
{
 
};
 
class IDXRenderHandler: publicIRenderHandler
{
 
};

更具体的情况,想要渲染各个模型,由于各个模型的渲染方式不一样,这样的情况必须再从中继承一个类以适应不同模型的渲染情况。

class MMDRenderHandler: publicIGLRenderHandler
{
public:
   virtual ~MMDRenderHandler( void ) { }
    ……
   void Init( void )
    {
       ……
    }
protected:
   QVector<quint16>           m_Indices;
   QVector<Material>          m_Materials;
   QVector<Texture2D>         m_Textures;
   QVector<Bone>               m_Bones;
   QString                     m_Dir;
 
    ……
};

对于OpenGL1.1规范和OpenGL1.5规范,再从中继承两个类以实现不同OpenGL规范的渲染。

class MMDRenderHandler_1_1:
       public MMDRenderHandler,
       protected QOpenGLFunctions_1_1
{
         voidInit( void )
{
……
}
void Render(void )
{
……
}
void Release(void )
{
……
}
}
class MMDRenderHandler_1_5:
       public MMDRenderHandler,
       protected QOpenGLFunctions_1_5
{
void Init( void)
{
……
}
void Render(void )
{
……
}
void Release(void )
{
……
}
};

我使用的是Qt,对于各种OpenGL规范,Qt为我们提供了各种程序集,我和大家一样,无法具体知道每一个函数是从OpenGL什么版本开始提供的,又有哪些函数从OpenGL什么版本开始消失的,还好Qt提供了QOpenGLFunctions_X_X这样的类,通过其保护继承可以让开发者知道在某个版本中OpenGL的函数是否可用,如果不可用,它会报编译错误(OpenGL1.1以及以前的函数除外)。

在Qt中有一个非常方便的函数,可以判断当前系统支持的最高的OpenGL版本,它就是QGLFormat::openGLVersionFlags( )静态函数。我们使用一个指针(或智能指针)保存渲染处理器的地址,像这样:

QScopedPointer<MMDRenderHandler>m_pRenderHandler;

根据系统环境来渲染的代码是:

// 看看OpenGL标准来决定渲染器
if ( QGLFormat::openGLVersionFlags( ) &QGLFormat::OpenGL_Version_1_5 )
{
   m_pRenderHandler.reset( new MMDRenderHandler_1_5 );
}
else if ( QGLFormat::openGLVersionFlags( )& QGLFormat::OpenGL_Version_1_1 )
{
   m_pRenderHandler.reset( new MMDRenderHandler_1_1 );
}

在开发中,我接触过一游戏引擎,我看到它们对渲染器的封装很深,不过不外乎在渲染时有BeginRender()和EndRender()这样的函数。BeginRender()这样的函数我想意义在于将顶点缓存暴露出来供用户填充数据进来,而EndRender()函数则是结束用户的顶点输入,提交顶点。这里BeginRender()和EndRender()看起来和glBegin()、glEnd()的作用差不多,但由于一些特殊而高效的数据结构使得游戏引擎的执行效率很高。BeginRender()和EndRender()这对函数还包含了对渲染器代码的封装,使得使用跨渲染器的游戏引擎成为可能。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics