microsoftxiao

记忆 流逝

导航

聚焦3D地形编程第四章光照化地形

 

聚焦3D地形编程第四章光照化地形

翻译: 邵小宁 microsoftxiao@163.com 神杀中龙
原著  《Focus on 3D Terrain Programming》
翻译的烂请见谅

纹理化地形致使我们的地形进入一个新的细节等级,而光照化纹理将使整体进入新的真实感境界。问题是这样的:我们如何快速的光照我们的地形同时有保持高度的真实感?好,所有的技术让我将所有快速光照化地形的技术传授给你。我不会讲述复杂的全局光照算法
(虽然我将为你指出一些获取这些信息的地方)因为地形真实感大概需要整本书来讲述。好的,下面是本章的议程:

n         基于高度的光照

n         硬件光照

n         应用光照图到地形

n         超酷的斜坡光照算法

我会继续简短的讨论光照因为我了解你(否则如果你不知道,而我知道怎么做)想从比较酷的地形算法开始,我将在写三章就描述。让我们走吧!

Height-Based Lighting

Height-based lighting是简单且非真实的,但是它是光照的一种类型,所以我至少要简要的描述它。我们在第二章的demos里采用基于高度的光照,所以如果你不知道的话在这之前你就习惯了.

Height-based Lighting仅仅是——在顶点的高度上进行光照。高顶点(从地形碎片的高度数据)是比低顶点更亮一些,and that’s all there is to it.到那种程度我们需要使用我们的GetTrueHeightAtPoint函数(member of the CTERRAIN class)来求出在当前(x,z)位置(范围在0-255)的明亮像素从高度图里,and that is our brightness value. 它是简单的!如图4.1 加强这个概念。

在图中,顶点A几乎是黑的,顶点B有点亮,而顶点C将是完全被照亮了(白色)。你可以——用三段文字和一符图来结实基于高度的光照。

现在你知道了什么是height-based lighting, 但是你怎样计算你代码中的光照值呢?好的,它相当简单,考虑你已经加载了高度图。例如,假设你试着计算(157,227)的亮度。好的,顶点的亮度将从高度图中获取一个高度直,很简单。

ucShade = GetTrueHeightAtPoint(157, 227);

ucShade是一个我们存储光照值的变量,而GetTrueHeightAtPoint是从我们高度图中计算出信息在顶点(157,227)这个位置,范围从0()255(明亮)。现在,让我们为我们的光照添加上颜色。

Coloring the Light Source 光源色彩

我们并不总是想让我们的光颜色是灰色(黑到白)。大部分时候,我们喜欢我们光是各种各样的色彩。例如,如果没有云的时候,用户希望有漂亮的日落,所以我们希望我们的光颜色逐渐变为橘黄色,粉红色或者紫色。我们需要创建一个向量以存储我们的光颜色信息并且一个简单函数类设置光的颜色。(我们想将光的值指定在0.0f-.0f之间。你将在下一步找到为什么。)然后,我们可以找到前面的值再使用下面等式乘以每个RGB光颜色。

Intensity = shade*color

现在,使用那个等式,我们可以计算出RGB颜色的分量。然后发送给渲染API:

ColorToAPI((unsigned char)(ucShade*m_vecLightColor[0]),

           (unsigned char)(ucShade*m_vecLightColor[1]),

           (unsigned char)(ucShade*m_vecLightColor[2]));

ucShade是一个我们前面计算出的亮度值,而vecLightColor是我们光的颜色。现在拿出demo4_1(CDCod"Chaptter4"de4_1)如图4.2。如果你需要恢复demo的控制,看表4.1

当你看到这个图时,你注意到地形比较低的区域暗而高的区域是亮的。这说明height-based lighting正在发挥作用: 高区域亮,低区域暗。问题是这是什么算法?首先它是难以置信的不真实。这个算法不会考虑太阳光直接照到低区域的情形,而导致那个区域非常亮。问题如图4.3

你看,在4.3内,阳光照到顶点AB, 但是根据我们使用height-based lighting技术计算出来的光信息,顶点A是亮的而顶点B是暗的,而在这里这是错误的(如图所示)

另外的问题是这个技术给你带来了非常小的自由度对于你想要的地形光照。现在我们需要继续并讨论更灵活和真实的光照化地形。

Hardwre Lighting 硬件光照

这个几书有两个主要的问题。首先,它依赖于高级API所以我不会给你展示代码或给你演示。第二,它对于动态地形网格是没有用的——这个性质我们将在后三章来处理。因为这些原因,我将给你一些译本的细节实现。

硬件光照许哟啊你计算出每个三角形的面法线。最好的时候是在你demo进行预处理时;那样,程序的计算量不会陷入困境。之后计算法线,你仅仅发送它到API给当前三角形到你想要渲染的地方,然后就完成了。

警告

必须确认你恰当的在做任何事之前设置了你的API,开启硬件光照。硬件光照可以偶尔有个痛苦的甚至,所以如果你的地形没有或只有一点光请不要太惊讶。你需要确认你已经设置好了光源(使用正确的衰减,漫反射/镜面反射/环境光值日,等)。之后设置你的光,确定你开启了光源以及光分量的API。许多人向我问起关于硬件光照的问题, 75%都是他们已经忘记了开启他们的光源!这简直难以统计。

