【轉】遊戲開發高度圖有關資料與Balder中的相關支持

資料一:使用Managed DirectX建立三維地形

來源:GameRes網站

內容:

使用Height Map做爲輸入
  首先,什麼是高度圖(Height Map)呢?所謂高度圖實際上就是一個2維數組。建立地形爲何須要高度圖呢?咱們這樣考慮,地形實際上就是一系列高度不一樣的網格而已,這樣數組中每一個元素的索引值恰好能夠用來定位不用的網格(x,y),而所儲存的值就是網格的高度(z)。正是因爲這個簡單的映射關係,最多見的地形生成方法都使用高度圖做爲輸入數據。同時,爲了減少數組的尺寸,一般使用Byte類型來保存高度值,所以,地形中最低點將用0表示,而最高點使用255表示(固然,這樣作可能會出現一些問題,好比,地形中大部分區域的高度差異都不大,可是有少數地方高度差特別大時,不過大多數狀況下這個系統都能運行的很好)。使用2D Byte數組的另外一個好處就是咱們高度圖恰好能夠用一張灰度位圖(grayscale bitmap) 來表示。對於位圖中的每一個像素來講,一樣使用0~~255之間的值來表示一個灰度。這樣,咱們又能把不一樣的灰度映射爲高度,而且用像素索引表示不一樣網格。

  那麼如何來建立高度圖呢?有兩種方法:直接使用程序建立2D數組或者使用其餘繪圖軟件建立灰度位圖。先來看看兩種方法的優缺點。直接建立數組,經過特定算法填充每一個元素的值(只爲每一個元素賦隨即值是不可行,這樣會致使你的地面看起來極度不真實,不連續的高度值可能建立出很扭曲的地形。),你不須要任何額外的工具就能建立地形。可是,經過這種方法建立的地形基本是隨機的,雖然能夠經過調節算法的參數控制大概的地形形狀,卻不能精確控制每一個點應該凹下仍是凸起。而使用灰度圖,你沒必要掌握複雜的地形生成算法,能夠把3維軟件建好的地形模型渲染爲灰度圖,也可使用經過衛星採樣的圖片做爲灰度圖。咱們的示例程序將使用後一種方法,不過首先,咱們仍是來看看徹底使用程序生成地形的算法。


