Ogre HardwareBuffer

5 Hardware Buffers

頂點緩存,索引緩存以及像素緩存的大多數特性都是從HardwareBuffer繼承的,一個硬件緩存的一般前提是它是一個內存塊,在這裏你能做想要的任何事情,緩存本身沒有格式與它相關,二是與使用它的方法相關。也就是說,硬件緩存就像使用”malloc”分配的內存塊,只不過區別是它位於GPU或者AGP中。

相關知識:

三種內存:AGP內存,顯卡本地內存,系統內存。其中我們都知道顯卡本地內存就是顯存,系統內存就是咱那內存條,那這AGP內存是個啥玩意啊?其實是因爲在以前顯卡內存都很小,那時還是在顯存是16M32M爲主流的時候,如果你運行一個需要很多紋理的3D程序,那麼顯存一會就不夠用了,那該咋辦呢?只好問系統內存借點用用了!這就是AGP內存的由來,在我們電腦BIOS中有個設置AGP Aperture的選項,這裏就是設置顯卡可以使用系統內存的最大允許值,通常是設置爲64M。注意,這裏只是說最大允許值,並不是一開機他就把這64M給拿走了,你的256內存就變成192了!而是你的內存依然還是256M,只是限制顯卡最多可以使用64M的系統內存。

再說說這三個內存的速度的不同吧!

系統內存當然是人家CPU讀和寫操作最快啦!而顯卡讀寫系統內存就會相對於使用自己的顯存慢上很多很多!AGP內存是顯卡讀和寫的速度一般,當然肯定沒有顯卡使用顯存速度快啦!CPU就相對複雜點了,CPU讀取AGP內存速度很慢,但是寫的速度卻並不會慢,而是速度一般,比使用系統內存慢那麼一點,也就是說適合CPU去寫但不適合讀。有人就要問了,同樣是系統內存只不過名字不一樣,咋速度的就有差別了呢?這個嘛,我也不太清楚,老外沒有說的太詳細,大家只要記住就行了!
最後說的就是顯存了,這個很簡單,當然是顯卡讀和寫的速度最快,而CPU讀和寫的速度肯定要慢好多的!
說了三個內存的區別,現在說說他們都有什麼用處吧!這裏涉及一個D3DUSAGE枚舉量,D3DUSAGE_DYNAMIC,這個變量是在你創建資源時使用到的,它指示D3D將資源指定爲動態的,而動態的意思就是需要經常修改,
修改通常是CPU進行修改,所以動態資源應該放在AGP內存中。這樣對速度的影響可以減至最小。

 

5.1 hardware buffer manager

HardwareBufferManager類是幾何系統裏面的所有對象的工廠接口,你一般都是通過這個類來創建和銷燬用來定義幾何體的對象。它是一個單件,所以可以使用HardwareBufferManager::getSingleton()來訪問它--必須注意的是它只在RenderSystem被初始化之後才能保證存在(在調用Root::Initialise之後),這是因爲這些對象始終都是通過具體的API來創建,儘管可以通過一個通用的接口來訪問。

例如:

VertexDeclaration* decl = HardwareBufferManager::getSingleton().createVertexDeclaration();

HardwareVertexBufferSharedPtr vbuf =

         HardwareBufferManager::getSingleton().createVertexBuffer(

                   3*sizeof(Real), // size of one whole vertex

                   numVertices, // number of vertices

                   HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage

                   false); // no shadow buffer

 

5.2 Buffer Usage

由於硬件緩存中得內存在場景繪製過程中很可能面臨激烈爭奪,在它使用的過程中對它的訪問類型就變得極爲重要;是否需要經常更新緩存,使用要能夠從緩存中回讀信息,這些都是圖形卡如何操作緩存的重要因素。用於創建緩存使用的方法和具體參數取決於是頂點緩存還是索引緩存,它們有一個通用的參數,”usage”

硬件緩存最常見的類型是不經常更新、從不被讀取。createVertexBuffer createIndexBufferusage參數可以是下面中的一個:

HBU_STATIC:這意味着你不需要經常更新這個緩存,但是你可以會偶爾想要會回讀它

HBU_STATIC_WRITE_ONLY:意味着不需要經常更新緩存,並且不需要回讀它。但是你可以從設定的影子緩存來讀取它。這是一個最優的用法設置

HBU_DYNAMIC:意味着需要經常更新緩存,同時你希望要從它那裏讀取數據。這是最少見的緩存設置

HBU_DYNAMIC_WRITE_ONLY:意味着你想要經常更新緩存,但是你從不去讀取它。但是,你可以從你設定的它的影子緩存裏讀取它的數據。如果你使用這個選項,同時每幀替換掉整個緩存的內容,這個時候應該使用HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE 替代,因爲在有些平臺它有更好的性能

HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE :這意味你想要在一個非常規律的基礎上,大多數情況下是每幀,去替換掉整個緩存的內容。通過選擇這個選項,你可以讓系統從在任何時候必須關心丟失存在(existing)內容中解放出來,你會在下一幀去替換它。在有些平臺上這個可以產生巨大的性能差異,因而你可以在任何想要規律更新一個緩存的時候使用這個選項。注意如果按這種方式創建緩存,在鎖定它的內存用於寫入的時候必須使用HBL_DISCARD標籤

     小心的選擇緩存的用法對於取得最優的性能非常的重要。如果你面臨一個需要經常更新頂點緩存的情形,考慮下你是否真的需要更新它的所有部分或者只是其中的一部分。如果是後面這種情況,考慮使用多於一個緩存,只用於需要更新的數據,使用HBU_DYNAMIC緩存。

     應該總是嘗試使用_WRITE_ONLY的形式,這意味着你不能直接從緩存讀取數據,這是個良好的習慣,因爲從硬件緩存讀取非常的慢。如果需要從它讀取數據,使用下節說的影子緩存。

 

5.3 Shadow Buffers

前面提到從硬件緩存讀取數據非常慢。但是如果你必須要讀取頂點緩存中的數據,你必須設置 createVertexBuffer createIndexBuffer的參數’shadowBuffer’爲真。這促使硬件緩存在系統內存裏面生成一個副本,你可以從這裏讀取數據,而不會比從正常內存中讀取有多大劣勢。這個操作是當你向硬件緩存中寫入詩句的時候,它會首先更新系統內存的拷貝,然後再更新硬件緩存,作爲一個單獨的拷貝操作,因而這個技術在寫入數據的時候會有額外的開銷。除非真的需要從它讀取數據,否則不要使用它。

 

5.4鎖定緩存

爲了讀取或者更新一個硬件緩存,你必須先“鎖定”它。這執行兩個功能它告訴圖形卡你想要訪問這個緩存(這會對它的繪製隊列產生影響),然後它返回一個能讓你操作的指針。注意如果你已經請求讀取這個緩存(並且記住,如果這個緩存創建的時候沒有設定影子緩存,你不應該請求訪問),硬件緩存的內容已經被拷貝到系統內存中了,以便於讓你訪問。基於同樣的原因,當你操作完成之後,你必須爲它解鎖;如果你鎖定緩存用於寫入,這將觸發將修改信息上傳到圖形硬件的過程

 

鎖定參數

鎖定緩存的時候,使用下面中的方法:

// Lock the entire buffer

pBuffer->lock(lockType);

// Lock only part of the buffer

