本文主要讲的是《天龙八部》游戏中水面(TerrainLiquid)的具体实现,使用C++,Ogre1.6。

天龙的水面做的比较简单,虽然没有倒影,但动态纹理+深度图做出的效果还行,看着不是特别假。

一般情况下,TerrainLiquid有一层动态纹理,有的还会有一层1D深度图纹理,深度图纹理用来控制不同深度水面的透明度。另外还会给出一个坐标,可以称之为种子坐标,通过这个坐标可以填充整个水面。总的来说要实现天龙的水面只要搞清楚两个问题

1.如何利用种子坐标填充整个水面

2.如何利用深度图纹理控制水面透明图

文章最后我放了TerrainLiquid的代码的链接,配合上篇随笔给的地形Demo代码再加上水面相关的资源,很容易就能在那基础上加上水面效果。

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了。

第二层纹理的形式是这样的:(中间那一条)

在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实现《天龙八部》场景中水面(TerrainLiquid)详解的更多相关文章

  1. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  2. [翻译]Unity中的AssetBundle详解(三)

    构建AssetBundles 在AssetBundle工作流程的文档中,我们有一个示例代码,它将三个参数传递给BuildPipeline.BuildAssetBundles函数.让我们更深入地了解我们 ...

  3. 「翻译」Unity中的AssetBundle详解(二)

    为AssetBundles准备资源 使用AssetBundles时,您可以随意将任何Asset分配给所需的任何Bundle.但是,在设置Bundles时,需要考虑一些策略.这些分组策略可以使用到任何你 ...

  4. 「翻译」Unity中的AssetBundle详解(一)

    AssetBundles AssetBundle是一个存档文件,其中包含平台在运行时加载的特定资产(模型,纹理,预制,音频剪辑,甚至整个场景).AssetBundles可以表示彼此之间的依赖关系;例如 ...

  5. ArcGIS Engine中的Symbols详解

    转自原文ArcGIS Engine中的Symbols详解 本文由本人翻译ESRI官方帮助文档.尊重劳动成果,转载请注明来源. Symbols ArcObjects用了三种类型的Symbol(符号样式) ...

  6. Android中的动画详解系列【4】——Activity之间切换动画

    前面介绍了Android中的逐帧动画和补间动画,并实现了简单的自定义动画,这一篇我们来看看如何将Android中的动画运用到实际开发中的一个场景--Activity之间跳转动画. 一.定义动画资源 如 ...

  7. spring在IoC容器中装配Bean详解

    1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean ...

  8. Java中日志组件详解

    avalon-logkit Java中日志组件详解 lanhy 发布于 2020-9-1 11:35 224浏览 0收藏 作为开发人员,我相信您对日志记录工具并不陌生. Java还具有功能强大且功能强 ...

  9. php中关于引用(&)详解

    php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...

随机推荐

  1. Windows代码页、区域

    目录 第1章代码页    1 1 代码页    1 1.1 单字节字符集    1 1.2 双字节字符集    1 1.3 多字节字符集    1 1.4 ANSI代码页    2 2 枚举代码页   ...

  2. struts2配置的ajax参数传递方法

    在action.java ,struts.xml,js代码文件中,需要约定配置相同的参数名字,否则获取不了. 如下图.

  3. 响应式web设计读书笔记

    1.媒体查询可以在链接link标签和具体的CSS中使用: 2.通过<link>标签的 media 属性为样式表指定设备类型和其他条件  中间用and和()来分隔,如下 <link r ...

  4. 5.5.8 XPath定位

    1.什么是XPath XPath定位方式是自动化测试定位技术中的必杀技,几乎可以解决所有的定位难题.它是XML Path语言的缩写,主要用于在XML 文档中选择文档中的节点.基于XML树状文档结构,X ...

  5. MATLAB时间序列预测Prediction of time series with NAR neural network

    具体请参考:http://lab.fs.uni-lj.si/lasin/wp/IMIT_files/neural/nn05_narnet/ 神经网络预测时间序列数据,有三种模型, 这里是给出的是第二种 ...

  6. Axis2 webservice入门--写个简单的webservice

    上一篇介绍了webservice开发前的准备.下面开始写webservice.如果不了解axis2请看上一篇,如果是新手:建议一边看一边写代码,自己动手完成这个过程. 一.新建一个web项目 二.新建 ...

  7. linux bash: sqlplus: command not found 错误处理

    在oracle用户下 ,执行sqlplus命令,抛出如上错误.   解决办法:   1.su oracle   2.cd /home/oracle   3. 执行命令 source .bash_pro ...

  8. 老男孩linux高级架构 百度云盘下载

    关于Linux的资源我了解还是比较少的,因为我最讨厌用命令行了(那是我大学时代的阴影啊!).这个资源收集很久了一直没有分享出来,因为我对Linux的了解真的很少,不知道怎么去描述,但是今天分享出来我想 ...

  9. cf 307

    一开始我是不想打的  又因为307这个数字太特殊了 毕竟307  希望今天的考试不要挂掉 http://codeforces.com/contest/551/problem/A #include< ...

  10. js继承实例

    第一种方法:对象冒充(临时属性) 借用临时属性,指向超类,末了删除 function Person(name,gender){ this.name=name; this.gender=gender; ...