使用Midpoint Displacement方法生成高度圖
  這裏咱們介紹一種比較經常使用,也比較簡單的地形生成算法,稱爲Midpoint Displacement中點偏移算法。使用這個方法,咱們先建立一張平坦的高度圖,而後再來升高或下降不一樣的網格建立隨機地形。爲了不生成的值是徹底沒有規則的,咱們先把整個平面分爲4個正方形區域,接下來重複對這四個正方形進行一樣的分割,同時,調整每一個正方形頂點的高度。隨着細份層次的增長,相應減小頂點高度調整的幅度。


  使用[0,255]之間的浮點值來進行調整,以保證最後能用8位的灰度值來表示全部高度。每一步,都在一個肯定範圍內產生一個隨機值來做爲頂點偏移值。對於第一步來講,隨機值將在[-128,128]之間(爲了方便說明,咱們把這個隨機值範圍記爲[-delta,delta]產生,而且賦給上圖左邊的A,B,C,D四個頂點。接下來,用虛線把它分爲4個小區域,這將建立5個新的頂點。計算每一個新頂點所在邊兩個頂點高度的平均值做爲這個點的基準值(好比 把點A和B的高度平均值做爲點1的基準值),其中,點5的基準值是由四個頂點A,B,C,D的平均值來決定的。再計算[-delta,delta]之間的一個隨機值,對基準值進行偏移,做爲這個點的最終值。5個點的值都計算完畢以後,我就調到下一階段,使用一樣的方法,計算個頂點值,如上圖右邊所示。

  爲了引導地形的產生,再把delta和一個縮放因子相乘。咱們把這個因子稱爲roughness,它是一個1~0之間的值,這樣,每一個階段都會減少delta的值。

delta = delta * roughness

roughness的值越大,地形起伏就越明顯,而越小,相應的地形也就越平坦。


使用Perlin Noise生成高度圖
  任何沒有討論噪聲函數的程序地形算法都是不完整的。最重要的噪聲函數就是Perlin Noise。他幾乎是現代圖形軟件包生成各類火焰,雲彩,奇形怪狀的岩石,以及樹木和大理石表面等許多應用的基礎。這裏不對Perlin Noise的理論作詳細介紹,咱們主要看看如何使用它爲咱們的地形添加噪聲。

  Perlin噪聲能夠適用於任何維度的空間,但這裏咱們只討論二維的狀況。本質上,2D Perlin噪音就是對每一個網格頂點法線的一種插值,來仔細看看這個技術吧。


  首先,使用網格把整個圖片劃分爲幾個不一樣部分。如上圖所示,咱們使用了一個4X4的網格來劃分整個圖片。這裏,網格的多少控制着噪聲的複雜性。網格越多,噪聲越密集(tiger),相似於電視沒有信號時顯示出的雪花點;而網格越少,噪聲的波形就越明顯,相似於雲朵的效果。

  對於每一個網格頂點咱們都分配一個隨機法線(normal vector)。這些法線實際上就是一些指向不一樣方向的單位矢量而已。這裏,常見的方法是建立一張有256個指向不一樣方向(造成一個圓周)的向量查找表。而後爲每一個網格隨機分配一個向量,如上圖所示。

  對於圖片中的每一個像素來講,咱們先找到包含它的網格單元。而後,再建立4個從網格頂點指向所要計算的像素的方向矢量,以下圖所示。如今,每一個網格頂點有2個向量:一個隨機的單位向量以及一個指向像素的方向向量。計算每對向量的點積,把它做爲每一個網格頂點的梯度高度值(scalar height value)。接下來,混合這4個值決定所計算像素的高度。這裏,不一樣的混合方法能夠產生不一樣效果,最多見的就方法就是經過目標像素與每一個頂點位置的權重來計算。


咱們將執行3次混合操做。首先須要計算混合權重。使用以下公式:

W = 6t^5 – 15t^4 + 10t^3 (^符號表示冪運算)

其中w表示權重,t根據須要替換爲x或y值。這個方法與最先Perlin提出的公式(w = 3t^2 – 2t^3)有些區別。它雖然計算起來比較慢,但效果要好得多。
首先,計算x方向上的權重,使用公式:

V = Ca(w) + Cb(1-w)

  混合網格上邊的兩個頂點。其中Ca和Cb分別爲上面兩個頂點的梯度高度值,w是上一個公式計算出的權重值。而後,使用一樣的方法混合下面兩個頂點。最後,使用前兩部混合的結果,以及y方向上的權重再進行一次混合。最後爲這個像素計算出的高度值位於[0,1]之間,咱們再把它縮放爲相應的灰度值。

  舉個例子,假如網格上邊兩個頂點的座標分別爲Ca[2,0]和Cb[8,0],梯度高度值分別爲h0和h1,所求像素位置爲[4,2],那麼兩個頂點指向這個像素的矢量就是:

Vector2 d0(4 -2,2-0)
Vector2 d1(4-8,2-0);

X軸方向的權重就爲:

Sx = 6*d0.x^5 – 15d0.x^4 + 10d0.x^3

相應的插值就爲:

avgX0 = h0*Sx + h1(1 –Sx)

若是下面兩個頂點的插值爲avgX1,則最後的插值就是:

Result = avgX0 * Sy + avg2(1- Sy)

一般狀況下,爲了得到真實的地形,會選取不一樣網格粒度,分別對圖像進行屢次Perlin噪音處理,最後把這些處理過的圖加到一塊兒,得到最終結果。


生成地形
  如今來看看如何把高度圖轉變爲爲多邊形網格。一開始就說過,把高度圖中像素的x,y值轉換爲頂點的x,y值,把像素的顏色值轉換爲頂點高度。咱們能夠把這些值縮放爲所須要的尺寸。

  每2X2個像素就對應着2X2個頂點,同時能夠組成2個三角形。能夠把把頂點數據儲存爲一個簡單的(x,y,z)列表,三角形數據儲存爲三個索引值一組的頂點列表。這兩個列表以後就轉變爲頂點緩衝和索引緩衝。

public class Terrain
{
    private Device device;
    private VertexBuffer vb;
    private IndexBuffer ib;
    private int numVertices, numIndices, numTriangles;
    //保存從高度圖中提取的數據
    float[,] heights;
    //地形大小
    private float terrainSize;

    public unsafe Terrain(Device d,float Min, float Max,float terrainSize)
    {
        device = d;
        //加載高度圖
        Bitmap heightMap = new Bitmap(@"..\..\heightmap.bmp");
        //根據位圖大小建立數組
        heights = new float[heightMap.Width,heightMap.Height];
        //鎖定數據
        BitmapData data = heightMap.LockBits(new Rectangle(0,0,heightMap.Width,heightMap.Height, ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb);
        //得到位圖中第一個像素的地址
        byte* p = (byte*) data.Scan0;
        //遍歷位圖,得到最高和最低點的灰度值
        byte lowest = 255;
        byte hightest = 0;
        for(int i=0;i<heightMap.Width;i++)
        {
            for(int j=0;j<heightMap.Height;j++)
            {
                if ( *p < lowest)
                    lowest = *p;
                if( *p > hightest)
                    hightest = *p;
                //因爲每一個像素是24位,而指針是8位,因此+3指向下一個像素
                p += 3;
            }
        }
        //填充數組,max表示地形最高點的位置,min標誌最低點。
        p = (byte*) data.Scan0;
        for(int i=0;i< heightMap.Width;i++)
        {
            for(int j=0; j< heightMap.Height; j++)
            {
                heights[i,j] = (float)(*p - lowest) / (float)(hightest - lowest) * (Max - Min) + Min;
                p += 3;
            }
        }
        heightMap.UnlockBits(data);
        //計算頂點,索引,三角形數量
        numVertices = heightMap.Width * heightMap.Height;
        numIndices = 6 * (heightMap.Width - 1) * (heightMap.Height - 1);
        numTriangles = 2 * (heightMap.Width - 1) * (heightMap.Height - 1);
        //建立頂點數組
        Vector3[] verts = new Vector3[numVertices];
        int[] index = new int[numIndices];
        int x = 0;
        int n = 0;
        float dx = terrainSize / (float) heightMap.Height;
        float dy = terrainSize / (float) heightMap.Width;
        //填充頂點數組
        for ( int i = 0; i < heightMap.Height; i ++)
        {
            for ( int j = 0; j < heightMap.Width; j ++)
            {
                verts[i*heightMap.Width+j] = new Vector3((float)j*dx -terrainSize/2f,heights[j,i],(float)i*dy -terrainSize/2f);
            }
        }
        //填充索引數組
        for ( int i = 0; i < heightMap.Width-1; i ++)
        {
            for ( int j = 0; j < heightMap.Height-1; j ++)
            {
                x = i * heightMap.Width + j;
                index[n++] = x;
                index[n++] = x+1;
                index[n++] = x+heightMap.Width+1;
                index[n++] = x;
                index[n++] = x+heightMap.Width;
                index[n++] = x+heightMap.Width+1;
            }
        }
        //設置頂點以及索引緩衝
        vb = new VertexBuffer(typeof(Vector3),numVertices,device,Usage.None,VertexFormats.Position,Pool.Default);
        vb.SetData(verts,0,0);
        ib = new IndexBuffer(typeof(int),numIndices,device,Usage.None,Pool.Default);
        ib.SetData(index,0,0);
    }

    public void DrawTerrain()
    {
        device.VertexFormat = VertexFormats.Position;
        device.SetStreamSource(0,vb,0);
        device.Indices = ib;
        device.Transform.World = Matrix.Translation(0,0,0);
        device.DrawIndexedPrimitives(PrimitiveType.TriangleList,0,0,numVertices,0,numTriangles);
    }
}

  好了,看看咱們的工做成果吧,還不錯把。源碼中咱們使用了一張位圖做爲高度圖。

  固然,這只是初級的地形技術而已,咱們沒有爲地形貼紋理,頂點沒有法線信息,以致於不能使用燈光照亮他,另外,也沒有進行任何LOD處理。下一次,咱們將仔細討論這些問題。


下載配套例程


資料二:如何由Height Map生成Normal Map
來源:cnblogs(http://www.cnblogs.com/cxrs/archive/2009/11/01/1594155.html)

內容:
javascript

Nvidia和ATI都有相應的工具把Heightmap轉成NormalMap,有了NormalMap,咱們就能夠用NormalMapping技術進行Per Pixel Lighting計算了。那麼HeightMap是怎麼轉化成NormalMap的呢?
      其實並不難, 在《3D 遊戲與計算機圖形學方法》中,提供了一種由高度圖生成法向圖的方法。其思想是根據高度圖中的象素與其周圍象素的高度差,在切空間構造S 向量和T 向量,由SXT 獲得法線向量。
H(i,j) 表示在height map (i,j) 象素點的高度值,則在切線空間S T 方向的切向量能夠表示成:
S(i,j) = (1,0,H(i+1,j) - H(i-1,j) )
T(i,j) = (0,1,H(i,j+1) - H(i,j-1) )
Normal(i,j) = S(i,j) X T(i,j)
H(i+1,j) – H(i-1,j) 爲沿S 方向的高度差,也就是S 方向的坡度,H(i,j+1) - H(i,j-1) 爲沿T 方向的高度差,也就是T 方向的坡度。當相鄰象素高度差爲0 時,則算出的Normal(i,j) = (0,0,1) ,表示法線垂直於平面,當有高度差時,法線就會分別朝S 方向或T 方向偏移。
 
   shader 來實現也很簡單,VS PS 代碼以下,上邊左圖爲HeightMap,右圖爲由下面shader生成的NormalMap,這個方法生成的NormalMap並不夠好,在 RenderMonkey中有一個叫NormalmapFilter的Sample,會生成更高質理的NormalMap,有興趣的朋友能夠參考。
VS_OUTPUT main(float4 Pos: POSITION){
   VS_OUTPUT Out;
   // Clean up inaccuracies
   Pos.xy = sign(Pos.xy);
   Out.Pos = float4(Pos.xy, 0, 1);
   // Image-space
   Out.texCoord.x = 0.5 * (1 + Pos.x);
   Out.texCoord.y = 0.5 * (1 - Pos.y);
   return Out;
}
 
float4 main(float2 texCoord: TEXCOORD) : COLOR {
   float2 ff = 1.0 / HeightMapSize;
   float Scale = 1;
   // Sample teh neighbor
   float s0 = tex2D(Heightmap, texCoord + float2(-off.x,0)).r;
   float s1 = tex2D(Heightmap, texCoord + float2( off.x,0)).r;
   float s2 = tex2D(Heightmap, texCoord + float2( 0,-off.y)).r;
   float s3 = tex2D(Heightmap, texCoord + float2(0,off.y)).r;
   float3 U = float3(1,0,s1 - s0);
   float3 V = float3(0,1,s3 - s2);
   float3 normal = normalize(Scale * cross(U,V));
   // Pack [-1, 1] into [0, 1]
   return float4(normal * 0.5 + 0.5,1);
}
資料三:Ogre 天龍八部地形 Heightmap(高度圖)+GridInfo(地表信息)初步結果

剛研究出的天龍八部的地形高度和GridInfo,正確的載入了高度圖和地表信息,能夠看出場景的大體樣子了:)
第一張圖是我本身載入的 明教的光明殿地形,第二張是天龍八部遊戲中的場景,能夠看出差距啊:)

php

通過分析天龍八部場景的實現方式,我用了跟天龍八部實現方式徹底相同的作法,載入了HeightMap和GridInfo文件,而且能夠解析多個版本的GridInfo結構。
如下是使用Ogre渲染出來的天龍八部的地形:
這是明教光明殿

這張是天龍寺

這張是無量山

場景裏沒有放入mesh,因此不大容易看出來,不過載入mesh就簡單了,我將盡快放出新版本截圖!html



TerrainLiquid是用來作湖水,海水,熔岩之類效果的。最多有兩層,一層放貼圖動畫,另外一層做alpah值。
天龍八部TerrainLiquid的實現實在有點那個-3-





對Model的支持只是最簡單的載入mesh,尚未加入動畫:)