pBuffer->lock(start, length, lockType);

     第一個調用鎖定整個緩存,第一個只鎖定從”start”開始的”length”字節的區塊。這個可以比鎖定整個內存塊更快因爲它只要遷移更少的數據,但是如果你後面又要更新緩存的其餘數據的話就不一定了,因爲像這樣在小塊間操作意味着你不能使用HBL_DISCARD

     鎖定類型參數可以對程序的性能產生巨大的作用,特別是你不使用影子緩存的情況下。

     HBL_NORMAL:這種類型的鎖定允許讀取和寫入緩存,這是最不好的一個操作因爲基本上你在告訴顯卡你能夠做任何操作。如果你使用影子緩存,它要求緩存數據從顯卡來回傳輸,如果使用影子緩存的話這個影響是最小的

     HBL_READ_ONLY:意味着你只想要讀取緩存的內容。最好是在緩存設定了影子緩存的時候使用,因爲這個時候數據不需要從顯卡中下載

     HBL_DISCARD:這意味着你很樂意丟棄顯卡上這個緩存裏的所有內容。言下之意是你不會從這個緩存讀取數據,也意味着如果這個緩存正在被繪製的話,顯卡可以避免拋錨,因爲它將給你一個完全不同的緩存數據。如果鎖定一個沒有使用影子緩存的緩存話,儘可能的使用這個參數。如果使用了影子參數的話,它的影響就比較小,儘管有影子緩存,它更傾向於一次性鎖定整個緩存,因爲它允許影子緩存使用HBL_DISCARD,當它上傳更新的內容到真正緩存的時候。

     HBL_NO_OVERWRITE:這個很有用,如果你只是要鎖定緩存的一部分的話,因爲那樣就不能使用HBL_DISCARD。它告訴顯卡你承諾不更改這幀中已經在繪製中的緩存的那部分。這個也是隻在沒有設定影子緩存的緩存上纔有用。

     一旦你鎖定了一個緩存,你可以使用返回的指針進行操作(只是不要嘗試在使用了HBL_DISCARD的情況下去讀取數據,或者在使用HBL_READ_ONLY的情況下去寫入數據)。內容的更改基於緩存的類型。

 

5.5實用緩存小建議

創建時的usage模式和在讀取/更新時的鎖定選項的相互作用對於性能是很重要的,這裏是一些小提示:

1.           力爭使用HBU_STATIC_WRITE_ONLY創建“完美”的緩存,不使用影子緩存,同時只使用HBL_DISCARD來鎖定對它進行填充。不再去動它

2.           如果你需要經常更新緩存,你必須使用折中的方式。在創建的時候使用HBU_DAYNAMIC_WRITE_ONLY(仍然不使用影子緩存),同時使用HBL_DISCARD來鎖定整個緩存,或者使用HBL_NO_OVERWRITE來鎖定部分緩存

3.           如果你真的需要從緩存讀取數據,在創建它的時候使用影子緩存。確保在爲讀取鎖定緩存的時候使用HBL_READ_ONLY,因爲它將避免與未鎖定的緩存相關的上傳操作。你也可以將這一點和前面的兩點結合起來使用

4.           如果你發現對於不同的頂點元素頂點緩存的使用模式是不同的話,就講頂點緩存分成多個。

5.6 Hardware Vertex Buffers

 

5.6.1 VertexData

VertexData類包含了所有頂點相關用於繪製幾何體的信息。新的繪製操作需要指向VertexData對象的指針,同時它也用於在MeshSubMesh來存儲頂點位置,法線,貼圖座標等數據。VertexData可以單獨使用(用於繪製非索引幾何體,這裏面頂點的數據流定義了三角形),或者是和索引數據相結合,這些索引數據使用頂點數據中的索引來定義三角形。

VertexData類有許多重要的成員

vertexStart:綁定的緩存中用於開始讀取頂點數據的位置,這可以讓你爲多個繪製對象使用一個緩存

vertexCount:在這個繪製組中的頂點的個數

vertexBufferBinding:一個指向VertexBufferBinding對象的指針,用來定義哪些緩存綁定到哪些數據源。同時,它通過VertexData爲你創建

 

5.6.2 Vertex Declarations

頂點聲明定義了用於繪製你想顯示在屏幕上的幾何體的頂點輸入。基本上這意味着對於每個頂點,你想要給予一定量的數據集到圖形管道中,用來影響在三角形繪製的時候如何顯示(繪製成個什麼樣子)。頂點聲明讓你從任意數量的緩存拉取數據項(這裏我們稱爲頂點元素,由VertexElement類表示),這些緩存對於這個特定的元素可以是共享的,也可以是專用的。你必須自己確定緩存的內容在按VertexDeclaration所指明的方式進行解釋的時候能有意義。