对于静态地形这个技术工作的很好其性质我们也已经在过去的两章使用了并且其中一个就是在本章使用的。它轻微的产生了动态光白天/野外模拟。然而,因为硬件光照主要基于顶点,动态地形网格加一点硬件光照看起来则不那么好。(动态网格经常移动顶点。)这是我们的一点硬件光照讨论。希望你别无视。

Lightmapping 光照图

遍及本书我们将使用光照图。一个lightmap正确的像高度图(在第2章讨论的)除了将高度信息进行取代,光照图包含的仅仅是光信息。所有加载,保存和卸载光照图的代码与高度图的一样(除了光照操作函数与高度图函数处理的变量不同), 所以我就不在这些函数上浪费宝贵的时间了。我们的光照图信息也将被存储在灰度的RAW纹理当中,就像我们的高度图,在光照图中除了只有属于光照的信息。例如,看高度图还有高度图4.4那么看到4.5达到的结果。看怎样将光照图影响到地形光照?

看图4.5光照怎样在球面通过球形来表示出图4.4的光照图? 这就是为什么我们使用光照图:可以为地形碎片定义出精确的光照类型。并且因为你可以一预先创建光照图,你可以使用各种各样的产生它们的算法。通过多种方式你可以产生光照图。这些方式很复杂——但是看起来很像全局照明技术。下一节我将为你展示创建光照图的方法。

之后你已经将光照图采用加载高度图的方式加载完毕,你需要创建一个函数将求出每个像素的亮度:

inline unsigned char GetBrightnessAtPoint(int x, int z)

{ return (m_lightmap.m_ucpData[(z*m_lightmap.m_iSize)+x]); }

记住我们怎样使用GetTrueHeightAtPoint来为height-based lighting获取亮度信息在demo4_2? 我们必须替换的调用GetBrightnessAtPoint并且设置它!看这些光照技术是多么容易?拿出位于CD上的Code"Chapter4"demo4_2demo4_2上,并且试着创建一些你自己的小的高度图然后观看它们开启和关闭的情形。我创建了如图4.6一个有趣的光照图,结果如图4.7

As if it weren’t obvious before, I am suffering from an extreme combination of having too much time on my hands and having far too much with this book!

光照图是种在游戏紧要时候强烈需要高级特性时的一种实现手段。我谈到了如何将其粘贴到地形上,但是更多高级光照图概念将集中在如何生成光照图。(我在稍后将展示给你这个算法。)

