公告板及精灵的本质特点就是可以很容易的完成渲染,但在实时3D几何图形处理中它们是否还具有优势…很好,实时3D。这有没有可能找到一种在两个世界中最佳的方法呢。
Billboards or sprites have the advantage of being very cheap to render, but real 3D geometry has the advantage of looking like it's... Well, real 3D. Wouldn't it be great if it were somehow possible to have the best of both worlds?
当然,在我没有告诉你那些准许你得到两个世界中最合理的那种技术之前我是不会这么问你的。这种技术的就叫做“视点替用特效(光源替用特效)”。
Of course I wouldn't ask you this if I wasn't about to tell you that there is a technique that allows you to get the best of both worlds. This technique is called "impostoring".
1、引言(Say what?)
视点替用特效本质就是精灵 ― 可替代实时3D模型的2D图像。但请把握住精灵是由美工手绘完成的,而视点替用特效则是由3D引擎本身实时进行渲染。
An impostor is essentially a sprite - a 2D image that replaces a real 3D model. The catch is that while sprites are usually drawn by artists, impostors are rendered at run-time, by the 3D engine itself.
让我们假设你的场景中包含了一棵用视点替用特效表现的树。3D引擎渲染的首先步骤是给3D树模型加入纹理,然后把你在场景中寄予的纹理当做一个公告板。只要照相机不移动太大,视点替用特效就能这样被保留。当照相机显著地移动时,引擎也只不过重新渲染下视点替用特效,并且,你可以很好的去获得其它的帧。
Let's say your scene contains a tree that will be represented by an impostor. The 3D engine first renders the 3D tree model to a texture, then places that texture in your scene as a billboard. As long as the camera doesn't move too much, the impostor can be kept as is. When the camera does move significantly, the engine simply re-renders the impostor, and you're good to go for another couple of frames.
你正在做什么,事实是,渲染对象一旦进行,接下来的重新使用,对于随后渲染的几帧图像而言几乎就是零成本。它显然能减少你庞大的多边形数量。当在使用“传统”公告板或精灵的时候,因为引擎需要频繁的对纹理进行更新,性能相比之下一定受到影响,而视点替用特效的更新仅仅与你的照相机位置发生联系,因此它们更为象实时3D对象。
What you're doing, in fact, is rendering an object once, and then reusing it in several subsequent frames at almost zero cost. This can obviously reduce your polygon counts quite a lot. While performance is a bit lower than using "traditional" billboards or sprites because of the frequent texture updates, impostors are updated to match your camera position, so they do look like real 3D objects.
2、创建视点替用特效(Creating an impostor)
当开始应用时,需为你欲要使用视点替用特效的每个对象创建一个新的纹理对象。当使用视点替用特效图像的时间到来时,请测定你3D对象的屏幕空间的包围矩形。这可保证对象包围盒投影到屏幕及指定的包围矩形内。最快的解决方案是使用包围球,如此一来你仅需投影两个点,当然你也可以使用包围盒。
When your application starts up,you'll need to create a new texture object for every object that you want to impostor. When the time comes to render the impostor image,you determine the screen-space bounding rectangle of your 3D object.This can be done by projecting the object's bounding volume to the screen and taking the bounding rectangle of the projection. The fastest solution is to use a bounding sphere,as you'd only need to project two points, but you can also use bounding boxes.
在你弄明白包围矩形时,就可利用当前照相机的位置对3D对象进行渲染了。而后你可用glCopyTexSubImage2D()复制包围矩形内的视点替用特效的纹理内容。
When you've figured out the bounding rectangle, you render the 3D object, using your current camera position. You can then use glCopyTexSubImage2D() to copy the contents of the bounding rectangle to the impostor's texture.
图1
纹理图像的分辨率应该取决于屏幕上3D对象的大小。假如对象当前占用了30x30的象素,你就不应该使用256x256的纹理来实现视点替用特效。相反的,你应该动态地调整纹理分辨率以最好的适应对象的需要。就保证纹理大小保持在2的幂,以满足放大或缩小时的需要。缩放纹理的大小应该调用glCopyTexImage2D()而不是glTexSubImage2D()函数。
The texture image's resolution should depend on the size of the 3D object on screen. If the object occupies 30x30 pixels, you shouldn't use a 256x256 texture to store the impostor. Instead, you can dynamically adjust the texture resolution to best fit the object. Just make sure that the texture size remains a power of two, and that you enlarge or shrink it when needed. Resizing the texture can be done by calling glCopyTexImage2D() instead of glTexSubImage2D().
3、渲染视点替用特效(Rendering the impostor)
既然你已为使用视点替用特效纹理做了必要的准备,在你3D对象的位置渲染一个对齐视窗的精灵应是件很简单的事。你需要做的唯一的事情就是确保视点替用特效不时地进行更新。
Now that you have the impostor texture ready for use, rendering it is simply a matter of placing a viewport-aligned sprite where your 3D object used to be. The only thing you need to do is make sure the impostor gets updated every now and then.
图2
第一种情形是当你屏幕空间的包围矩形改变大小的时候,需要重新创建视点替用特效纹理。其意味着照相机到对象之间距离的改变,因此视点替用特效的分辨率也可能发生适当的改变。对距离变化的回应是,你可维持视点替用特效纹理的纹素之间的一个几乎恒定的比率 ― 比率的最佳理想值应该是1:1。
A first situation in which you should recreate your impostor texture is if the screen-space bounding rectangle changes size. This means that the camera's distance to the object is changing, so the impostor's resolution may need to change as well. By responding to changes in distance, you can maintain an almost constant ratio between texels in the impostor image - ideally this ratio would be 1:1.
第二个需要更新的情形是,当对象与照相机观察矢量之间的角度发生大范围变化时。这显而易见的:如果你从一个不同的角度观察对象时,视点替用特效需要改变以反映你新的视点。
The second situation in which the impostor needs to be updated is when the angle between the object and the camera view vector changes by more than a few degrees. This is obvious: if you view the object from a different angle, the impostor needs to change to reflect your new viewpoint.
这里还有一个发现:如果照相机很靠近视点替用特效对象时,用户就可得知,其3D对象事实上就是一个精灵。当对象的屏幕尺寸达到某个阈值时,你可以依靠渲染一个真实的3D对象来代替视点替用特效以避免问题的发生。这不仅可防止用户察觉你正在欺骗他们,也保证了视点替用特效纹理的大小限制在一定的尺寸内。
There's one catch: if the camera comes very close to the impostor, the user will be able to tell that the 3D object is actually a sprite. You can avoid this by rendering the real 3D object instead of the impostor when the object's screen size exceeds a certain threshold. This not only prevents the user from finding out that you're cheating, it also keeps the impostor texture sizes within reasonable limits.
4、问题(Issues)
在你进而实现视点替用特效时,你可能会遇到一些问题。首先,你可能对视点替用特效的阿尔法通道感到疑惑。归根结底,这技巧的运作状况良好,视点替用特效需要带有透明背景。
There are a couple of problems that you might run into when implementing impostors. First of all, you're probably wondering about the impostor's alpha channel. After all, in order for this trick to work, the impostor has to have a transparent background!
假如你需要一个32位的颜色缓冲区,缓冲区内含阿尔法通道,命名为“destination alpha”。在正常的情形下,阿尔法目的单元格是从不用到的。当你调用glCopyTexImage2D(),及设置RGBA格式时,另一方面,OpenGL也将阿尔法目的单元格的内容复制到纹理上。将glClearColor()的参数alpha设以零可清除阿尔法目的单元格的信息。如果你接下进行视点替用特效渲染,你将在透明背景上得到一张不透明的图像,其正是你需要的。
If you reque