注:

由於可以將多種類型的數據放在同一個緩存中,要麼是交替存放(絕大多數都是這種情況),要麼就是按順序排放,這就需要確定一些信息來決定如何訪問這些數據,假如一個緩存中同時存放了頂點位置,紋理座標以及法線這三種數據,並且它們交替存放,這個時候就要對頂點聲明添加三個元素,分別對應三種類型的數據。

要添加一個元素到VertexDeclaration,調用addElement方法,這個方法的參數如下:

        Source:這告訴這個聲明所需添加的元素從哪個緩存拉取,注意這只是一個索引,從0到綁定爲頂點數據源的緩存個數減1。參見5.6.3節中的緩存綁定以瞭解一個真正的緩存如何與一個source index綁定起來。用這種方式(而不是使用緩存指針)能讓你非常方便地重新綁定一個頂點的source,而不用更改頂點聲明自身的格式

        Offset:元素在緩存中的字節偏移量(例如緩存中存放了頂點和紋理,則頂點元素的offset0,因爲頂點在起始處,紋理元素的offset則爲sizeof(3floats),因爲存放頂點數據需要3float字節)

        Type:定義頂點輸入的數據類型,包含它的長度,有這麼幾種類型

       enum VertexElementType

     {

       VET_FLOAT1 = 0,

       VET_FLOAT2 = 1,

       VET_FLOAT3 = 2,

       VET_FLOAT4 = 3,

       /// alias to more specific colour type - use the current rendersystem's colour packing

                  VET_COLOUR = 4,

                  VET_SHORT1 = 5,

                  VET_SHORT2 = 6,

                  VET_SHORT3 = 7,

                  VET_SHORT4 = 8,

       VET_UBYTE4 = 9,

       /// D3D style compact colour

       VET_COLOUR_ARGB = 10,

       /// GL style compact colour

       VET_COLOUR_ABGR = 11

    };

        Semantic:定義了元素的類型

enum VertexElementSemantic {

                  /// Position, 3 reals per vertex

                  VES_POSITION = 1,

                  /// Blending weights

                  VES_BLEND_WEIGHTS = 2,

       /// Blending indices

       VES_BLEND_INDICES = 3,

                  /// Normal, 3 reals per vertex

                  VES_NORMAL = 4,

                  /// Diffuse colours

                  VES_DIFFUSE = 5,

                  /// Specular colours

                  VES_SPECULAR = 6,

                  /// Texture coordinates

                  VES_TEXTURE_COORDINATES = 7,

       /// Binormal (Y axis if normal is Z)

       VES_BINORMAL = 8,

       /// Tangent (X axis if normal is Z)

       VES_TANGENT = 9

        };

        Index:這個參數只有在一個頂點聲明中支持多餘一個同類型的元素時才使用。例如,如果你支持多於一個的紋理座標集,可以設置第一個座標集的index0,第二個爲1

        可以爲重複調用addElement來添加頂點輸入結構中的多個元素。

 

重要考量

        理論上你對於頂點的格式有完全的控制權,但是實際上有一些限制。老版的DIRECTX硬件對於每個緩存中的元素限定了順序,特別是Direct 9以前的硬件有下面的限定:

頂點元素必須按下面的順序添加,同時共享緩存中的元素的順序必須如下:

1.      positions

2.      blending weights

3.      normals

4.      diffuse colours

5.      specular colours

6.      texture coordinates

0開始,按順序來,中間不能有間隙

5.6.4 Vertex Buffer Bindings

頂點緩存綁定指的是將一個頂點緩存與5.6.2中使用的source index聯繫起來。

 

創建頂點緩存

 

         HardwareVertexBufferSharedPtr vbuf =

         HardwareBufferManager::getSingleton().createVertexBuffer(

                   3*sizeof(Real), // size of one whole vertex

                   numVertices, // number of vertices

                   HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage

                   false); // no shadow buffer

