云雾散去,露出一片巨大而又古老、绵延的深林。数不清的远古铁杉,高出你而形成一个绿色的大教堂。叶子像彩色玻璃窗一样,将阳光打碎成一束束金黄色的雾气。巨大的树干之间,你能在远出制作出巨大的深林。 对于我们游戏开发者来说,这是一种超凡脱俗的梦境设置,这样的场景我们经常通过一个模式来实现,它的名字可能并不常见,那就是享元模式。
我能用简短的语句描述绵延不绝的深林,然而在一个虚拟现实游戏中去实现它却又是另一回事。当你看到各种形状不一的树填满整个屏幕时,在图形程序员眼里看到的,却是每隔60分之一秒就必须渲染进GPU的数以百万计的多边形。
我们谈到成千上万的树,每一颗的几何结构又都包含了成千上万的多边形。即便你有足够的内存来描述这片深林,到了需要渲染的时候,这些数据还必须像搭乘巴士一样从CPU传输到GPU。 每一颗树都有一堆与之相关联的数据:
- 交织成网状的定义了树的主干、树枝以及树叶的多边形。
- 树皮和树叶的贴图。
- 它在深林中的位置以及朝向。
- 大量的参数像大小、颜色等,这样每棵树看起来都不一样。
如果你将它在代码中表述出来,那么你将得到下面的东西:
class Tree
{
private:
Mesh mesh_;
Texture bark_;
Texture leaves_;
Vector position_;
double height_;
double thickness_;
Color barkTint_;
Color leafTint_;
};
数据很多,并且网格和贴图数据还挺大。整个深林的物体在一帧中被扔进去GPU就太多了,所幸的是,有一个很古老的窍门来处理这个。
关键的是,即便深林中有成千上万的树,它们大部分看起来相似。它们大部分都用同样的网格和贴图。这就意味着远近许多物体间都有着许多共性。
我们通过把物体分块能很明显地模拟这一切,首先,我们取出所有树共有的数据放到一个单独的类:
class TreeModel
{
private:
Mesh mesh_;
Texture bark_;
Texture leaves_;
};
游戏只需要一个这样的类,因为没有道理在内存中存有成千上万个同样的网格和贴图。因此,每一个树的实例,都用一个指向共享的TreeModel这个类的引用。这就意味着,在Tree这个类里有这么一个指针:
class Tree
{
private:
TreeModel* model_;
Vector position_;
double height_;
double thickness_;
Color barkTint_;
Color leafTint_;
};
你可以这样形象地描述:
这样能很好的在主内存中存储物体,不过这却对渲染没太大帮助。在树林搬到屏幕上之前,它必须自己搬运到GPU,我们必须用显卡能理解的方式来表达资源共享。