WCollision是天龍八部遊戲中用來實現「碰撞」的。下圖粉紅色區域即爲WCollision信息


橋的下面是熔漿,不容許行走的,可是能夠從橋上經過,而天龍裏不是根據橋這個mesh,來實時檢測玩家所應該處的高度,而是經過WCollision裏所記錄的信息來判斷的。
這一點很容易驗證,咱們把.scene文件裏全部的mesh刪掉,玩家依然站在了正確的高度,以下圖所示

若是把WCollision文件刪掉,玩家就會站到熔漿裏了:)
由於地表法線算的有問題,一直沒察覺,因此燈光這部分繞了點彎路,呵呵!如今場景可漂亮多了:)

這是明教光明殿,燈光比較多

由於尚未加入lightmap因此跟天龍八部遊戲自己的效果,仍是有點差異:)
java

lightmap(光照圖)實際上是很簡單的,加一層UV就能夠了,不過因爲圖片太大,不提升貼圖的採樣率就會變的很模糊,可是提升採樣率就會大大下降效率-3-,天龍八部的設置bin\System.cfg裏View_LightmapQuality估計就是作這個用的。可是陰影嘛,模糊就模糊沒什麼關係:)

仍是光明殿

大理

婚禮場景


.Frame文件是配合.Model文件一塊兒使用,來實現場景節點動畫的(NodeAnimationTrack)。好比鳥兒的飛行軌跡,魚羣的活動路線,隨風搖曳的燈籠等等。

     是否分析天龍八部的Frame文件格式,我醞釀了兩三天,由於推測該文件是3DSMAX或Maya的導出插件導出的(後面會給出推測的理由),其格式應該是公開的,可是Google了數次未果,只好手動分析了。

     Frame文件開頭部分有明顯的[Serializer_v1.10]標記,顯然是使用Ogre的Serializer類保存的,且存在不少重複的模型,好比天龍寺的鈴鐺,徹底可使用1個mesh就能夠了,可是資源裏共有4個mesh,分別命名爲鈴鐺1,鈴鐺2,鈴鐺3,鈴鐺4,大理派主殿就更多了,有十幾個鈴鐺。-3- 其實全部的鈴鐺,樣子都同樣,只是相對於中心點(0,0,0點)的偏移不一樣。這就證實了上面的推測,若是不是直接從MAX或Maya導出的Frame動畫,而是本身作一個編輯器來製做動畫。那麼使用1個放在0,0,0點的鈴鐺就夠了。並且Frame文件中保存的幀的信息不能夠直接使用,仍是要通過計算 -3-,計算的方法徹底是爲了將就多個不一樣偏移的mesh。

     這個插件估計跟OFusion相似,應該提供兩種版本的Frame文件,一種是xml文本結構的,一種是Serializer二進制結構的。(純屬猜想)