许多游戏,像Max-PayneQuake 2, 使用了radiosity方法。(Visit http://freespace.virgin.net/hugo.elias/radiosity/radiosity.htm 如果你喜欢技术解释。) 注意那是两款室内游戏,而地形是室外的话题,你也许会猜测,我必须找到另外的我们的计算光照图的技术。幸运的是,有许多不同的技术可以实现(几乎使人混乱,事实上)。我将提供一个简单的算法,但是之所以说它简单并不是说它不强大,我将在最后提及全局照明算法。

Slope Lighting 斜坡光照

Let me get this out of my system right now: 这是一个我使用的最长时间的算法。它难以置信的易于似乎用,并且它提供了非常锐利的视觉效果。 Slope lighting是一种简单的光照技术,用于根据顶点和相关附近的顶点的高度来进行着色。

Okay, Slope Lighting Is Cool, But How Is It Performed? 好的,斜面光照是酷的,但是怎样执行它呢?

斜面光到地形上,我们将获取从此顶点到当前顶点(方向将被指示通过光方向)然后减去当前顶点的高度。基本上,我们核对看如果其他顶点正被一个阴影投射到当前顶点。我认为这样现实例子的完美再现。假设你站在一个大楼的前面,阳光从你的视点照向你。这个大楼对你投射了阴影,如图4.8

如图所示,光源射线将不能照到你的位置,被大建筑阻挡了。结果是你位于阴影的区,使你表现出人获得的光射线很少而发暗。同理完成斜坡光照——着色顶点似的光源的射线被高的顶点所阻挡。概念如图4.9所示。

正如你看到的,光源发射出各种光线(在一个无限空间内,实际上)但是,这种情况下,它们将无法到达顶点B因为顶点A阻挡了光线。仅仅因为顶点B接受不到直射光不意味着将完全黑暗;一些光被其他的顶点接受并反射,产生轻微的照明。A顶点将不再完全黑暗。

这个算法中有一个缺陷,当你设置光方向时,它必须是45度的增量。例如,图4.1左边的光照使用(1,1)的方向。如果我们想移动光到左边,我们必须改变光方向到(0, 1)这将导致光移动了45度取代了平滑移动的过程。一次2-5度。

现在,即使你注意了,两个图区别不是巨大的。因此,你可以很容易的实时的改变光的方向。大部分用户不会注意到跳跃式的着色。

Create a Slope-Lighting System 创建斜坡光照系统

在开始编写slop-lighting代码前我们需要添加一点新的变量在CTERRAIN类内。(如果你认为这个类现在已经有太多特性了,just wait until later!)我们需要可以定义亮度的最小值/最大值给我们的地形,因为,我前面说过,很少有顶点完全变黑。我们也需要柔和光的变量和光的方向,both of which need to be used if we want to achieve realistic results. 另外,我们希望函数将容易让用户自定义slope lighting系统的参数:

inline void CustomizeSlopeLighting(int iDirX, int iDirZ,

                            float fMinBrightness,

                            float fMaxBrightness,

                            float fSoftness)

{

       // set the light direction

       m_iDirectionX = iDirX;

       m_iDirectionZ = iDirZ;

       // set the minimum/maximum shading values

       m_fMinBrightness = fMinBrightness;

       m_fMaxBrightness = fMaxBrightness;

       // set the light’s softness

       m_fLightSoftness = fSoftness;

}

Dynamically Creating Lightmaps 动态创建光照图

在这之前,你回想我说过光照图是危急时的算法?好,现在我们将为了使用地形而创建光照图。你可以选择为每帧计算光照图(我描述了这个过程不是很慢的算法。)但是好的计算方式是在开始时仅计算一次,然后在需要的时候再计算。这里首先我创建了函数的一半:

void CTERRAIN::CalculateLighting(void)

{

       float fShade;

       int x, z;

       // a lightmap has already been provided, no need to create one

       if(m_lightingType == LIGHTMAP)

              return;

       // allocate memory if it is needed

       if(m_lightmap.m_iSize!=m_iSize || m_lightmap.m_ucpData == NULL)

       {

              // delete the memory for the old data

              delete[] m_lightmap.m_ucpData;

              // allocate memory for the new lightmap data buffer

              m_lightmap.m_ucpData = new unsigned char[m_iSize*m_iSize];

              m_lightmap.m_iSize = m_iSize;

       }

       // loop through all vertices

       for( z=0; z<m_iSize; z++)

       {

              for( x=0; x<m_iSize; x++)

              {

                     // using height-based lighting, trivial

                     if(m_lightingType == HEIGHT_BASED)

                 SetBrightnessAtPoint(x, z, GetTrueHeightAtPoint(x, z));

              }

       }

}

直到这里,你将可以领会任何东西。我们预制作光照图出发。如果这么做了, 那么我们不能在光照图内写过多的信息。然后我们需要为光照图分配内存。之后,我们在所有顶点遍历,光照图需要和我们高度图有相同的大小——并检测用户是否使用height-based lighting。如果是,我们设置当前像素和高度像素相同。其余的函数将进行slop lighting吹,所以我分两段描述:

// using the slope-lighting technique

else if(m_lightingType == SLOPE_LIGHT)

{

       // ensure that we won’t be stepping over array boundaries by

       // doing this

       if( z>=m_iDirectionZ && x >= m_iDirectionX)

       {

              // calculate the shading value using the “slope lighting”

              // algorithm

              fShade = 1.0f-(GetTrueHeightAtPoint(x-m_iDirectionX, z-m_iDirectionZ) –

                            GetTrueHeightAtPoint( x, z ) )/ m_fLieghtSoftness;

       }

}

这里将发生slope-lighting计算。正如你看到的,我们减去前一顶点的高度值——在用户指定的方向上——即当前顶点。我们试图看到之前的顶点投射了多少阴影。我们然后切分光柔和且从此值减去1.0f之后我们再分开。我真的不在谈论怎样处理柔和光了,所以看图4.11, 使用两个等级不同的柔和光.

现在,放在这个函数里:

       // if we are stepping over a boundary, then just

       // return a very bright color value (white)

       else

              fShade = 1.0f;

       // clamp the shading value to the minimum/maximum

       // brightness boundaries

       if( fShade<m_fMinBrightness )

              fShade = m_fMinBrightness;

       if( fShade>m_fMaxBrightness )

              fShade = m_fMaxBrightness;

       // set the new brightness for our lightmap

       SetBrightnessAtPoint(x, z,

                     (unsigned char )( fShade*255 ) );

}

}

}

}

在这节,我们夹取fShademinimum/maximum亮度范围并且在光照图内设置当前点的亮度。demo4_3(CDCode"Chapter4"demo4_3)我添加了很酷的对话框(4.12)让你完全可以动态自定义slop-lighting系统。祝你愉快。

摘要 Summary

本章已经快速的从头到尾讲述了简单的光照技术以使你的地形的真实感提升一个层次。我们谈论了height-based lighting硬件光照, 光照图,还有斜坡光照。斜坡光照大概是你简单地形演示最好的选择。我没有时间提及非常酷的全局光照技术,在HoffmanMitchell的文章 “Real-Time Photorealisitic Terrain Lighting”,但是如果你对它感兴趣的话,它干脆而拥有价值。无论如何,你自己最好做好准备——你已经要进入地形编程的核心部分,将由所有高级地形算法信息组成。

参考 References

1 Van Noland, Charlie. “Slope Lighting Terrain. “ 2002.

http://www.gamedev.net/reference/articels/article1436.asp

posted on 2008-07-05 22:59  龙巢NET刀  阅读(888)  评论(0编辑  收藏  举报