??xml version="1.0" encoding="utf-8" standalone="yes"?> 如下是最新公布的一D?#8220;CryEngine 3”引擎制作FPS游戏地图视频Q从制作到演CZ?分钟Q看了让人瞠目结舌,唏嘘不已?/p>
--------------------------------------
q篇文章U属是去q看郎教授的讲后的一时冲动,陆箋写了很久。但很多观点现在看来值得商榷Q可能是当时太激动了Q就着郎教授的理论往下分析了一下。修改的工作量太大也没时间就攑ּ了,原样贴出来供大家参考?br>---------------------------------------
产业链战争中的中国游戏业
《业链阴谋?br>在万众欢腄北京奥运会闭q后不久Q一本名为《业链阴谋》的图书悄悄q入市场Q引发了一늃潮。郎咸^教授再次向公众传输了他的新思想Q让因生zd“世界工厂”而无比骄傲的国h用另一U视角重新看世界Q重新认识中国制造业。随着q底金融啸下南方工厂的大批倒闭及外汇储备的大量贬|q种思想昑־来珍c?br>而与此同Ӟ中国|游业却一枝独UQ在寒冬中逆市增长Q而且自主研发的市场占有率辑ֈ59%。Ş势似乎一片大好,情况真的是这样吗?
-------------------------------------------------------------
与制造业的相同点
中国企业家进口机器设备,利用廉h力_力与原料Q按照外国的配方或技术标准生产大量廉价商品,大部分出口。我们的企业更像是代工厂。而游戏业的情况又怎么样呢Q来看看我们的一天吧?br>在珠三角的代工厂Q工Z按照标准开始生产X-box360L的芯片、主板与外壳Q技术架构与标准是IBM、ATI、SiS联合制定的,时尚的外观是国旧金qAstro Studios和日本大阪的Hers Experimental共同设计的。工厂把成品上交l新加坡公司伟创力、台湑օ司纬创、以及加拿大公司天弘Q最后交l微软验Ӟq往全世界的销售网炏V?br>在上分公司Q中国员工正忙着lX360游戏《末日战争》制作图像素材,有的负责模型Q有的制作脓图,有的制作动画?br>在北京某公司的游戏研发项目组Q策划清一色用着Microsoft的Office制作一个个扩展名ؓDoc、Xls的文ӞE序员?Microsoft的VisualStudio2005Q以ISO的ISO C为标准,或者直接在买来的Ogre、BigWorld或UE3引擎基础上略加修改,工使用Painter、PS?DSmax不断水U生产出大批素材?br>在上L某网吧,|游消费者像往怸h开以Intel、Nvidia芯片为核?国外围配g的电脑,q入Microsoft?WindowsVista操作pȝQ\由器自动分配了一个符合IPv6标准的IP地址Q消费者用微软的IEq入国发明的InternetQ连接到电信服务商的IBM服务器再q接到目标网站的服务器,不久打开一个国产网游,实际采用了Ogre、BigWorld或UE3引擎+Microsoft的数据库+国外皮?br>
一句话Q我们按照美国标准代工制造了大量游戏机、按照外国h的要求ؓơ世代游戏制作了很多术素材Q这些g能算入GDP与出口总额中去Q但我们却没有得CQ何实惠,国家没有正式的次世代L市场Q我们的企业无缘ơ世代。我们用美国的工具软g按照国的技术标准生产了一大堆国|游Q我们的消费者用着国标准的电脑玩着国标准的国产网游,我们早已习以为常?br>最早在中国讄研发中心的国际游戏巨头育,最l已中国作为外包基圎ͼ主要是次世代术。中国员工成了外企降低研发成本的重要环节。还有东星、维塔士?EPIC{。品多是家用游戏机的游戏,Ua用于出口Q因为国内没有这个市场。《生化震荡》《末日战争》等来次世代游戏中出C国h的n影。以《洛城大N》这ƾX-box游戏ZQ由国总部q行游戏架构与视觉风D计,香港工作室(Enlight Hong Kong StudioQ负责编E与详细术讑֮Q而美术执行部分全部来自大陆工作室QEnlight China StudioQ,包括建模、脓图、骨骼动Mq场动画制作。也是_中国D存的单机游戏业也是术外包了,q一点上是IT民工Q与q东那些造鞋造玩L工厂没有本质区别Q简直就是殖民地。只剩下大宇{寥寥几家还在苦苦支撑,其他的要么倒闭要么转型为网游?br>q些情况是不正常的。金p事长求伯君说q:“世界上没有哪一个民族愿意把作ؓ信息产业灵魂的Y件业完全徏立在他h的智慧上。全世界优秀软g的决战更是一U文化的撞Q一场智慧的较量Q?#8221;
从技术的角度Ԍ游戏产业铄g与Y件两部分l成Q分片、操作系l、API、引擎与工具、硬件代工与软g外包Q核心是摩尔定律。美国掌握着世界g的走向,摩尔定律推动着g的不断升U,也间接推动游戏技术的不断q化Q次世代游戏的出现刺ȀZ升g或购买新机器Q于是一个Yg互相促进的业链形成了。CSDNC有一U观点:软g产业基本形成“三梯?#8221;的国际分工格局Q外国牢牢控制着技术标准,军_技术的未来走向Q攫取高额利润。美国掌握了最先进的Y件技术,控制着软g开发^台和软g工具Q在全球软g产业链中居于领先CQ如DX11、VS2008?DSMax2009?Office2007。而中国企业在技术竞争力上与外国巨头的差距是来大了?br>中国消费者可以不买国产品牌的电脑Q但却不得不在兼Ҏ中用Intel的酷?CPUQNvidia的GeForce昑֍Q微软的 WindowsVista。中国玩家可以不玩外国游戏,但国产游戏中已经有不用的是外国的Ogre、BigWorld或UE3引擎。核心技术永q操于敌手,我们只是生外皮+l装而已?br>我们的确生活在太q盛世,但也的确生活在没有硝烟的产业链战争之中,无声无息之中已经蕴含了无限杀机。核心技术操于敌手就是中国游戏业与制造业的共性?br>-------------------------------------------------------
与制造业的差?br>
中国C会U学院研I员王立为,在开攄q程中,发展中国家的最大损p不是国外的订单,而是自己的市源?%的市场流失对国家发展构成伤宻I过10%构成实质性伤宻I过30%可以造成无法弥补的伤実뀂我们国家的GDP每年都在以两位数的速度递增Q进出口总额屡创新高。但中国q出口总额?60%都来自外资企业或外资控股企业Q其中高U技产品高达80%。国务院研究发展中心的研I报告指出,在中国已开攄产业中,每个产业排名?位的企业几乎都由外资控制Q中?8个主要业中Q外资在21个业中拥有多数资控制权。巨额利润被国外企业攫取Q中国制造业的利润率?%Q严重媄响中国企业的U篏与再循环能力Q而且严重威胁国家的战略安全。而游戏业的情况与之不同?br>中国游戏业的M是网游,占据?9%。而网怸单机是不同的Q这个市场暂时不是殖民地。中国网游市场暂时没有对外开放,而处于政府管制之中,政府是巨大的保护伞Q国家的一些政{限制了它的发展Q也限制了外资的q入Q所以目前民族业发展较快。GameRES论坛上,有些资深从业者尖锐的指出Q目前民族业处于闭兌守下抢占国内市场的阶Dc?br>在保护伞下,外资难以完全控制产业链,产业价值大部分向本土企业。由于外资不能以M方式参与q营Q只能寻找本土的q营商进行合作,外国研发商的分成比例大约?5%Q所以网游的产值没有完全流向外国,代理型企业的利润率是30%Q自ȝ发的|游公司利润率达?0%以上。在2004q代理盛行的时候,外国游戏一度占据了市场?0%Q吸C大量资金。目前自ȝ发占据了59%Q资金回W的比例来大Q情况对本土有利。即便是九城q样的纯代理型企业,在暴雪的盘剥之下Q三q来通过《魔兽世界》也U篏?0亿资金,保持了充裕的现金,为日后自ȝ发{型奠定了雄厚的基?br>
产业链也可以分ؓQ电信运营商、游戏研发商、运营商、渠道商、媒体。业链的上游是工具与流行内容,其中热门p与文学外资没有控Ӟ反而是国内企业通过收购控制了其中一部分Q如盛大的vҎ学网Q盛大文学)。外资在工具占优势,掌控了大部分关键基础工具与^収ͼ如WindowsVista?VisualStudio2008、UE3、WACOMl图ѝ酷?、高U服务器Q这些是国内无力掌握的;中游是研发,外国占优势,但由于文化差异,外国游戏在国内的竞争力相对减׃Q在自主研发能力不断提高的今天,民族企业的对外依赖度大大降低Q代理时的议仯力也相对提高Q可以与Ҏq行博弈Q代理难度也相对降低了;而下游是q营Q外资无法介入,只能获得一部分分成?br>与此同时Q盛大等公司已经开始整合业链Q如上游的网游引擎、文学网Q中游的工作室ƈ购与合作Q下游的渠道扩张与v外出口。在产业链战争中的战斗力指数正在不断增强。这些优势是外企无法获得的?br>抛开复杂的技术问题,换一个视角,产业链战争的关键在于资金的流向。从资金的角度来看,外国势力在上游赚取了工具的钱Q在中游获得了一些代理费Q在下游获得?5%的运营分成。所以整体上Q业h值的六成以上都回到国内,产业链战争中我方占主|有利于民族企业自w的资金U篏与@环,而这恰恰是业实力不断增强的关键。资金大部分向国内是中国游戏业与刉业的最大差异?br>-----------------------------------------------------------
长远发展的隐?br>
表面上,中国游戏业取得了胜利Q但除去政府的保护、国外的引擎与工具之外,又有几分功劳可以归功于自q强大实力呢?
从品牌的角度来看Q中国的知名游戏品牌q是太少Q还有没世界U的游戏大作。这个问题始l难以解冻I而这个问题不解决Q民族网怼业就像是暴发戯不是世界公认的尖游戏商。自ȝ发的瓉有很多。而这几年的《劲舞团》《魔兽世界》代理权的恶性竞争事件恰恰说明了民族企业的实力薄弱?br>从另一个角度看Q中国IT业已l|了,软g业的85%、硬件的大半都已l不属于我们了。我们生存在外国的技术标准之下,处于产业铄下游与外~,南北差异巨大Q不对等。网游市场的局部胜利不能改变IT市场整体的劣ѝ?br>没有否认q个行业规模很大Q发展迅速,但很多h都认为我们还是个弱国。没有自q核心技术、^台、国际大作、王牌制作h、知名研发团队、大发行商,所以很隄为游戏强国,而只是殖民地或即成为殖民地?br>什么是游戏强国Q日本有着NDS、Wii、PS3q_Q以此^台标准按照标准游戏类型打造的众多标准化王牌游戏,全世界的游戏公司都要跟着q个标准开发ƈ销售品,软硬件共同创造巨大的产倹{制定硬件标准、标准游戏类型,制定规则是一企业的选择Q这U优势是难以在短期赶上的。强国需要长期的渐进的积累,无数l节的锤|大跃q无用。美国日本这些强国用?8q时间才形成目前的局面。中国游戏业才不q?4q_而且发展受到大环境限Ӟ与国外差距巨大?br>
在政府的保护伞下Q民族业获得了暂时的主导地位。但问题在于Q一旦这个市场的开攄度扩大,如外资可以以合资公司或独资公司方式参与运营,那么以暴雪的狼子野心来推,整个产业价值流向外国的比例会大大提高,民族企业受到极大冲击?br>中国有不业都是在市场的不断开放中被外资控制的Q如化妆品、a料、机械、胶南民族企业的实力q不够强Q就像是孩子与职业招_手对战,败多胜少。从q个角度来看Q网怸与其他行业的情况极ؓcMQ不同的是这个行业还处于保护伞之下,没有完全开放。但M有开攄一天的?br>中国|游业的发展q几q已l逐渐慢下来了Q市模L一天会走向停滞。它不会无限扩张下去Q成熟的|游市场必然是低增长、低利润率与高竞争的。到那个时候,中国游戏业会走向哪里Q?br>
中国的游戏市ZM国家都不相同Q它?00亿元的网游市Zؓ表层Q以上千亿的元的家用机、掌机、PC单机C水货、盗版市Zؓ里层。目前国内企业只能从表层市场赚钱Q而对C市场无能为力。那个地下市场寄托着玩家对各q_单机游戏的热爱、有限收入的无奈、与对行业现状的妥协Q也象征着未来发展的方向。虽然单机游戏业q转衰Q彻底消亡,但h们的热情发炽烈?br>中国|游业的产值在全球游戏业中之占6%Q我们始l与巨大的家用游戏机与掌机市场隔l。我们的产业l构是世界上独一无二的中国特色的Q没有哪个国家是如此C赖网游。如今自ȝ发网游的瓉正与单机发展不力的情况密切相养Il验不Q缺乏可以利用的游戏原型Q而只是一H蜂地随大流Q缺乏自q独到思考与创造力?004q匿名h士的一火爆网l引发无数{载的奇文《一o所有游戏圈的兄弟汗颜的文章》中说的已经很透彻了?br>中国|游业能否在未来成ؓ世界W一Q还值得商榷。假设成功了Q那时中国也未必是游戏强国。因为网游在全球游戏业只是小弟,无法d取代庞大的单机市场?br>单机游戏如何从如今的术外包转向未来的重建国内单机游戏市场,杀入国际家用游戏机与掌机市场,q是未来必然要面临的问题。这也是中国要成Z界公认游戏强国的最大障。未来的产业链上游是游戏基础工具、^台制造、流行内容;中游是各q_各类型游戏研发;下游是销售、运营、培训、游戏衍生业。那时的产业链战争会更加_ֽQ中国真正与国际接轨Q在产业链战争中成ؓ游戏强国?br>--------------------------------------------------
参考资?br>《业链阴谋》作者:郎咸q?br>《利用外资八思》(来自《瞭望新d刊》)
《一o所有游戏圈的兄弟汗颜的文章》作者:|络匿名人士
《迷茫的E序员和中国软g业》(来自CSDNQ作者:wfwonde (逆风沉)
阿健的动漫游戏教室(来自天博客Q?br>癑ֺ癄
-----------------------------------------------
Ҏ如下Q引用)Qhttp://blog.csdn.net/kun1234567/archive/2008/04/11/2282761.aspx
CEGUI使用utf8~码格式。这意味着我们可以很简单的显CZ文?/p>
1、弄个包含中文的字体Q在q里我借用大多C子里?“CQ?windows/Font/simhei.ttf”文g。把q个文g拯到Datafiles文g夹的Font文g多w?/p>
2、随便照着一?.Font文gQ自己写一个simhei.font文g。可以用TXT写,然后保存Q有的朋友说需要保存ؓutf8~码格式Q实际上是不需要的?/p>
3、同时注意修改你加蝲到程序里的scheme文gQ将里面的字体文件设|成simhei.ttf。你也可以l用FirstWindowq个例子Q这L话直接修Ҏ代码里的字体为simhei.tff?/p>
4、现在在E序里进行字W编码{换,我拿代码说明问题Q?/p>
std::wstring aa = L"123中文abcあいうえ?;
char buff[128] = "";
WideCharToMultiByte( CP_UTF8, 0, aa.c_str(), aa.size(), buff, sizeof(buff), 0, 0);
button1->setText ( CEGUI::String ( CEGUI::utf8* )buff );
原理是这LQ对于utf8来说Q英文字W和ansi~码在内存布局上没什么区别,都是一个UCHAR。但是对于非英文字符Q则是UCHAR+UCHAR+UCHAR。如果我们手工进行编码格式{换,会比较烦琐?/p>
比较h的方法就是,我们先用WCHAR(unicode内存布局,UCHAR+UCHAR+UCHAR+UCHAR)来储存需要显C的字符Ԍ然后调用Win32API来帮我们把宽字符转换成char(多字节字W集内存布局)?/p>
q就是基本方法了Q然后我们可以根据这个{换方针,利用Win32API随意的{换字W编码格式,从而满程序中的各U需求?/p>
于是l箋Google(我很懒,别h能做的事情从来不ȝ自己Q懒得跟t代?Q结果还真让我找C两篇相关的文章:一份是千里马肝的《游戏中汉字昄的实C技巧》,另一份是免费打工仔的《让OGRE支持中文》。从中找C原因Q?/p>
通过跟踪调试Q发C问题所在,原来|魁R是他: const FontGlyph *Font::getGlyphData (utf32 codepoint) { } 原来CEGUIҎUnicode字符的编码顺序,为每256个字W分配一张纹理(例如~码0-255存放在纹理一Q编?68-1023 存放在纹理四Q。英文很Ҏ搞定了,那么几个字符一张纹理就够了Q可中文得靠运气了Q有时显C几个字p生成几张U理Q还要将每张U理用不需要的盔R字填满,x伤胦Q?/p>
发现了问题,我便按照千里马肝的思想对函数进行了攚w,用的文字攑օ一张纹理中Q因为纹理最大承?56个字Q所以,当汉字超q?56个时Q则不常用的去掉,新的字W写入?/p>
后来我发现汉字的引用没有太多的规律,常用的一千多汉字出现的概率没有那么悬D(废话Q要不怎么是常用呢!Q,没有办法很好地按照用的频率汉字限制在256个字以内Q写q纹理,q性一旦满了就字全部释放掉,重新写入。(也需有我没找刎ͼq请高手指教Q?/p>
代码如下Q?/p>
const FontGlyph *Font::getGlyphData (utf32 codepoint) { } void FreeTypeFont::rasterizeHZ (utf32 codepoint) { }
PagedGeometry?/span>OGRE引擎的一个插Ӟ它对Q无IP大区域的量|格的渲染提供优化策略。它非常适合于稠密的林和室外场景,那里有v量的树,草,岩石Q灌木丛{场景对象?/span>
2, PagedGeometry理
PagedGeometry class
该类负责加蝲那些需要立卻I或很快)可见的场景几何,而不加蝲其他的场景以节约内存I间?/span>PagedGeometry引擎可以通过诸如静态几何,imposters{方法去昄实体对象Q?/span>EntitiesQ。这些方法是与实体和场景相机的距,以及你的配置相关的?/span>
寚w面的加蝲是通过用户定义?/span>PageLoader来完成的。这P用户可以~程实现加蝲面Q无论它是来自硬盘上的文ӞE序产生的或是其他地斏V?/span>
GeometryPageManager
?/span>PagedGeometrycM使用Q用于管理分页面,在需要的时候做cacheing, deleting{工作。它通过分析摄像d计算面cathe的速度Qƈdelete那些已经l过了设定时间的不可见页面?/span>
调用PagedGeometry::addDetailLevel(), 创徏 GeometryPageManager ?/span>
addDetailLevel() returns a pointer to this page manager, allowing you access to some useful functions, documented below.
cL员函C有些注释?/span>DO NOT USE的,不能被用户用,它们是在PagedGeometry中被使用的?/span>
addDetailLevel()
Adds a detail level to the PagedGeometry object.
PageType 对该detail levelQ用h使用的页面类型。它不是一个函数参敎ͼ而是一个模板参数?/span>
maxRange q个detail level使用的最大距(摄像机的视距Q?/span>
transitionLength 期望的衰减{变长?/span> - Q可选)
CZQ?/font>
pagedTrees->addDetailLevel<BatchPage>(100); //Use batched geometry from 0-100
pagedTrees->addDetailLevel<ImpostorPage>(500); //Use impostors from 100-500
名叫pagedTrees?/span>PagedGeometry 的对象会?/span>0Q?/span>100单位摄像ȝ范围内?/span>batchedQ分批几何,静态几何)Q?/span>100Q?/span>500单位距离范围内?/span>imposters
如果已有的页面类型不合适,用户可以很轻杄d自定义的GeometryPage子类
transitionLength 参数在默认情况下是没有的Q但是用户可以自己添加,它的作用是ɾl节层次转变更^滑。但是注意,使用参数可能会是渲染的效率下?/span>10Q-30Q?/span>
3,面cd
Class GeometryPage
如上所_该类是一个虚基类Q用户可以扩展承该cd实现不同的替代被渲染实体的方式,?/span>batchedQ?/span>imposters{。如果你需要其他的面cdQ你可以自己实现一些?/span>
以下是用户必d子类中实现的虚函敎ͼ
virtual void init(SceneManager *mgr, Camera *cam) = 0;
virtual void setRegion(Real left, Real top, Real right, Real bottom) = 0;
virtual void addEntity(Entity *ent, const Vector3 &position, const Quaternion &rotation, const Vector3 &scale, const Ogre::ColourValue &color) = 0;
virtual void build() {}
virtual void removeEntities() = 0;
virtual void setVisible(bool visible) = 0;
virtual void setFade(bool enabled, Real visibleDist, Real invisibleDist) = 0;
virtual void update() {}
以下是页面管理器Q?/span>page managerQ如何去使用上面的函敎ͼ
1Q当PagedGeometry首先create一?/span>GeometryPageQ马上调用它?/span>initQ)Q该函数好像构造函C栗?/span>
2Q准备一个几何页面给待渲染实体,讄面区域?/span>
3Q添加实体对象到该页面,q初始化位置Q方向等?/span>
4Q添加实体到场景中的最后一步。在d完所有实体对象后自动调用?/span>
5Q从面完全删除实体对象?/span>
6Q整个面变ؓ可见?/span>
7Q设|页面的消减属性?/span>
8Q在帧@环中保证面几何的更新?/span>
class BatchPage
The BatchPage class把实体当作静态几何体来渲染?/span>
静态几何体Q?/span>StaticGeometryQ:在场景中有许多实体是从它们加入场景开始就不会Ud的,如房屋,树等Q?/span>OgreU情冉|供了StaticGeometryc?/span>,它允怽Ҏ?/strong>很多物体.q个通常要比手动?/span>SceneNodesd要快多了?/span>
class ImposterPage
the ImpostorPage class.把实体当?/span>impostorsQ一U布告版囑փQ看h像真实的实体Q来渲染
所?/span>pagecd中,q个是最快的Q它使用ImpostorsM替要渲染?/span>entitiesQ这P昑֍只要L染一pd?/span>2d imageQ而不?/span>3d mesh
class GrassPage
A custom page type designed specifically for use with GrassLoader.
GrassPage面cd是被设计来配?/span>GrassLoader使用?/span>
q种cd是没有做q优化的Q它只是Ҏl的entityq行克隆Q然后绑定到一个新的场景节点之上。这意味着性能很差?/span>
4Q?/span>BatchedGeometryQ一U静态几何的版本
class BatchedGeometry
它是Ogre::StaticGeometry的一个轻量版本?/span>
staticGeometry?/span>ogre提供l用LҎ染的c,它可以一ơ性渲染那些在场景中保?#8220;静态的”实体Q?/span>entitiesQ,如,房屋Q树{?/span>
它给用户提供多一些的?/span>batch材质{的控制?/span>
class SubBatch: public Ogre::Renderable
subBatchl承?/span>RenderableQ是BatchedGeometry的内部类Q它的构造函C传入BatchedGeometry指针?/span>submesh的指针,该类包含?/span>BatchedGeometry中?/span>
5Q?/span>Loading Entities
class PageLoader
虚基cR用户可以扩展它L供负责加载页面(PageQ的回调函数?/span>
PagedGeometry预装了几U不同的LoaderQ承于PageLoaderQ跟很多实体理器不同的是,PagedGeometry不允许用户一ơ性向objectd所有的entitiesQ由于它是用于大规模的游戏场景,所以它会对世界几何q行分页Q然后调度,只加载需要绘制的面?/span>
只要引擎需要确定世界几何的那一块区域需要被加蝲Q就要调?/span>PageLoader的成?/span>loadPage()。在函数中的PageInfol构体,提供了页面的包围体信息。在函数中,用户通过
addEntityQ)来添加实体到该页面中Q还可以定义实体的尺度,位置Q旋转等属性?/span>
当用户创Z自己?/span>loader对象后,必须其attatch?/span>PagedGeometry对象上去。如下:
pagedGeometry->setPageLoader(yourPageLoader);
PageInfo: l构体。提供给PageLoader的有用的面信息。基本的信息是定义被加蝲的区域,q有一些该区域的其他信息?/span>
l构体内包含了一?/span>TBoundQ?/span>typedef Ogre::TRect<Ogre::Real> TBoundsQ,所有在该页面内的实体都必须攄在这个矩形包围体之中?/span>
具体?/span>loadersQ?/span>
Q?/span>1Q?/span>GrassLoader l承?/span>PageLoaderQ用于和PagedGeometry一起生真实的草地场景。所谓真实的草地模拟Q?/span>GrassLoader成员函数addLayerQ)来添加草Q?/span>updateAnimation()来驱动动甅R?/span>
当用户创ZGrassloader对象后,必须其attatch?/span>PagedGeometry对象上去。如下:
pagedGeometry->setPageLoader(GrassLoader);
草地loader在用几何页面类型的时候,被推荐ؓ使用GrassPageQ这L渲染效率较高Q而采用其他类型则效率很低?/span>
同时提供了一个数据结构来完全控制Grass的属?/span> class GrassLayerQ它是通过addLayerQ)来添加的Q该cL供了一pd?#8220;set”ҎQ用于用户配|草地的材质Q密度,大小{属性信息?/span>
Q?/span>2Q?/span>TreeLoader2Dl承?/span>PageLoaderQ用于和PagedGeometry一赯易的实现在地形上攄树木的功能?/span>
使用Ҏ和前qC_使用ӞaddTreesQ)Ҏ向场景中d树木。需要注意的是,loader加蝲树木时ƈ不知道该地Ş的高度,而是假设高度?/span>0Q这需要设定获得当前(即给?/span>xQ?/span>z值时Q的地Ş高度?/span>Loader提供了这些方法?/span>
Q?/span>3Q?/span>TreeLoader3D?/span>2D的情况差不多Q只是效率大概低40Q左叟?/span>TreeLoader3D树在内存中打包的效率很高,大约10Bytes一|Q相当于100万棵树只?/span>
引擎?/span>2DQ?/span>3Dloader提供了一些公用的工具c,?/span>class TreeRefQ以内联函数的性质?/span>loader提供了位|,旋{Q尺寸,方向{的“get”数学q算。?/span>TreeIterator2DQ?span>TreeIterator3D则ؓloader提供了树指针的操作?/span>
一Q清除地形分U烦引缓冲及地Ş分页Q根据地形信息数据流加蝲地Ş信息配置文gQ?/span>
void TerrainSceneManager::loadConfig(DataStreamPtr& stream)Q其先将地Ş配置信息从数据流中逐一d到map中,然后通过void TerrainSceneManager::selectPageSource(const String& typeName, TerrainPageSourceOptionList& optionList)讄地Ş数据?目前只有高度图数据源)。在地Ş信息配置文g中可以配|多个地形数据源Q然后根据一U数据源cd生成地ŞQ参?typeNameq来指定数据源cdQ目前就是HeightMapQ找到指定的数据源后Q对指定的数据源q行初始化,
void HeightmapTerrainPageSource::initialise(TerrainSceneManager* tsm, ushort tileSize, ushort pageSize, bool asyncLoading, TerrainPageSourceOptionList& optionList)。初始化的过E主要是调用void HeightmapTerrainPageSource::loadHeightmap(void)高度图灰度囑փ数据加蝲到内存中Q如果是Raw数据Q就加蝲到mRawData中,否则加蝲到mImage中。整个加载过E其实完成了两大工作Q首先加载地形配|信息,然后加蝲高度图数据?
二:初始化分U烦引缓Ԍvoid TerrainSceneManager::initLevelIndexes()Q?
三:void OctreeSceneManager::resize( const AxisAlignedBox &box )
四:讄地Ş材质Qvoid TerrainSceneManager::setupTerrainMaterial(void)Q?
五:讄地Ş分页Q?void TerrainSceneManager::setupTerrainPages(void)
首先创徏一个名为Terrain的场景根节点的子节点
mTerrainRoot = getRootSceneNode() -> createChildSceneNode( "Terrain" );
然后初始化TerrainPage2D mTerrainPagesQ?/span>
最后调?void HeightmapTerrainPageSource::requestPage(ushort x, ushort y)requestPage只支持一个PageQ首先将囑փ数据q行~放
然后调用 “TerrainPageSource::firePageConstructedQ)”通知ListenerQ然后调?#8220;TerrainPage* TerrainPageSource::buildPage(Real*heightData, const MaterialPtr& pMaterial)”创徏一个新的TerrainPage对象?/span>
buildPage()是一个比较核心的函数。它首先构造一?TerrainPage对象Q然后创Z个用于容UTerrainPage对象的场景节点:“page->pageSceneNode = mSceneManager->createSceneNode(name);”Q然后根据对地Ş的分Ԍ循环创徏子SceneNodeQƈ且创建子场景节点对应的可渲染体TerrainRenderableQ将该可渲染体attach到这个子节点上。通过 “TerrainRenderable::initialiseQ)”来创建顶Ҏ据、渲染方式等?
然后调用void TerrainSceneManager::attachPage(ushort pageX, ushort pageZ, TerrainPage* page)加入到mTerrainPages中;
然后在attachPage中调?#8220;mTerrainRoot->addChild(page->pageSceneNode);”加入到SceneGraph中?
注:当前只支持一个PageQ该分页被挂接到一个称谓Terrain的场景节点上Q该场景节点下面又创Z很多子场景节点,每一个子场景节点对应一个tileQ也是一个独立的可渲染体TerrainRenderable
WorldTexture=terrain_texture.jpg //地ŞU理
DetailTexture=terrain_detail.jpg //地Şl节U理(相机距离地面很近旉用细节纹?
DetailTile=3 //l节U理在一个地形小块中的^铺数
PageSource=Heightmap //高度图数据源
Heightmap.image=terrain.png //高度囑U?灰度图文件名),W合2^n+1
PageSize=513 //高度囑֤?br>TileSize=65 //地Ş块大小
MaxPixelError=3 //军_使用层次l节时充许误?br>PageWorldX=1500 //地Ş在世界中的范围x方向
PageWorldZ=1500 //z方向
MaxHeight=100 //世界中地形最大映高?br>MaxMipMapLevel=5 //层次l节上限
#VertexNormals=yes //在缓冲中计算点法线Q计机光照或GPUE序用到时打开
#VertexColors=yes //在缓冲中讄点颜色Q假如有GPUE序需要时打开
#UseTriStrips=yes //对于现在的硬Ӟx
VertexProgramMorph=yes //使用点E序q行LOD融合处理
LODMorphStart=0.2 //LOD融合开始点Q高Q低LOD之间距离之比
下列参数用于提供自己的着色程序时使用Q这会提供自己定义的materialQ那么先前定义的
WorldTexture ?DetailTexture的设|不再用到?/p>
MorphLODFactorParamName=morphFactor
//假设VertexProgramMorph被设为yes,定制的material中包括一个高U顶点程序。它指定了一个顶?br>//E序的参数名Q这个参数用于融合LOD,参数g0Q?Q?表示不调_1表示完全调整C一ULOD
MorphLODFactorParamIndex //用于materail中包含低U顶点程序的情况Q意义同?br>CustomMaterialName //指定的materail名字
上述配置文g定义了基于高度图的地形?/p>
q些参数定义可概括ؓ两类QOgre使用W一cM高度图生地?br>mesh与材质?/p>
W二cL定制材质与GPU点E序Q这可以代替ogre自动产生的着色程序?br>另外的说明:
TerrainScenceManager会把高度囑ֈ为多个page,每个page由几个tilel成.它们都定义了在生的mesh中一l构成正方Ş的顶炚w?br>WorldTexture定义的纹理不必与目标地Ş一样大?br>PageWorldXQPageWorldZ可以~放世界中的地Ş?br>MaxHeight 在Y方向~放地Ş?br>DetailTexture 只用一个纹理,如用多层纹理,应该使用自定义materail?br>
从程序加载地?/span>
setWorldGeometry()有重载Ş式,一U用于加载配|文Ӟ另一U我们可在程序中使用Q以
辑ֈ手工加蝲的功能。这里,SceneData?typedef 为std:map,它存储了如我们在terrain.cfg
中看到那些值对。假设我们已l从某个二进制文件读入我们想要的内容到SceneData中。我们要?br>的就是把d的内容{换成setWorldGeometry()需要的cd?/p>
Introduction ![]() |
Hidden surface removal is among the biggest problems when writing a 3D engine. I struggled with it since the very beginning of writing 3D engines and still have no satisfactory solution to it. The ideal visibility detection scheme would allow unlimited data, extremely dynamic worlds and would have zero overdraw. The first 3d engine which implements these three demands still has to be written. The Z buffer for example allows dynamical worlds and even crossing faces, but it suffers from immense overdraw. The BSP-tree on the other hand, if well implemented, has no overdraw at all but needs that much pre-processing that dynamical worlds are a definite nono. It wasn't until recently i first heard of the octree, and I must admit i was struck by it's simplicity. I never actually implemented this structure and therefore I will present no pseudo code at all. This explanation is merely an attempt to make it more clear to people who have never heard of it. Besides, if you really understand the structure, then implementation is a piece of cake. |
The Octree Structure ![]() |
Our virtual world or level is actually nothing more then a soup of polygons. Some of you people might have thrown in a couple of curves and voxels but most of it will be polygons. Here it is:![]() Fig 1. Our little level. In the picture I just built a simple level containing no more than 250 polys. Now imagine a cube surrounding the world like in the next image: ![]() Fig. 2. Our little level surrounded by a cube. Now it isn't hard to see that this cube can be divided into eight smaller cubes, hence the name octree. Take a look at this picture: ![]() Fig 3. Our little level with the surrounding cube subdivided. Call the larger cube the parent and the smaller cubes the children. On their turn subdivide each children into eight smaller cubes, and you will notice we are creating a tree where each node has eight children. There is still one little problem left. When should we stop dividing cubes into smaller ones? There are two possible solutions. The first one is to stop when a cube has some size smaller then a fixed number. The second one is more common. You might have noticed that every child has less polygons then it's parent. The trick is to stop subdividing when the number of polygons in a cube is smaller then some fixed number. |
Creating The Octree ![]() |
Trees are recursion, recursion is trees. It is as simple as that. If you have a correct definition of you cubeNode it is very easy to create an octree recursively. First of all you check all polygons against the boundarys of the cube. This is very simple cause these boundaries are all axis aligned. This means that the cube has six plane equations, which are: Where Q is the position of one corner of the cube. This are very easy equations and the all parent polygons can very easily be checked against them. It could occur that a polygon crosses a cube boundary. Again two possible solution are at hand. First of all we could clip the polygon against the cube, which is simple, because of the axis aligned boundarys. On the other hand we could put the polygon in all cubes it is in. This means that some cubes can contain the same polygons. In order to prevent us from drawing one poly more than one time we should have a flag on each polygon which will be set if the poly is drawn for the first time. The implementation of an octree is very straight forward. I haven't done it myself yet, but I will soon. It is all matter of recursion. In order to construct a tree, the first thing you should think of is recursion. Whether we are talking about binary trees, quad trees or octrees, it doesnt matter, just build the darn thing recursively. So have a class definition of one cubeNode and put the creation of the tree in it's constructor. In this constructor you will call the constructor itself for smaller cubes. |
The Purpose Of The Octree ![]() |
An octree is actually nothing more then a data structure. It can be used for very different things. It is not only handy for visibility detection but also for collision detection, realtime shadows and many more things. The most important thing to understand about octrees is that if a parent is not important then it's children aren't either. Let's makes this a little bit more clear with an example. We will do this in 2d, which therefore resembles a quadtree, but with some imagination it can very easily be extended to 3d. Here we test the cubes (squares) against the viewing frustrum. Take a look at the next picture: ![]() Fig 4. An octree from the top and a viewing frustrum. In this picture a colored square that has one color was dumped without checking itKs children. As you can see some squares had to be checked all the way to the final node, but some large squares could be dumped at once. The colored squares are the ones that are outside the viewing frustrum and the greyscale ones are the one inside the viewing frustrum. As you can see this is actually a worst case scenario because the viewing frustrum crosses the middle of the surrounding square and therefore all the four children have to be checked. You could also apply the octree to many other things. Collision detection for example. Just check in which cube your bounding sphere or box is and you will only have to check against those faces. There are many more examples. |
Conclusion ![]() |
There is already a lot written about octrees. I tried to give my view on them in the hope somebody might read this. As you can see octrees are way easier to implement than BSP-trees (although some disagree) while offering a better structure. The main point about an octrees is that when a parent is discarded so are it's children. Actually that is all there is to it. Code clean, play Goldeneye and go vegetarian. Jaap Suter a.k.a ......... |
![]() |
||
? |
![]() |
||
? |
![]() |
||
? |
![]() |
||
? |
1Q固定着?span>(constant shading)
固定着色根本不考虑光照模型Q只是根据多边Ş颜色的烦引或RGB值绘制它?/span>
PS:基本已经被游戏淘汎ͼ太简单的着色模?/span>
2Q恒定着Ԍflat shdingQ?/span>
对于每个多边形只需要根据一个顶Ҏ行光照计,然后Ҏ计算l果Ҏ个多边Şq行着Ԍq就是恒定着Ԍ也称为面片着?span>(faceted shading)Q对于由q面l成的多边Şq种Ҏ是可行的Q立方体Q,但对于由曲面l成的多边Şq种着色方式会出现非你惌的结?span>.
3Q?span>Gougraud着?/span>
对于点之间的像素值采用两个顶点之间的插D来定Q顶点之间的颜色q渡自然Q渲染效果也比较qx,没有恒定着色那L兀?/span>
4Q?span>Phong着?/span>
?span>Gougraud着色类|不过优点在于q对每个像素q行了法U的插|对于镜面反射效果比较好?/span>
|
现在Ҏ点存储了世界中的全部点。这是,他还是不能给我们M好处Q因Zq会L有的东西。我们想把这个接点分?个部分。一ơ,我们划分Ӟ?个立方体包含那最初的Ҏ点的立方体。那意味着?个立方体在上面,4个立方体在下面。请看下图:
|
C图中黄色轮廓U没有?br> 我们刚刚把这个世界分?个部分。想象一下如果我们有2Q?Q?个部分,我们的世界将是怎样Q我们的摄象机在世界的中_向着后面靠右的角落。如果你看这些线Q你注意到在OCTREE?个结点中的第4个。这些结点包?个后面的和底结炏V这意味着我们只用d储在q些l点中的点?/font>
|
我们如何出我们要画的结点?如果你学q破片拣选(frustum culling),q将是很单的。你能得到这些破片的大小q检每个结点,看他是否被截断或在你的视觉破片中。有一个关于破片拣选的教程?a >http://www.gametutorials.com. 如果一个立方体截断破片Q我们就画q些l点。在q个例子中,我们切的数量是我们需要画?0%。记住,q只是我们世界中的一个部分。部分越多,我们将精。当Ӟ我们不会需要太多的炏V下面,我们来看一下下面的图:
|
在上面这q图中,你将发现许多的不同。这个例子中Q不是在原始?个立方体的每一个结点中建立8个新的立方体。原始的8个结点的和底面没有l分。你L把一个结点分?个或更多的结点,但是Q如果在q个面没有三角Ş可以存储Q我们将忽视q个l点q不l他分配内存。如果我们进一步的l分Q更多的l点Ş成原始的世界。如果我们变换到另一个部分,那立方体相似的变换。ؓ了说明这一点,L图:
![]() |
![]() |
在图中,有两个球Q但是方向相反。注意左边的部分Q他那世界分成2个结点,不是8个。这是因为球只要2个结炏V请看右边的球是不是和左边的很相伹{这q图告诉我们Q只昄需要的部分l点。如果没有三角Ş占用I间Q结点将不会建立?br>
何时停止l分
现在Q我们明白了如何l分Q但是我们也需要知道怎样停止l分。有许多的方法:
1。如果当时的三角形数量少于我们定义的最大的三角形数量,我们可以停止l分当前的结炏VD个例子,我们定义三角形的最大数量ؓ100。这意味着我们在细分结点之前,要检一下当前三角Ş的L量是否小于或{于我们定义的最大数量,然后再做军_。如果数量少于或{于Q我们将停止l分q指定这些三角Ş到那个结炏V请注意我们从不指定M三角形到Ml点Q除非他是末l点。如果我们划分一个结点,我们不能存储三角形到那个l点Q但是可以存储在他的子结Ҏ他们的子l点中,甚至C们的子中Q等{。当我们复习了怎样?C树后Q将会有更多的了解?br>
2。另一个方法是在停止细分时Q看我们是否l分的数目是否超q了l分的标准。例如,我们可以先徏立细分的标准?0Q如果细分的数量大于q个标准Q我们将停止q指定这些在立方体中q个面的点到那个结炏V当我们?#8220;大于q个标准”意味着l分的数量有11个?br>
3。最后的Ҏ是看l点数是否超q结点变量定义时的倹{例如,我们讄l点变量的gؓ500。每ơ,我们建立一个结点,相应的结Ҏ增加1。然后在我们每次建立另一个结ҎQ检一下我们当前的l点数是否超q结点变量。如果结Ҏ?01Q我们将不会l分q个l点Q但是指定他的当前顶点给他。我自己?1?的方法,但是1?也很好,因ؓ你可以测试不同的l分的标准?br>
怎样画OCTREE
OCTREE建立后,我们可以画我们需要的l点。那些立方体不是全部包含在我们的视觉中的Q只有一部分。这是我们Z么要计算每个l点的三角Ş的数量,如果我们只需要一个结点中的一部分Q这h们就不需要画成千上万个三角Ş。我们从根目录开始画OCTREE。对于每个结炚w存储着一个中心点。这是非常好的,比如在下面这个函CQ?br>
//q个函数取走立方体(XQYQZQ的中心点和他的大小Qwidth/2)
bool CubeInFrustum(float x,float y,float z,float size);
q个函数q回true or false,是由立方体是否在破片中决定的。如果立方体在破片中Q我们将所有他的结点,看他们是否在破片中,否则我们忽U树中的整个分枝。当我们得到了破片中的结点,但是没有Ml点在他下面。我们想ȝ点存储在末l点中。记住,只有末结Ҏ点存储。看下面的图Q?/font>
|
有阴q立方体是在破片中。白色的立方体不是在破片中。这里显CZl分?层?br>
OCTREE 中的冲突
OCTREE 不是用于渲染Q但是用于冲H检很好。随着游戏的发展,冲突也在改变,你将不得不在游戏世界中运用自q式你的角色或目标是否冲突。下面是一些冲H检的例子Q?br> 1。你徏立一个函数允怽转?DI间中的点到你的OCTREEq返回在q个点周围的点。你{递的那个Ҏ你的角色和目标的中心炏V这虽然可以工作一端时_但如果这个点靠近一个立方体的边时呢Q你的角色或目标可能和另一个立方体的顶点相冲突。ؓ了解册个问题,你将做一些事情。你可以转递角?目标的半径或一定的I间Q然后检半径或一定的I间是否和周围的l点相冲H。这׃的角?目标的Ş状决定。下面是一些典型的函数Q?br>
//q个函数取走角色/目标QXQYQZQ的中心点ƈq回靠近他的点
CVector3 *GetVerticesFromPoint(float x,float y,float z);
//q个函数取走角色/目标QXQYQZQ的中心点和半径Q然后返回和他冲H的l点中的点
CVector3 *GetVerticesFromPointAndRadius(float x,float y,float z,float radius);
//q个函数取走角色/目标QXQYQZQ的中心点和立方体的大小Q然后返回和他冲H的l点中的点
CVector3 *GetVerticesFromPointAndCube(float x,float y,float z,float size);
我相信你有更好的Ҏ快速的,在这里只是给你一点基?br>
ȝ
q篇教程只是l你讲截如何建立自己的OCTREE。关于这文章中的代码在www.gametutorials.comQ我希望q篇文章对你有用?br>
中文译者:antking
http://akinggame.gameres.com
q篇文章的英文版?a >http://www.gametutorials.com/Tutorials/OpenGL/Octree.htm
Ben Humphrey (DigiBen)
Game Programmer
DigiBen@GameTutorials.com
Co-Web Host of www.GameTutorials.com
SOURCES +=main.cpp
CONFIG +=qt
ok 保存?/p>
打开命o行,切换目录到hello.cpp所在目录。生成Makefile文gQ输入:
qmake -o Makefile hello.pro Q?
接下来生成项目文?vcproj文g?
qmake -tp vc -o hello.vcproj hello.pro
3Q直接?span class=ColorResultsClass highlight="true" realoffset="1237" alpha-value="20">Qt Visual Studio Integration v1.2.2 for.VS.2003.2005插g(详见http://blog.csdn.net/znf19850924/archive/2008/01/16/2047373.aspx)
需要配|?span class=ColorResultsClass highlight="true" realoffset="1384" alpha-value="20">如下Q?/strong>
"Tools" -> "Options" -> "Qt" -> "Builds", d我们刚才~译?span class=ColorResultsClass highlight="true" realoffset="1405" alpha-value="20">Qt代码,名字?Qt 4.3.2", 路径?yourqtinstallpath)
启动一个新的工E?
选择"Qt projects" -> "Qt Application"cd,输入工程名字,单击OK.
双击工程文g里面?test.ui",马上出现了所见及所得的H体~辑?在上面添加一个按?
双击按钮,产生相应的消息响应函?
d头文?
#include <QMessageBox>
在函数void Test::on_pushButton_clicked()体内d如下代码:
QMessageBox box(this);
box.setText("Haha, hit me.");
box.exec();
U理好像q有些问?...
模型贴花Q根据脓花所在的|格自动生成模型Q一般脓图)
投媄贴花Q利用投q理的方式实现的脓花(frustumQ?/p>
|格贴花Q创建指定大的|格贴花Q然后自动调整网格的位置QmeshQ脓图)