游戲、動漫、影視教育培訓機構。
中國動漫游戲職業教育領導品牌
當前位置:首頁 > 學習園地 > 正文

用Ogre實現《天龍八部》場景中水面(TerrainLiquid)詳解

2015-06-12 編輯:匯眾教育成都軟件實訓基地-岳老師

當水面填充做好以后,這個就不難處理了,就是在每個頂點生成的時候設置紋理坐標。第一層紋理是動態紋理,第二層是一維的深度圖紋理。

本文主要講的是《天龍八部》游戲中水面(TerrainLiquid)的具體實現,使用C++,Ogre1.6。

天龍的水面做的比較簡單,雖然沒有倒影,但動態紋理+深度圖做出的效果還行,看著不是特別假。

一般情況下,TerrainLiquid有一層動態紋理,有的還會有一層1D深度圖紋理,深度圖紋理用來控制不同深度水面的透明度。另外還會給出一個坐標,可以稱之為種子坐標,通過這個坐標可以填充整個水面??偟膩碚f要實現天龍的水面只要搞清楚兩個問題

1.如何利用種子坐標填充整個水面

2.如何利用深度圖紋理控制水面透明圖

文章最后我放了TerrainLiquid的代碼的鏈接,配合上篇隨筆給的地形Demo代碼再加上水面相關的資源,很容易就能在那基礎上加上水面效果。

用Ogre實現《天龍八部》場景中水面(TerrainLiquid)詳解

TerrainLiquid格式

<Objecttype="TerrainLiquid">

<Propertyname="material"value="haihuwater"/>

<Propertyname="position"value="2500636-4901"/>

<Propertyname="texturescale"value="0.25"/>

<Propertyname="depthtexturelayer.enable"value="true"/>

<Propertyname="depthtexturelayer.heightscale"value="0.008"/>

</Object>

上面是一個典型的TerrainLiquid的例子,

material,不用說了,材質

position就是我上面說的種子坐標,天龍不給出整個水面覆蓋的范圍,而只給出這個坐標,載入場景時實時填充

texturescale,這個值是用來確定第一層紋理坐標的,假設某個點與種子間隔(x,y)個頂點,則該點第一層動態紋理的坐標為(x*texturescale,y*texturescale)。

depthtexturelayer.enable,這一項如果是true的時候,說明要用深度圖。

depthtexturelayer.heightscale,是用來確定水面上某點的深度和該點的透明度間的關系,深度*這個值=透明度。

水面填充

開始要實現水面的時候,我首先想的很簡單,弄四個點,一個平面,動態貼圖一貼,完了。后來發現沒那么簡單,水面不能用一個長方形來做,多看幾個場景就能發現,這個肯定是不合適的。Google了一下,看了幾個大牛的博客,知道水面應該用填充算法來生成,可惜大牛們都不貼代碼,估計覺得太簡單了吧……只好自己實現一下。

我用的填充算法比較簡單,遞歸…沒有任何優化,但很易懂,很簡單。一般如果不是大的變態的水面應該沒有問題,而且這個填充的過程是在載入場景的過程中,也沒有什么優化的必要,估計再快也就快個一兩秒吧。

下面是核心的填充代碼,很簡單吧…

voidTerrainLiquid::__spreed(intx,intz,intdirection)

{

//判斷是否已包含該點和該點是否應該被看做水面的一部分

if(!__isGridContained(x,z)&&__isValidGrid(x,z,direction))

__addGrid(x,z);

else

return;

__spreed(x,z-1,UP);

__spreed(x,z+1,DOWN);

__spreed(x-1,z,LEFT);

__spreed(x+1,z,RIGHT);

}__isValidGrid()用來判斷該點是否是水面的一部分,簡單點說就是判斷地形上的這一點是否高于種子的高度,實際上判斷還是有點復雜的,如果單純判斷點的當前點的高度遇到復雜一點的水面情況就會出BUG,我的做法是分不同的方向分別判斷。具體代碼如下:

boolTerrainLiquid::__isValidGrid(intx,intz,intdir)

{

inty=mSeedPos.y;

intleft=mTerrainInfo->getOffset().x;

intright=left+(mTerrainInfo->getWidth()-1)*mTerrainInfo->getScaling().x;

inttop=mTerrainInfo->getOffset().z;

intbottom=top+(mTerrainInfo->getHeight()-1)*mTerrainInfo->getScaling().y;

Ogre::Vector3leftTop=__getPos(x,z);

Ogre::Vector3rightTop=__getPos(x+1,z);

Ogre::Vector3leftBottom=__getPos(x,z+1);

Ogre::Vector3rightBottom=__getPos(x+1,z+1);

intlt=mTerrainInfo->getHeightAt(leftTop.x,leftTop.z);

intrt=mTerrainInfo->getHeightAt(rightTop.x,rightTop.z);

intlb=mTerrainInfo->getHeightAt(leftBottom.x,leftBottom.z);

intrb=mTerrainInfo->getHeightAt(rightBottom.x,rightBottom.z);

//boundingcheck

if(leftTop.x<left||rightTop.x>right||leftTop.z<top||leftBottom.z>bottom)

returnfalse;

if(lt>leftTop.y&&rt>rightTop.y&&lb>leftBottom.y&&rb>rightBottom.y)

returnfalse;

elseif(dir==LEFT)

{

if((lt<y||lb<y)&&(rt>=y&&rb>=y))

returnfalse;

}

elseif(dir==RIGHT)

{

if((rt<y||rb<y)&&(lt>=y&&lb>=y))

returnfalse;

}

elseif(dir==UP)

{

if((rt<y||lt<y)&&(rb>=y&&lb>=y))

returnfalse;

}

elseif(dir==DOWN)

{

if((rb<y||lb<y)&&(rt>=y&&lt>=y))

returnfalse;

}

returntrue;

}首先判斷四個點對應的地形的高度是否都大于種子高度,若大于則返回false然后判斷是否超出地圖邊界,超出則返回false再分四個方向判斷前兩點和后兩點的高度,若前兩點有一點或兩點地形高度小于種子高度,且后兩點地形高度都大于種子高度,則返回false具體為什么這么判斷比較難描述…反正在邊界比較窄的情況下,若不這樣判斷就會檢測不到水面的邊界。