注意到我們使用5.1中硬件緩存管理器來創建我們的頂點緩存,同時這個方法返回了一個叫做HardwareVertexBuffferSharedPtr的指針,而不是一個原始指針。這是因爲緩存會被引用計數,你能夠使用一個頂點緩存作爲多個幾何體的存儲源,因而一個標準的指針並不很好,因爲你不知道什麼時候不同的用戶使用完了它。這個HardwareVertexBuffferSharedPtr類通過存放它自己被使用次數來管理它自身的銷燬,當最後一個HardwareVertexBuffferSharedPtr最銷燬後,這個緩存會被自動被其自身銷燬。

參數:

vertexSize:指的是一個整體頂點的字節數(整體頂點是指一個頂點元素的整體,可能包括頂點,紋理,法線等)。

numVertices:頂點的個數

usage:告訴系統你想要如何使用這個緩存

useShadowBuffer:告訴系統時候使用影子緩存

 

綁定頂點緩存

        將一個創建的緩存綁定到一個源索引

vertexBufferBinding->setBinding(0, vbuf);

        這使得緩存vbuf與源索引0綁定起來,然後任何從源索引0拉取的頂點數據實際上都是從這個緩存獲取的

 

5.6.4更新頂點緩存

更新頂點緩存的複雜性完全取決於它的內容如何被存放。你可以鎖定一個緩存,但是你怎樣把數據寫入禁區非常取決於它存放了什麼。

現在家丁有一個緩存只包含了頂點位置,所以它每個頂點只有3個浮點的數據,這種情況,所需要的數據寫入操作是:

         Real* pReal = static_cast<Real*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));

然後再以三個Real塊的方式寫入位置,如果在這個緩存中還有其他浮點型的數據,就稍微有點複雜了,你需要寫入替代元素。但是如果你有不同類型的元素,或者你需要從元素自身派生出如何寫入頂點數據。那麼有一些基於VertexElement類有用的方法可以幫到你。

首先,鎖定這個緩存,但是將結果轉換爲unsigned char *類型而不是一個特定的類型,接着,對於每個存放在這個緩存中的元素(你可以同過調用VertexDeclaration::findElementsBySource查找得到),調用VertexElement::baseVertexPointerToElement,這可以把指針從緩存中頂點的起始位置偏移到指定元素的起始位置,然後允許你使用正確類型的指針去進行操作,下面是例子:

// Get base pointer

unsigned char* pVert = static_cast<unsigned char*>(vbuf->lock(HardwareBuffer::HBL_READ_ONLY));//這裏我表示懷疑

Real* pReal;

for (size_t v = 0; v < vertexCount; ++v)

{

         // Get elements

         VertexDeclaration::VertexElementList elems = decl->findElementsBySource(bufferIdx);

         VertexDeclaration::VertexElementList::iterator i, iend;

         for (i = elems.begin(); i != elems.end(); ++i)

         {

                   VertexElement& elem = *i;

                   if (elem.getSemantic() == VES_POSITION)

                   {

                              elem.baseVertexPointerToElement(pVert, &pReal);

                              // write position using pReal

 

                   }

                   

                   ...

                   

                   

         }

         pVert += vbuf->getVertexSize();

}

vbuf->unlock();

 

 

5.7 Hardware Index Buffers

索引緩存用於繪製幾何體,通過間接的引用緩存中的位置來構建三角形,而不是通過序列化的讀取頂點來構建三角形。索引緩存比頂點緩存更簡單,因爲他們只是一系列的索引,但是它們可以存放在硬件上,並且和頂點緩存一樣能再多個幾何體間共享,因而創建和鎖定的規則都是一樣的

 

5.7.1 IndexData

這個類包含了用一系列索引來繪製幾何體的信息,它的成員如下:

indexStart:用於這個幾何體片段的起始索引,在對於再多個幾何體片段間使用單個索引緩存是很有用的

indexCount:這個繪製對象的索引的個數

indexBuffer:用於存放索引的索引緩存

 