下面是截圖(動畫用截圖很差表現啊- -)
天龍寺的鈴鐺

飛翔的小鳥

遨遊的魚羣
ios

TerrainLiquid加入法線能夠說是最簡單的了,全部法線一概平行y軸,連算都不用算。如今水面看起來跟遊戲裏一個德行了-3-

算法

天龍八部的粒子特效作得很是漂亮,漂亮的背後必定有着複雜的實現:),如下是我總結的天龍對Ogre粒子系統的改動內容:

添加發射器1個
PolarEmitter

添加影響器6個
ColourFading
MeshAnimationAffector
MeshRotator
Movement
Revolution
ScaleInterpolator

添加的Renderer 2個
mesh
texcoord_billboard

由於我要實現場景中所使用的粒子效果,對於其它的,好比技能裏所使用的粒子則暫時不予考慮。
因此對273個場景文件所使用粒子作了統計以下:

粒子名,使用個數
a_y_dali_05, 1
baofu, 18
caihong, 16
cangying, 11
chuansongdian_01, 1
chuansongdian_03, 11
chuansongdian_04, 10
chuansongdian_05, 1
chuansongmen_03, 14
cj_ba01_big, 10
cj_ba03, 2
cj_denglong_020, 13
cj_ghuochong, 4
cj_huo, 37
cj_kvk01, 1
cj_kvk02, 1
cj_kvk_03, 1
cj_kvkfeng01, 7
cj_xing, 11
foguang, 7
guihun_01, 5
huahuo, 35
huangsha, 110
huoba01, 2624
huoba01_big, 555
huoba02, 615
huoba03, 7
huoba04, 5
huohua_011, 9
huoxing, 1886
huoxing7mi, 6
jian01, 18
jiujiaozhengqi, 93
jiujiaozhengqi_01, 48
kuangdui01, 37
kuangdui02, 354
kuangdui03, 7
kuangdui05, 1
kuangdui_hong, 249
kuangdui_hong01, 259
kuangdui_lan, 270
kuangdui_lan01, 365
kuangdui_lv, 154
kuangdui_lv01, 68
langhua, 86
langhua01, 119
langhua02, 76
penhuo, 120
penhuo_01, 41
penjianyanjiang, 6
pubushuihua_02, 1420
pubushuihua_03, 582
rain, 23
rain_xiao, 6
shandong_01, 18
shedengguangzhu, 93
shedengguangzhu_01, 64
shedengguangzhu_02, 56
shedengguangzhu_03, 17
shedengguangzhu_04, 5
shedengguangzhu_05, 1
shidenglong_01, 167
shidenglong_010, 124
shidenglong_02, 86
shidenglong_020, 19
shuidi, 44
shuidi_01, 158
shuidi_lv_01, 1
shuimianlianyi, 156
snow, 12
sunny01, 23
sunny02, 17
sunny03, 6
sunny04, 45
taohua, 528
taohua_big, 83
wenquan, 95
wenquanzhengqi, 73
xiangluyan, 77
xiangluyan_01, 51
yan01, 96
yan01_da, 4
yanjiangshuihua_001, 46
yanjiangwuqi, 211
yanjiangzhengqi, 34
yezi, 136
yezi01, 186
yezi01_, 2
yezi01__big, 6
yezi01_big, 179
yezi01_i02, 2
yezi01_ihua_02, 20
yezi01_ngdian_04, 12
yezi_big, 24
yezihuyang01, 7
yinghuochong, 1689
yun, 717
yun001, 65
yun002, 103
yun02, 6
yun_01, 17
yun_02, 1
ziti01, 40

