2008年11月19日

    水面原来采用渲染质量较高的菲捏珥水面,这样渲染出来的效果确实不错,不过这需要渲染折射图与反射图,再加上最后一遍场景渲染,整个场景需要渲染三遍,虽然GPU GEM2里面提到了这类水面的优化,经过项目中使用发现开销还是相当的大,外加上原来没有考虑边界柔和,导致水面切边非常锋利,所以决定采用较低的alpha水面混合来代替。

       接下来说说我采用的水面alpha混合方案,需求如下:

1.      水面只需要反射天空

2.      边界柔和,水深的地方反射强度大,也就是越看不清水底

3.      相机距离水面的距离越近,水面就需要越透明(试想相机看着人,而看不见水面下的情况)

根据以上需求,我决定采用在PS中做逐像素渲染。

1.      只反射天空虽然有时候看起来并不真实,但实现足够简单,效率高,这样就不需要反射图了,节省一个RT。简单的实现就是采用一张立方体贴图,然后根据相机与像素向量求个反射向量,再用texCUBE做个采样搞定。

2.      边界柔和看这张图,没有边界柔和的图很丑陋,像一把刀切过一样。这个可以通过做一张水深遮罩图来完成,这张图用基本的灰度图就可以了,平铺整个地形,可以这样生成:取像素任意一点,转换为世界坐标,求出与地形的交点,如果地形在上,则为全透明,填0,如果大于25.5则填1,中间做个线型混合 (byte)(abs(h) × 10)。这样得到的深度图效果如下:

 

如果把这张深度图做为alpha来输出,颜色为白色,我在场景中截了一张图,看,是不是有点样子了,岸边的锋利边缘被柔化了^_^

 

3      相机距离水面的距离越近,水面就需要越透明,这个只须在PS中用distance求出相机与像素的距离,然后通过求出的距离混合alpha就可以了。

看看加上颜色信息后的最终效果,反射了天空,柔化了边缘,效率也提高了,正是我们想要的。

 

 

再来一张:

 

 

 

 

转载请注明出处:

作者:gogoplayer

E-mail : gogoplayer@163.com

QQ : 78939328

http://www.gogoplayer.com.cn

posted @ 2008-11-19 15:46 gogoplayer 阅读(1623) | 评论 (9)编辑

2008年11月13日

 

     每盏灯都可以有镜面反射,最多支持三盏灯,再多就不能使用ps2_0,附上HLSL代码和执行文件,自己玩吧^_^

 

 

 

 1float4x4 World;
 2float4x4 View;
 3float4x4 Projection;
 4float4x4 WorldViewProjection;
 5float3 EyePosition;
 6
 7#define MaxLights 3
 8
 9float3 LightDirs[MaxLights];
10float4 LightColors[MaxLights];
11int LightCount;
12
13float4 AmbientColor = float4(0.05,0.05,0.05,1);
14float SpecularPower = 16;
15
16texture Texture;
17sampler TextureSampler = sampler_state
18{
19    Texture = (Texture);
20    AddressU  = Wrap;
21    AddressV  = Wrap;
22    AddressW  = Wrap;
23    MinFilter = Linear;
24    MagFilter = Linear;
25    MipFilter = Linear;
26}
;
27
28struct VertexShaderInput
29{
30    float4 pos : POSITION0;
31    float2 texCoord : TEXCOORD0;
32    float3 normal   : NORMAL;
33    float3 tangent  : TANGENT;
34}
;
35
36struct VertexShaderOutput
37{
38    float4 pos : POSITION0;
39    float2 texCoord : TEXCOORD0;
40    float3 normal : TEXCOORD1;
41    float3 view : TEXCOORD2;
42}
;
43
44VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
45{
46    VertexShaderOutput output;
47
48    WorldViewProjection = mul(mul(World, View), Projection);
49    output.pos = mul(input.pos, WorldViewProjection);
50    output.texCoord = input.texCoord;
51    output.normal = mul(input.normal, (float3x3)World);
52    output.view = EyePosition - mul(input.pos, World);
53    
54    return output;
55}