創建一個索引緩存

HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().

         createIndexBuffer(

                   HardwareIndexBuffer::IT_16BIT, // type of index

                   numIndexes, // number of indexes

                   HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage

                   false); // no shadow buffer   

返回的類型是一個共享指針可以進行引用計數

        indexType:有兩種類型的索引,16位和32位。它們兩個都表現得一樣,只是後者可以尋址更大的頂點緩存。如果你的緩存包含多於65536個頂點,你需要使用32位的索引。注意到你只能在需要的時候使用32位索引,因爲它們會比16位的產生更大的開銷,並且在一些老的系統上不支持

        numIndexes:緩存中索引的個數,和頂點緩存一起使用的話,你必須考慮到你是否能夠使用一個被多個幾何片段共享的索引緩存,因爲在不經常切換索引緩存的時候能有性能提升

        usage:用法

        useShadowBuffer:是否使用影子緩存

 

5.7.2更新索引緩存

更新索引緩存只有在鎖定了緩存用於寫入的時候才能進行。鎖定返回了一個void型指針,然後必須強制轉換成合適的類型,對於索引緩存這個必須要麼是一個unsigned short(16bit),要麼是一個unsigned long(32bit)。比如:

unsigned short* pIdx = static_cast<unsigned short*>(ibuf->lock(HardwareBuffer::HBL_DISCARD));

 

 

5.8 Hardware Pixel Buffers

硬件像素緩存是一種特殊的緩存用於在圖形卡中存儲圖形數據,一般用作紋理。像素緩存能夠代表一個1維的,2維或者3維的同學,一個紋理可以包含多個這樣的緩存。與頂點緩存和索引緩存不同,像素緩存並不直接創建。當創建一個紋理時,用於存放這個數據的像素緩存就被自動創建。

 

5.8.1紋理

一個紋理是一張圖片,可以應用到三維模型的表面。在OGRE中,紋理通過Texture資源類表示。

創建一個紋理

        紋理通過TextureManger來創建,在大多數情況中它們直接被Ogre的資源系統從圖像文件創建。如果你讀到這裏,你一定很想要自己手動創建一個紋理,那樣你就可以自己用圖像數據來填充它。這個通過TextureManger::createAManual來完成

ptex = TextureManager::getSingleton().createManual(

   "MyManualTexture", // Name of texture

   "General", // Name of resource group in which the texture should be created

   TEX_TYPE_2D, // Texture type

   256, // Width

   256, // Height

   1, // Depth (Must be 1 for two dimensional textures)

   0, // Number of mipmaps

   PF_A8R8G8B8, // Pixel format

   TU_DYNAMIC_WRITE_ONLY // usage

);

這個例子創建了一個名爲MyManualTexture的紋理,在資源組General中。它是一個方形的2維紋理,寬256,高256,它沒有mipmaps,內部格式爲PF_A8R8G8B8,使用方式爲TU_DYNAMIC_WRITE_ONLY

 

紋理用法

        除了5.2中描述的硬件緩存用法,紋理緩存還有一些特定的用法標籤:

        TU_AUTOMIPMAP:用於這個紋理的mipmaps將圖形硬件會被自動產生,沒有定義具體的算法,但是你可以假定它是一個2x2的盒型濾波器

        TU_RENDERTARGET:這個紋理將被作爲一個繪製目標,例如作爲一個到紋理的繪製目標,設置這個標籤將會忽略除了TU_AUTOMIPMAP外的所有其他紋理用法

        TU_DEFAULT:這實際上是一個用法標籤的組合,等價於TU_AUTOMIPMAP|TU_STATIC_WRITE_ONLY,資源系統使用這些標籤來從圖像加載紋理

 