而後是對這102種粒子詳細信息的統計:

發射器類型,使用個數
Box, 64
Point, 3
PolarEmitter, 4
Ring, 21

影響器類型,使用個數
ColourFading, 83
MeshAnimationAffector, 1
Movement, 39
Revolution, 11
Rotator, 71
ScaleInterpolator, 69

Render類型,使用個數
billboard, 87
mesh, 1
texcoord_billboard, 4

並不存在的粒子
cj_ba01_big
cj_ba03
cj_denglong_020
cj_ghuochong
cj_huo
cj_xing
yezi01_
yezi01__big
yezi01_i02
yezi01_ihua_02
yezi01_ngdian_04

發現場景中使用的這些粒子幾乎將天龍添加的那部分所有使用了-3-,原本還想偷懶一下呢,看來所有得實現啊!其中huoba01使用的最多,我就以這個爲例子,作出來的效果和遊戲中的差很少,以下圖:
遊戲中的效果

查看器中的效果

由於個人號級別比較低,不少特效沒有在遊戲裏看到過,作起來仍是比較困難的,不過好在參數名都能比較清晰的反映它們的做用,因此猜一猜用途,大體仍是能實現的,只不過可能不少細節跟遊戲中不盡相同,須要再作調整-3-


btw:餿狐在09年3月3號左右已經將all.material,all.particle等文件加密了,真囧~~~express