56
57float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
58{
59    float4 diffuseSum = 0;
60    float4 specularSum = 0;
61    for(int i = 0; i < LightCount; i++)
62    {
63        float3 L = normalize(-LightDirs[i]);
64        float3 N = normalize(input.normal);
65        float3 R = normalize(reflect(LightDirs[i], N));
66        float3 V = normalize(input.view);
67
68        diffuseSum += saturate(dot(N, L)) * LightColors[i];
69        specularSum += pow(saturate(dot(R, V)), SpecularPower);
70    }

71    
72    float4 textureColor = tex2D(TextureSampler, input.texCoord);
73
74    float4 final = AmbientColor + textureColor * diffuseSum + specularSum;
75
76    return final;
77}

78
79technique Technique1
80{
81    pass Pass1
82    {
83        VertexShader = compile vs_2_0 VertexShaderFunction();
84        PixelShader = compile ps_2_0 PixelShaderFunction();
85    }

86}

87

 

转载请注明出处:

作者:gogoplayer

E-mail : gogoplayer@163.com

QQ : 78939328

http://www.gogoplayer.com.cn

posted @ 2008-11-13 16:09 gogoplayer 阅读(1312) | 评论 (5)编辑

2008年11月6日

    新版Ogre的帧监听器(FrameListener)新加了一个方法,frameRenderingQueued,查看例子后发现,原先的frameStarted基本都被这个方法所代替了,决定打开源代码看看Ogre的意图。

    我们从renderOneFrame开始分析,这个方法只有三句话。

 1    bool Root::renderOneFrame(void)
 2    {
 3        if(!_fireFrameStarted())
 4            return false;
 5
 6        if (!_updateAllRenderTargets())
 7            return false;
 8
 9        return _fireFrameEnded();
10    }

11

1.      触发所有FrameListenerframeStarted

2.      _updateAllRenderTargets

3.      触发所有FrameListenerframeEnded

这个过程很清晰,不再多说,并没有发现frameRenderingQueued调用。接着进入_updateAllRenderTargets,这个方法也做三件事。

 

    bool Root::_updateAllRenderTargets(void)
    
{
        
// update all targets but don't swap buffers
        mActiveRenderer->_updateAllRenderTargets(false);
        
// give client app opportunity to use queued GPU time
        bool ret = _fireFrameRenderingQueued();
        
// block for final swap
        mActiveRenderer->_swapAllRenderTargetBuffers(mActiveRenderer->getWaitForVerticalBlank());
        
        
return ret;
    }

 

1.      更新所有渲染目标(不翻转)