獲取一個PixelBuffer

        一個紋理可以由多個像素緩存組成。從一個紋理對象獲取一個像素緩存使用Texture::getBuffer(face,mipmap)face對於non-cubemap貼圖必須爲0,對於cubemap紋理它指明使用的面,是在5.8.3接種描述的紋理類型的立方體面中的一個。Mipmap對於0級的mipmap水平爲0,第一級的mipmap水平爲1,以此類推。對於有自動mipmap生成(TU_AUTOMIPMAP)的紋理,只有level 0可以被訪問,其他的必須由繪製API來維護,一個簡單的使用例子如下:

         // Get the PixelBuffer for face 0, mipmap 0.

         HardwarePixelBufferSharedPtr ptr = tex->getBuffer(0,0);

 

5.8.2更新像素緩存

像素緩存可以用兩種方法去更新,一個是簡單、方便的方法,一個是更困難(但在一些情況中更快速)的方法。兩種方法都使用PixelBox對象來表示內存中的圖像數據。

blitFromMemory

將一副圖像更新到像素緩存的簡單方法是使用HardwarePixelBuffer::blitFromMemory,這採用一個PixelBox對象,它完成所有需要的像素格式的轉換盒伸縮,下面是例子:

// Manually loads an image and puts the contents in a manually created texture

Image img;

img.load("elephant.png", "General");

// Create RGB texture with 5 mipmaps

TexturePtr tex = TextureManager::getSingleton().createManual(

   "elephant",

   "General",

   TEX_TYPE_2D,

   img.getWidth(), img.getHeight(),

   5, PF_X8R8G8B8);

// Copy face 0 mipmap 0 of the image to face 0 mipmap 0 of the texture.

tex->getBuffer(0,0)->blitFromMemory(img.getPixelBox(0,0));

 

直接內存鎖定

        一個從像素緩存來回傳輸數據的更高級的方法是採用鎖定,通過鎖定一個像素緩存你可以直接訪問到它的內容,不論它在GPU裏面的內部格式是怎樣的。

/// Lock the buffer so we can write to it

buffer->lock(HardwareBuffer::HBL_DISCARD);

const PixelBox &pb = buffer->getCurrentLock();

 

/// Update the contents of pb here

/// Image data starts at pb.data and has format pb.format

/// Here we assume data.format is PF_X8R8G8B8 so we can address pixels as uint32.

uint32 *data = static_cast<uint32*>(pb.data);

size_t height = pb.getHeight();

size_t width = pb.getWidth();

size_t pitch = pb.rowPitch; // Skip between rows of image

for(size_t y=0; y<height; ++y)

{

   for(size_t x=0; x<width; ++x)

{

       // 0xRRGGBB -> fill the buffer with yellow pixels

       data[pitch*y + x] = 0x00FFFF00;

}

}

 

/// Unlock the buffer again (frees it for use by the GPU)

buffer->unlock();

 

5.8.3紋理類型

當前的硬件支持4中類型的紋理,其中的三種只在維數上有區別,第四中是特別的,不同類型如下:

TEX_TYPE_1D:一維紋理,和一維紋理座標一起使用

TEX_TYPE_2D:二維紋理,和二維紋理座標一起使用

TEX_TYPE_3D:三維體積紋理,和三維紋理座標一起使用

TEX_TYPE_CUBE_MAP:立方圖(六個二維紋理,每個立方體面一個),和三維紋理座標一起使用

立方圖紋理

+X(face 0)-X(face 1)+Y(face 2)-Y(face 3)+Z(face 4)-Z(face 5)

 

5.8.4像素格式

        一個像素的格式描述像素數據的存儲格式,它定義了像素在內存中的編碼方式,OGRE中定義了下面的像素格式類型

 

Native endian formats

這些是在內存中的本地尾端的整數,這意味着一個格式爲PF_A8R8G8B8的圖像可以看作32位整數的數組,在十六進制中定義爲0XAARRGGBB,字母的意思在下面被描述

 

Byte formats(PF_BYTE_*)

        這些格式每個通道有一個字節,內存中的通道的組織順序由格式的名稱來確定,例如,PF_BYTE_RGBA包含一個有4個字節的塊,一個字節存Red,一個存Green,一個存Blue,一個存Alpha

 