從TLSceneViewer中導出,而後用TLSceneImporter導入就能夠了,哈哈,效果以下:







話說3DS MAX SDK真不討人喜歡vim

天龍八部的地表作法與9年前的紅警2的極爲相似。都是由不少個格子(Title)組成,每一個格子四個點,兩個三角形。
下面我先給出天龍八部GridInfo文件格式,而後再簡要介紹一下實現方式。
[GridInfo file format]
DWORD     nVersion     版本號
int     nWidth          地表寬度(橫向格子數)
int     nHeight         地表高度(縱向格子數)

若是版本號大於0x00100002則後面跟一個bool型數據,不然不存在這個bool型數據
bool     bLarge         GridInfo是否爲7字節類型

若是bLarge存在而且值爲1,則其後跟的是7字節版本不然爲5字節版本
[GridInfo 5字節版本]
BYTE     nFirstLayer     該值即爲pixelmap的索引(第幾個pixelmap)
BYTE     nFirstLayerOp     對nFirstLayer的操做,可能取值以下:
     0 不變
     1 水平翻轉
     2 垂直翻轉
     4 向左旋轉90度
     8 對角線鏡像
     注意:這些值之間是能夠取和的,好比9=1+8說明圖片須要水平翻轉和對角線鏡像

BYTE     nSecondLayer     該值爲pixelmap的索引
     天龍八部的地表最多能夠兩層融合,說白了就是每一個點裏有兩層UV,這裏爲第二層pixelmap的索引