2.      触发所有FrameListenerframeRenderingQueued(发现目标了^_^

3.      翻转所有渲染目标

由此可见,frameRenderingQueued的调用时机是在frameStarted的后面,frameEnded的前面,而且在frameStartedframeRenderingQueued还有一个更新所有渲染目标(不翻转),经过这样分析,调用关系就清楚了,简单归纳为下列五步:

1.      触发所有FrameListenerframeStarted

2.      更新所有渲染目标(不翻转)

3.      触发所有FrameListenerframeRenderingQueued

4.      翻转所有渲染目标

5.      触发所有FrameListenerframeEnded

通过分析后已经可以看明白调用过程了,接下来进一步说说,为什么要加入frameRenderingQueued呢,一个方法的加入总归是有原因的,大家都很忙,Ogre团队不会浪费时间加一个没用的东西。首先看原来用的frameStarted,可以看到frameStarted的调用时机是最靠前的,接下来是更新所有渲染目标(不翻转),然后是frameRenderingQueued。可以看到,frameStartedframeRenderingQueued的主要区别就在于更新所有渲染目标前调用还是后调用。如果把逻辑放在frameStarted中,那么更新所有渲染目标这个操作必然在其之后;反之把逻辑放在frameRenderingQueued中,则逻辑执行在更新所有渲染目标之后,那为什么要放在后面,而不是放在前面呢?原因在于渲染是由GPU来完成的,更新所有渲染目标这个操作返回时,GPU需要一定的时间来完成当前的渲染,这样当执行frameRenderingQueued时,相当于逻辑和GPU在并行计算,CPU也没有闲着,这样就提高了效率,效率很重要,不是么,^_^

这里还需要指出的一点是,由于frameRenderingQueued执行时,GPU已经在渲染上一帧内容了,因此写在frameRenderingQueued里的逻辑将在下一帧才能够在渲染中体现出来,一般来说,这个问题是无关紧要的。

 

 

转载请注明出处:

作者:gogoplayer

E-mail : gogoplayer@163.com

QQ : 78939328

http://www.gogoplayer.com.cn

posted @ 2008-11-06 16:17 gogoplayer 阅读(1135) | 评论 (3)编辑

2008年10月30日

      XNA2.0系统API居然出错!折腾了我N久。现象是鼠标射线不准,莫名其妙的不准,有时旋转一下相机就乱了,在官网论坛上找了一个替代版本,问题解决。这个问题XNA1.0并不存在,到2.0就有了,用反编译查看,果然是XNA1.0使用DX实现,XNA2.0是重写的方法。这个问题XNA论坛都提出来了,ViewPort.Unproject也算是一个比较重要的方法,居然到XNA3.0还存在,真不知道开发人员是怎么想的,为这个破问题折腾来折腾去,先前以为是相机问题,重写了好多遍,看来即使是官方API也不要过于迷信,这回主要就栽在这点。

经验总结:代码使人写的,不是神写的,人写的就会出错,就这么简单。最后附上可用的代替版本,看有多少可怜的孩子还在受到原API的毒害…

 

Code

 

 

转载请注明出处:

作者:gogoplayer

E-mail : gogoplayer@163.com

QQ : 78939328

http://www.gogoplayer.com.cn

posted @ 2008-10-30 10:55 gogoplayer 阅读(1415) | 评论 (5)编辑

2008年10月14日

 

          换装基本上是每个网游都必须有的一个功能,每种网游的做法都各有不同,有些是换掉整个模型,有些则是通过可以换掉模型的一个部分完成。前者属于整体换,相对简单些;后者则是通过部分替换实现,目前用的比较多,本文主要描述后者的。

       在开始描述换装前,首先要具备骨骼动画的知识,如果对骨骼动画的原理不熟悉,换装是比较难以理解的。换装的核心其实并不在换上,而是要理解为什么能换,而这些都和骨骼动画密不可分。骨骼动画是通过关键帧驱动骨骼运动,随之依次调整每块骨头的朝向和坐标,骨头再带动顶点运动(为了高效,现在很多都使用GPU加速,这样就不需要修改顶点缓冲区了),蒙皮信息就描述了了每个顶点受哪些骨头的影响,以及他们的权重,这样骨骼动画就运动起来了。其他的原理先略过,google上解释比我的好,^_^。

       目前一般网游的换装都是采用替换掉模型某个部分来实现的,脸型、头发、衣服、手套、裤子、裙子、鞋子等一般都和人物肉身做在一起,可以在需要时显示或隐藏对应部分。这里一般有一个原则,如果对应部分需要支持形变,则必须和肉身做到一起,因为必需要有蒙皮信息才能实现形变。

       在我这个设计中,人物分成六个部分,分别是头发,头,上身,手,腿,脚。骨骼只采用一套,男女共用,动作也分开来,每个动作都可以驱动骨架。每个部分都需要包含蒙皮信息,但不需要骨架,因为骨架可以共用,不需要保存多份,动作也一样,也只要一份就可以了,想要实现高效的换装,骨架和动作一定要支持共享。在Ogre中,骨骼和动作被合起来放到一个skeleton文件中,为了换装,自然要把这些东西分拆掉,难道天龙八部就是这个原因?(^_^,我猜的,可能是)

       分成六个部分后,就可以在需要是把对应部分替换掉实现换装,刚才说了,换装的难点在于为什么能换。能换的原因是在接缝的地方,只要没有接缝,这个部分就好像被换掉了,那怎么保证没有接缝呢,那就要使接缝处的顶点受到骨头影响的权重保持一致,只要一致了,换装就可以实现了。

再说点题外话,一般手里拿的武器属于挂接物体,不属于换装范畴,挂接相对来说更简单一点,创建一个节点绑定到某块骨头就可以了,这类物体的共性是物体只有刚性变化。还有一些换装是通过换模型的贴图来完成。总之,换装如果不考虑效率和资源共享并不难,换装的原理是何骨骼动画相关的,当然这只适用于本文提到的换装方式,如果你有其他更巧妙的换装方法,请告诉我,一起分享啊,^_^。

 

转载请注明出处:

作者:gogoplayer

E-mail : gogoplayer@163.com

QQ : 78939328

http://www.gogoplayer.com.cn

 

posted @ 2008-10-14 14:52 gogoplayer 阅读(1991) | 评论 (4)编辑

2008年5月9日

 

近日很多朋友咨询Overlay中文显示问题,回答的多了想索性再写个文档算了,放在网上共享,于是就有了本篇。

       Ogre1.2.5版本中,通过与Ogre官方论坛的开发者讨论实现了Overlay的中文显示,当初的实现非常的怪异,具体的实现可以参见Ogre官方论坛。

随着Ogre的更新,现在Ogre已经发布了1.4.71.4系列版本有一个重要的改进,就是加入了UTFString,这为Ogre中文显示予以很大的帮助。为了便于演示,我直接使用Ogre自带的Overlay,也就是大家熟悉的DebugOverlay,测试工程我选择Demo_ParticleFX,选择其他的也没有关系。现在编译它,运行后得到下图:


图的最左下角显示的就是英文DebugOverlay,接下来我们的任务就是把它编程中文的,^_^

Overlay中文化操作步骤如下

1.      打开OgreSDK\media\packs\ OgreCore.zip

2.      打开C:\WINDOWS\Fonts,把simhei.ttf添加到OgreCore.zip,(什么,没有simhei.ttf这个文件,那就还其他的中文ttf字体吧)。

3.      打开OgreCore.zip中的Ogre.fontdef,里面有BlueHighway这个字体定义块,在他的下面添加我们的SimHeicode_points里面的一大堆数字看不明白没关系,随后文章会解释。

SimHei

{

       type             truetype

       source         simhei.ttf

       size              16

       resolution   96

       code_points 33-166 24403-24403 21069-21069 24103-24103 36895-36895 29575-29575 24179-24179 22343-22343 26368-26368 39640-39640 20302-20302 19977-19977 35282-35282 24418-24418 25968-25968 37327-37327 25209-25209 27425-27425

}

4.      打开OgreCore.zip中的OgreDebugPanel.overlay,把BlueHighway全部替换成SimHei,我们要使用中文字体了,嘿嘿。

5.      修改完成后,确保所做的修改已经保存到OgreCore.zip

6.      进入Ogre解决方案,打开文件ExampleFrameListener.h,把54-59行的代码替换如下:

              static String currFps = "Current FPS: ";

              static String avgFps = "Average FPS: ";

              static String bestFps = "Best FPS: ";

              static String worstFps = "Worst FPS: ";

              static String tris = "Triangle Count: ";

       static String batches = "Batch Count: ";

 

              static DisplayString currFps = L"当前帧速率: ";

              static DisplayString avgFps = L"平均帧速率: ";

              static DisplayString bestFps = L"最高帧速率: ";

              static DisplayString worstFps = L"最低帧速率: ";

              static DisplayString tris = L"三角形数量: ";

              static DisplayString batches = L"批次: ";

7.      最后重新编译工程,下面是我运行的截图,是不是已经显示中文了,^_^


现在再来看看SimHei中的code_points是如何生成的,这个可以参考我上次写的这篇文章http://www.cnblogs.com/gogoplayer/archive/2008/05/09/1189795.html,至此,实现Overlay中文显示。

转载请注明出处:

作者:gogoplayer

E-mail : gogoplayer@163.com

QQ : 78939328

http://www.gogoplayer.com.cn
posted @ 2008-05-09 13:43 gogoplayer 阅读(1736) | 评论 (9)编辑

今天在做Ogre中文显示时,遇到了Ogre字体code_points生成问题,下面来看一下我使用的黑体定义。

SimHei

{

       type             truetype

       source         simhei.ttf

       size              16

       resolution  96

       code_points 33-166 24403-24403 21069-21069 24103-24103 36895-36895 29575-29575 24179-24179 22343-22343 26368-26368 39640-39640 20302-20302 19977-19977 35282-35282 24418-24418 25968-25968 37327-37327 25209-25209 27425-27425

}

字体code_points就是你要使用的Unicode字符编码,例如‘当’这个字的编码用16进制表示是5F53,转换成10进制就是24403,在Ogre字体定义中使用的是10进制,根据Ogre字体定义文档的描述,这个‘当’字需要在文件中描述成这样24403-24403,你可以到http://www.chi2ko.com/tool/CJK.htm查看每个字符对应的编码。

为了自动生成Ogre字体code_points,网上转了一圈没有找到合适的软件,那就自己动手吧,软件界面如下:


    软件中分隔线以上是单字符转换,对应上述网站的编码查找功能;分隔线以下是字符串转换,可以用他来生成Ogre字体需要的code_points格式。

    点击这里下载,注:软件需要.Net2.0 Framework。

 

转载请注明出处:

作者:gogoplayer

E-mail : gogoplayer@163.com

QQ : 78939328

http://www.gogoplayer.com.cn/

 



posted @ 2008-05-09 13:03 gogoplayer 阅读(1358) | 评论 (3)编辑

2008年4月24日

 

       QuickGUIOgre引擎下的一种用户界面,如果你使用的Ogre,而Ogre自带的Overlay无法满足你的需求时,你可以考虑QuickGUI,相对于CEGUI,他小巧,并且完全基于Ogre设计,这点比CEGUI要好,但是没有CEGUI支持那么多的控件,不过对于一般的用户,QuickGUI提供的控件还是够用的,下面是QuickGUI目前支持的控件『V0.97版本』。

Button 
CheckBox 
ComboBox 
Console 
Image 
Label 
List 
ListItem 
Menu 
MenuList 
NStateButton 
Panel 
ProgressBar 
RadioButton 
Sheet 
TextBox 
TrackBar (Horizontal
/Vertical) 
ScrollBar (Horizontal
/Vertical) 
Window (with TitleBar)

 

目前QuickGUI编辑器还没有开始做,主要是想使用C#来做UIC++只负责解析,这样的话会比较容易,虽然CUI那部分要重写读取和保存模块,比直接用C++多了一个步骤,但是C#开发UI的简便还是让我选择了这样做。

       了解了这些,就可以知道,现在一定先做C++的XML解析部分,现在让我们把目光集中在QuickGUI XML的文件格式上。

 1<?xml version="1.0" ?>
 2
 3<QuickGUILayout ActiveSheet="MainSheet">
 4
 5  <Widget Type="Sheet" Name="MainSheet" Skin="qgui">
 6
 7    <Widget Type="Window" Name="MainWindow">
 8
 9      <Property Dimension="10 10 320 180" />
10
11    </Widget>
12
13    <Widget Type="Window" Name="TestWindow">
14
15      <Property Dimension="10 220 320 180" />
16
17    </Widget>
18
19  </Widget>
20
21</QuickGUILayout>
22
23


       xml解析使用了tinyXML库,目前只支持SheetWindow,以后会逐渐增加对其他控件的支持,其它的控件格式是类似的,XML文件描述很简单,下面放上一张截图:

 

最后再简单说一下QuickGUI的事件绑定和注入:

QuickGUI支持事件绑定,用法和CEGUI类似:     

 

1     mCloseButton->addEventHandler(QuickGUI::EVENT_MOUSE_CLICK,&Window::hide,dynamic_cast<Window*>(mParentWidget)); 
2
3      mCloseButton->addEventHandler(QuickGUI::EVENT_MOUSE_BUTTON_UP,&Window::hide,dynamic_cast<Window*>(mParentWidget)); 
4

 

QuickGUI可以和OIS共用,支持注入:

 

 1
 2      bool injectChar(char c); 
 3
 4      bool injectKeyDown(const KeyCode& kc); 
 5
 6      bool injectKeyUp(const KeyCode& kc); 
 7
 8       
 9
10      bool injectMouseButtonDown(const MouseButtonID& button); 
11
12      bool injectMouseButtonUp(const MouseButtonID& button); 
13
14      bool injectMouseLeaves(void); 
15
16      bool injectMouseMove(const int& xPixelOffset, const int& yPixelOffset); 
17
18      bool injectMousePosition(const int& xPixelPosition, const int& yPixelPosition); 
19
20      bool injectMouseWheelChange(float delta); 
21
22      void injectTime(Ogre::Real time);  
23
24

 

QuickGUI官方论坛

QuickGUI下载地址

从文件中读取并解析代码

posted @ 2008-04-24 18:14 gogoplayer 阅读(2129) | 评论 (3)编辑

2008年4月21日

Shawn Hargreaves Blog的blog对我很有帮助,翻译高手的文章,诚惶诚恐,一定做到认真仔细,保持原汁原味,基于外语和水平的限制,难免会有纰漏,到时还望各位指正。

posted @ 2008-04-21 18:17 gogoplayer 阅读(235) | 评论 (0)编辑

2008年3月28日

 

Lsge ---2D引擎介绍

       Lsge全称为Light Shadow Game Engine,和Ogre有点像,因为这个2D引擎使用了很多Ogre的代码,因此得名。

引擎使用VS2005构建,支持Unicode,工程附带三个演示,可以帮助开发人员迅速了解,Lsge最大的特点是使用简单,内置了很多兼而易用的功能。

       Lsge是我大一、大二时的作品,设计的初衷是为了能快速写出小游戏,使开发者把注意力放在创造上,这个引擎的渲染效率不高,但是没有关系,引擎目标是小游戏,简单的演示,不是大规模游戏,在我设计期间,网络给了我所需要的大多数资料,让我有机会接触到Ogre,zlib,audiere,ois等巨人,有了他们的帮助,才有这个引擎的发展,引擎有完整的注释,文档比较完善。

       很早就想开放Lsge,回报网络,只有开放才能避免固步自封,我始终认为我所了解的只是沧海一粟。在Lsge完成的很长一段时间里,我差点忘了他的存在,前几天我想起了他,认为我改写点东西,整理出来,让他不只是在我的硬盘中,这对一些开发者可能有帮助,发表出来,将让他重获新生,最后,再次感谢Ogre,zlib,audiere,ois等巨人,感谢网络,没有你们就没有Lsge。

        附上一张引擎演示图(^_^):

 

附加说明:

选项配置:Lsge自带所需要的库,需要注意的是Lsge所需要的DX库需要特殊配置。


 

项目属性配置:

需要把调试->工作目录设为../Bin/$(ConfigurationName)



VS2005版本较多,主要分为sp1和非sp1版本,配置比较麻烦,经常会出现应用程序没有初始化之类的错误,这些可以通过安装正确地库一一排除。

Lsge下载:
blog不支持大文件,还要分割成五份,麻烦啊。
Lsge第一部分
Lsge第二部分
Lsge第三部分
Lsge第四部分
Lsge第五部分

posted @ 2008-03-28 14:42 gogoplayer 阅读(3589) | 评论 (7)编辑

导航

<2009年7月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

统计

与我联系

搜索

 

常用链接

留言簿

我的标签

随笔分类

随笔档案

最新评论

阅读排行榜

评论排行榜