Short formats(PF_SHORT_*)

        這些格式每個通道爲一個無符號整形(16位),通道的順序也是通過名稱來確定

 

Float16 formats(PF_FLAOT16_*)

        這些格式每個通道爲一個16位的浮點數,通道的順序通過名稱來確定

Float32 formats(PF_FLOAT32_*)

        這些格式每個通道爲一個32位的浮點數,通道順序通過名稱來確定

 

Compressed formats(PF_DXT[1-5])

        S3TC壓縮紋理格式,可以在http://en.wikipedia.org/wiki/S3TC找到具體信息

 

顏色通道

        RGBA的值爲01.0

        L:亮度值,從01.0

        X:不使用這個通道

如果在一個格式中沒有定義RGB或者L,它們的默認值都爲0.對於Alpha通道就不一樣,如果沒有定義Alpha,它的默認值爲1

 

像素格式的完全列表

Byte formats

PF_BYTE_RGB, PF_BYTE_BGR, PF_BYTE_BGRA, PF_BYTE_RGBA, PF_BYTE_L, PF_BYTE_LA, PF_BYTE_A

Short formats

PF_SHORT_RGBA

Float16 formats

PF_FLOAT16_R, PF_FLOAT16_RGB, PF_FLOAT16_RGBA

Float32 formats

PF_FLOAT32_R, PF_FLOAT32_RGB, PF_FLOAT32_RGBA

8 bit native endian formats

PF_L8, PF_A8, PF_A4L4, PF_R3G3B2

16 bit native endian formats

PF_L16, PF_R5G6B5, PF_B5G6R5, PF_A4R4G4B4, PF_A1R5G5B5

24 bit native endian formats

PF_R8G8B8, PF_B8G8R8

32 bit native endian formats

PF_A8R8G8B8, PF_A8B8G8R8, PF_B8G8R8A8, PF_R8G8B8A8, PF_X8R8G8B8, PF_X8B8G8R8, PF_A2R10G10B10 PF_A2B10G10R10

Compressed formats

PF_DXT1, PF_DXT2, PF_DXT3, PF_DXT4, PF_DXT5

 

 

 

5.8.5 Pixel boxes

Ogre中所有使用或者返回原始圖像數據的方法都返回一個PixelBox對象。一個PixelBox是一個描述內存中體(3D),圖像(2D)或者線段(1D)的基單元。它描述了存放圖像數據的內存的位置和格式,但是它自身不做任何內存管理,pixel boxdata成員指向的內存中,像素被存爲一個連續的“深度”(沿Z方向)塊,每個包含“寬度”(X)個“高度”行(Y)的像素。不使用的維數的值必須置爲1,如一維圖像有下標(width,1,1),二維圖像有下標(width,height,1)。PixelBox有如下的成員

 

data:指向內存中圖像數據的一個部分

format:圖像數據的像素格式(在上一節中有描述)

 

rowPitch:一行最左邊的像素到下一行最左邊之間的元素的個數,對於壓縮格式這個值等於getWidth()返回的值

 

slicePitch:一個深度塊和下一個深度塊的左上角像素間的元素個數,必須是rowPitch的倍數,對於壓縮格式這個值必須等於getWidth()*getHeight()

 

 

它也有一些有用的方法:

getWidth():獲取這個box的寬度

getHeight():獲取這個box的高度,如果是一維圖像這個值爲1

getDepth():獲取這個box的深度,如果是一維和二維圖像,這個值爲1

setConsecutive():設置rowPitchslicePitch以讓數據在內存中連續存放

getRowSkip():獲取一行最右邊和下一行最左邊間的元素的格式,如果行是連續的話值爲0

getSliceSkip():獲取一個塊的右下角像素與下一個塊的左上角的像素間的元素,如果塊連續的話值爲0

isConsecutive():返回內存中的緩存是否是連續的(或者說pitches是否等於維數)

getConsecutiveSize():獲取如果在內存中連續存放的話這個圖像的字節數

 

 

 

 

 

 

 

發佈了48 篇原創文章 · 獲贊 25 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章