BYTE     nSecondLayerOp     對nSecondLayer的操做,取值同nFirstLayerOp

BYTE     IndexOrder     對格子的三角形的操做,可能取值以下
     0正常三角形索引
     1不一樣於正常的三角形索引
     以下圖,該值主要用在水池啊一類的地方,若是三角形索引不變的話,水池四個角中的兩個角就不對了。



本文出自www.MobileGameBase.com
[GridInfo 7字節版本]
short     nFirstLayer
     讀取後需交換高8位與低8位的值,需作以下操做
     nFirstLayer = (nFirstLayer<<8)|(nFirstLayer>>8)
BYTE     nFirstLayerOp
short     nSecondLayer
     同nFirstLayer,需交換高8位與低8位的值
BYTE     nSecondLayerOp
BYTE     IndexOrder

實現方式:
想象一下若是你設好每一個點的位置,UV,法線,材質,整個場景不就出來了嘛。
如今的問題是如何操做這些VertexData和IndexData,其實Ogre自己就有大量的類直接這二者,好比Mesh,StaticGeometry,ManualObject等等。不過前陣子忽然發現有位朋友寫了個魔獸3地形的例子,使用Ogre實現的,寫得很是好,呵呵,我就借花獻佛推薦你們看這個例子吧,相信你應該有點感受:)

btw:聽說《成吉思汗OL》是用Ogre作的,並且仍是在《天龍八部OL》的基礎上改的,場景方面作的更好。由於成吉思汗的製做人就是原來天龍八部的製做人,呵呵,國內遊戲圈這麼小,消息渠道我多少有一點。有興趣的朋友研究下吧:)數組

Ogre場景編輯器基本功能已經完成:)
1.相似於3DSMAX的攝像機,很是方便美術操做
2.動態刷新素材資源
3.支持全部操做任意步撤消
4.能夠導入並編輯天龍八部的地表(經過TLSceneViewer導出)
5.能夠輕鬆繪製特定圖案,繪製時自動融合

6.能夠對地表應用特定格式(Word的格式畫刷)
7.多種方式調整地表高度,製做斜坡,懸崖等很是方便

8.支持動態光源,以及光源的特定變化
9.能夠很方便的擺放物體,經過快捷鍵和鼠標既能夠直觀的調整物體位置、旋轉、縮放,也能夠在場景組件界面裏作特定調整