水面透明度處理

當水面填充做好以后,這個就不難處理了,就是在每個頂點生成的時候設置紋理坐標。第一層紋理是動態紋理,第二層是一維的深度圖紋理。第一層紋理坐標的設定要根據TerrainLiquid中texture_scale值來確定。由于Ogre默認的紋理映射方式是wrap,就是說Anyvaluebeyond1.0wrapsbackto0.0.Textureisrepeated(引用自Ogre官網的Manual).我們不需要考慮紋理坐標大于1或者小于0的狀況,它自己會映射到正確的位置,所以我們只要將(x,y)處點的紋理坐標設為(x*texture_scale,y*texture_scale)就OK了。

第二層紋理的形式是這樣的:(中間那一條)

用Ogre實現《天龍八部》場景中水面(TerrainLiquid)詳解

在ps里面看一下可以發現,這是一張寬度為256,高度為1的一維紋理,有4通道,每個通道的值都是從0-255遞增。不難推測,我們需要用水面的深度情況來取一個值作為水面的透明度。

從水面的紋理的材質可以看出,第二層紋理的映射方式為clamp,所以紋理坐標在在大于1.0時,會映射到1.0.所以(depthtexturelayer.heightscale*水面深度)求出的就是第二層的紋理坐標。水面深度=種子坐標高度-當前點的地形高度。

要注意的是第二層紋理一定要聲明為1D的。

下面是頂點格式的聲明,有FLOAT3的頂點坐標,FLOAT3的法線方向(統一向上),FLOAT2的一層紋理,FLOAT1的二層紋理。

decl->addElement(MAIN_BINDING,offset,VET_FLOAT3,VES_POSITION);

offset+=Ogre::VertexElement::getTypeSize(VET_FLOAT3);

decl->addElement(MAIN_BINDING,offset,VET_FLOAT3,VES_NORMAL);

offset+=Ogre::VertexElement::getTypeSize(VET_FLOAT3);

decl->addElement(MAIN_BINDING,offset,VET_FLOAT2,VES_TEXTURE_COORDINATES,0);

offset+=Ogre::VertexElement::getTypeSize(VET_FLOAT2);

if(m_bDepthEnable)

decl->addElement(MAIN_BINDING,offset,VET_FLOAT1,VES_TEXTURE_COORDINATES,1);將TerrainLiquid添加到地形中

在原來地形Demo中載入場景的函數中適當位置添加這一段應該就沒問題了。

elseif(IsStrEqual("TerrainLiquid",strTemp))

{

TerrainLiquid*pTerrainLiquid=newTerrainLiquid;

SceneNode*pSsceneNode=m_pSceneManager->getRootSceneNode()->createChildSceneNode("terrain_liquid"+StringConverter::toString(staticIndex++));

TiXmlElement*propriety=element->FirstChildElement("Property");

floatx,y,z;

floattexture_scale=0.0f;

floatdepth_scale=0.0f;

booldepth_enable=false;

while(propriety)

{

strTemp=propriety->Attribute("name");

sValue=propriety->Attribute("value");

if(IsStrEqual("material",strTemp))

{

sValue=UTF8ToANSI(sValue);

pTerrainLiquid->setMaterial(sValue);

delete[]sValue;

}

elseif(IsStrEqual("position",strTemp))

{

sscanf(sValue,"%f%f%f",&x,&y,&z);

pSsceneNode->setPosition(x,y,z);

}

elseif(IsStrEqual("texturescale",strTemp))

{

sscanf(sValue,"%f",&texture_scale);

}

elseif(IsStrEqual("depthtexturelayer.enable",strTemp))

{

if(IsStrEqual(sValue,"true"))

depth_enable=true;

else

depth_enable=false;

}

elseif(IsStrEqual("depthtexturelayer.heightscale",strTemp))

{

sscanf(sValue,"%f",&depth_scale);

}

else

ThrowException("TerrainLiquid",strTemp/Files/syqking/TerrainLiquid_src.rar);

propriety=propriety->NextSiblingElement();

}

pTerrainLiquid->createTerrainLiquid(Ogre::Vector3(x,y,z),texture_scale,depth_enable,depth_scale,mTerrainMgr->getTerrainInfo());

m_pSceneManager->getRootSceneNode()->createChildSceneNode()->attachObject(pTerrainLiquid);

}

標簽:Ogre   天龍八部   場景   水面   匯眾益智   匯眾教育   匯眾動漫游戲學院   文章鏈接:http://www.dostihyjc.com/learnnew_24.html
廣告位

匯眾教育移動網站二維碼

關于我們人才招聘網站合作聯系方式客服中心網站地圖
Copyright 2008 匯眾益智(成都)教育科技有限公司All Rights Reserved 蜀ICP備14022117號-1

亚洲人成网站免费播放_在线观看日本高清=区_五月天网站