你先试着看看这段天空体的演示程序(skybox program),以为你的下一步做好铺垫:动态天空体。你也许曾经看过不少的例子,如Unreal或LithTech公司的游戏,及其它。其基本思路通常就是使用一个缺少现实色彩的静态背景图作为天空体。唉,至少你会想应该有一些移动的云彩吧。因此让我们来看看如何实现它。
If you've tried out the skybox program from the downloads page, you're ready to take the next step: animated skyboxes. You've probably seen examples of these in Unreal or LithTech games, or others. The basic idea is that a static background usually just isn't realistic. At the very least, you'll want some moving clouds. So let's find out how it's done.
假如你真的没有见过静态天空体的程序,我建议你首先看它一眼。以保证你在阅读此文之前知道它是如何工作的。
If you haven't seen the static skybox program on the downloads page, I suggest you take a look at it first. Make sure you understand how it works before returning to this article.
当做完那些事情以后,让我们决定它是什么,需要做那些工作。其可能有点类似于Unreal世界:
Now that we've got that sorted out, let's determine what it is that we want to do. Probably something like in Unreal:
虚幻游戏里的天空
在这里,我们实际上有三层天空体。在最外层,我们使用一些静态的背景材料,例如象星星。最里层则用了许多的“局部”细节,例如象山脉。在上面的截图中,月亮可能在最里层(它处于云层的覆盖下,而且很朦胧)。在这两层中,有个第三者 ― 移动云层覆盖物。
What we have here is actually a three-layered skybox. On the outermost layer, we have some static background stuff, like stars. On the innermost layer there's the more "local" detail like mountains. In the screenshot above, the moon is probably also on the innermost layer (it's never completely obscured by the clouds). Between these two layers there's a third with a moving cloud deck.
我们应该如何渲染分层的天空体?许多初学者可能会陷入多重纹理渲染的思路里。实际的操作比这个更简单:每层都绘制不同大小的天空体。其得到的结果有点象图1:
So how do we render a layered skybox? Many beginners seem to think that it involves multitexturing. Well, forget about that. the proper solution is even simpler: draw a differently-sized skybox for each layer. The result would look something like figure 1:
图1
工作到这里,我们还是无法让云层移动。让它们实现移动,有两个选择:变换整个云层体或变换云层体的相应位置。后者可能似乎更有趣些,但是要想办法创建六个位图以保证它们在任何情况下都能正确的排列,即使在改变云层相应位置的时候也一样。我不想这样。我仅想变换天空体本身,正确吗?
This works, but we can't make the clouds move yet. To make them move, there are two options: transforming the entire cloud box or transforming the box's texture coordinates. The latter might seem interesting, but try to think of a way to create six bitmaps so that they always line up perfectly, even when you start transforming texture coordinates. Can't find one? I didn't think so. So we'll just tranform the box itself then, right?
图2
我们能找到位于X或Z轴的体附近的旋转思路,如上图2所示。假如观察方向垂直于轴它看起来很好。但是当我们留意旋转中心时,其将破坏我们的幻想。我们因此要保证最外层要足够大,以便云层体永远不会与它相交叉。
We might come up with the idea of rotating the box around the X or Z axis, as in figure 2 above. This could look good if the viewing direction is perpendicular to that axis, but if we look along the rotation axis, we will see the center of rotation, which will ruin the illusion. We also need to be make sure that the outermost layer is large enough, so that the cloud box never intersects it.
说到这里我们可能还没理清头绪?在现实生活中,云层是绕回在无限苍穹上,但我并不希望为此绘制许多的多边形。让我们来试试比较好的近似方法:一个平面。我们可以在最里层的天空体与可滚动的图素纹理上绘制一个单一的多边形。然而,这方法也是有问题的,请看图3:
We don't really seem to be getting anywhere with this, are we? In real life, clouds are wrapped around a huge sphere, but we don't want to draw that many polygons. Let's try out the best approximation then: a plane. We can draw a single polygon above the innermost skybox and scroll a tileable texture on it. However, figure 3 shows that this approach also has its problems:
图3
图4
在云平面交叉的最外层,云会突然消失。这看上去会使人感觉象被包了起来,很不舒服。实际上,云平面的边缘在地平线位置或山顶以下位置会消失。如果我们把云平面的位置调到山顶以下,云平面就看不到了,但是它却会与山体交叉。要解决这个问题,我们必须保证先画云平面,然后再画山体。这很容易做到,但是要注意,不要把云平面画到深度缓冲区。同时注意:不要将云平面画到视点以下(图4中星号所示)。操作将产生如下效果:
The clouds will suddenly dissappear where the cloud plane intersects the outermost layer. This will make the viewer painfully aware that he's trapped inside a cube. Very annoying. In reality, the edges of the cloud plane would dissappear at the horizon, and probably end up below the mountain tops. So if we lower the cloud plane below the mountain tops, it's edges won't be visible - but the plane will intersect the mountains. To solve this problem, we need to make sure that the mountains always get drawn over the cloud plane. This is really easy though: don't draw the cloud plane to the depth buffer. Note that we can't lower the cloud plane below the viewpoint (marked with a star in figure 4). The algorithm would then look like this:
DrawOuterLayer;
glDepthMask(GL_FALSE);
ScrollCloudTexCoords;
DrawCloudPlane;
glDepthMask(GL_TRUE);
DrawInnerLayer;
画面上将出现漂亮的卷云。注意:看到地平线的地方不会产生这种效果。解决办法是在云平面的周围画上边框 ― 这样云的效果就是逐渐变淡,而不是消失。
And there you have it: perfectly scrolling clouds. Note that this doesn't work in skyboxes where you can see the horizon. To fix this, you might want to draw a border around your cloud plane that goes from fully opaque (at the sides that touch the cloud plane) to fully transparent (at the outer edges). This way the clouds will fade away at the horizon rather than just dissappear.
确切地说:你刚使用了令人满意地动态天空体。这技术确实能给你的3D引擎增色不少,而且它不要求过多的多边形:制作过程中最复杂的情况也只要21个多边形,这对任何3D加速卡而言都是小菜一碟。
That's it: you just made a nicely animated skybox. This technique can really add a lot of impact to your 3D engine, and it doesn't require a lot of polygons: the worst case described here requires no more than 21 - which is peanuts for any 3D card.