10.能夠自動很快速的生成很是優化的導航網格(Navigation Mesh, Navmesh)

生成的導航網格與天龍八部相似,已經很是優化了。
天龍八部很早就已經放棄Region的方式尋路,改用了.path文件,也就是導航網格,同時繼續沿用WCollision,可是WCollision僅只是用來調整人物高度的,並無參與尋路。

僅作了一點修改:導入後自動顯示貼圖。3DSMAX SDK比我想象中的麻煩,因此耽擱了兩天:)
OgreImporter使用實例將《天龍八部Online》的模型導入到3DS MAX中就能夠自由編輯了。

另外介紹一下如何查看MAX裏的材質,以下圖:)


注:OgreImporter不是僅能夠導入天龍八部的模型,任何Ogre的mesh文件均可以導入。
龍八部場景導入插件(TLSceneImporter)能夠將天龍八部的場景導入到3DSMAX中。導入後場景就能夠自由編輯了:)

使用方法:
1.解壓到3DSMAX9的安裝目錄。
2.用天龍八部場景查看器將須要的場景文件導出成.pxa文件。
3.運行3DSMAX,選擇「File->Import...」,文件類型選擇「TLBB Scene Pack (*PAX)」,再選擇以前導出的.pxa文件,點擊「打開」。
4.設置界面中「Output Folder」是指貼圖的輸出目錄,肯定後等待進度條完成,場景即導入成功。




app


正文:Balder 3D引擎中的HeightMap支持


下面這張圖來自於引擎在線示例快照:



下圖來自於我添加材質貼圖後生成的一個快照:



相關代碼以下:

XAML:

<UserControl x:Class="Balder_tut_Programatic.HeightMapTest"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Execution="clr-namespace:Balder.Execution;assembly=Balder"
    xmlns:Geometries="clr-namespace:Balder.Objects.Geometries;assembly=Balder"
    xmlns:View="clr-namespace:Balder.View;assembly=Balder"
    xmlns:Materials="clr-namespace:Balder.Materials;assembly=Balder"
    xmlns:Lighting="clr-namespace:Balder.Lighting;assembly=Balder" >
    <Grid x:Name="LayoutRoot">
 

        <Execution:Game Width="640" Height="480">
            <Execution:Game.Camera>
                <View:Camera Position="0,50,-40" Target="0,0,0"/>
            </Execution:Game.Camera>
            <Lighting:OmniLight Position="-100,100,0" Ambient="White" Strength="0.7"/>
            <Geometries:Heightmap x:Name="HeightMap"
                                  Dimension="128,64"
                                  LengthSegments="16"
                                  HeightSegments="16"
                                  HeightInput="Heightmap_HeightInput"
                                  InteractionEnabled="True">
                <Geometries:Heightmap.Material>
                    <Materials:Material Ambient="White" ReflectionMap="/Balder_tut_Programatic;component/Assets/cloudless.png" Specular="White" Shade="Gouraud" />
                </Geometries:Heightmap.Material> 
               
            </Geometries:Heightmap>
        </Execution:Game>
    </Grid>
</UserControl>

後臺代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using Balder.Objects.Geometries;
using Balder.Rendering;//DetailLevel

namespace Balder_tut_Programatic
{
    public partial class HeightMapTest : UserControl
    {
      
        private double _sin;
        private double _movement;

        public HeightMapTest()
        {
            InitializeComponent();



        }

        private void Heightmap_HeightInput(object sender, HeightmapEventArgs e)
        {
            var height = System.Math.Sin(_sin + _movement) * 2;
            //var height=1.2;
            e.Height = (float)height;
            var highlight = (byte)((height * 16f) + 32f);

            e.Color = Color.FromArgb(0xff, highlight, highlight, highlight);

            _sin += 0.03;

            if (e.GridX == HeightMap.LengthSegments &&
                e.GridY == HeightMap.HeightSegments)
            {
                _sin = 0;
                _movement += 0.05;
            }
        }

    }
}


文章轉自:http://space.itpub.net/14466241/viewspace-671625

相關文章
相關標籤/搜索