2013-07-01 47 views
3

我目前正在試驗在DirectX 10中顯示2D精靈的各種方法。我開始使用ID3DX10Sprite接口在一次調用中批量繪製我的精靈。然而,最終,我想要更多地控制我的精靈如何渲染,所以我決定研究基於四核的精靈渲染(即每個精靈都由一個應用了紋理的四核顯示)。在DirectX 10中渲染精靈的最有效方法是什麼?

我開始簡單:我創建了一個由四個頂點組成的單個頂點緩衝區,這些頂點在繪製子畫之前應用了一次。然後我繞過我的精靈,設置適當的屬性傳遞給着色器,併爲每個精靈進行繪製調用,如下所示:d3dDevice->Draw(4, 0);。儘管它很有效,但每個sprite的繪製要求都會讓我感到困擾,所以我尋找了一種更有效的方法。

經過搜索,我瞭解了對象實例化,並決定嘗試一下。一切都很順利,直到我嘗試實現精靈最重要的部分 - 紋理。總之,儘管我的紋理數組(在我的着色器的頂部聲明,如Texture2D textures[10];)可以在我的像素着色器中使用文字/常量作爲索引成功採樣,但我無法弄清楚如何控制應用於哪些紋理哪些實例通過紋理索引。

這個想法對我來說是每個實例都要傳遞一個紋理索引,然後可以用它來在像素着色器中對陣列中的相應紋理進行採樣。然而,在搜索了更多內容之後,我找不到一個如何完成的例子(並且發現很多事情表明,如果不移動到DirectX 11就無法完成)。

這是說,通過DirectX 10中的對象實例成功渲染精靈的唯一方法是基於紋理批量渲染它們嗎?因此,例如,如果我的場景由20個不同紋理的100個精靈組成(每個紋理由5個精靈引用),則需要20個獨立的繪圖調用來顯示場景,並且我一次只能發送5個精靈。

最後,我相當茫然。我做了很多搜索,似乎在提供相互矛盾的信息。例如,在段落this第6條它指出:

使用DirectX 10的,有可能在陣列相同的對象的不同實例中應用不同的紋理,從而使它們看起來不同

此外,this白皮書第3頁,它提到的選項:

從紋理陣列讀取每個實例的自定義紋理

但是,我似乎無法找到一個具體的例子,說明如何使用每個實例紋理索引設置着色器來訪問紋理數組。

最後,中心問題是:什麼是使用DirectX 10渲染精靈的最有效方法?

如果答案是實例化,那麼是否有可能控制哪個紋理應用於着色器中的每個特定實例 - 從而可以發送更大批量的精靈以及相應的紋理索引,單打電話?或者我必須滿足於一次只能以相同的紋理實例化精靈嗎?

如果答案是返回到使用提供的DX10 Sprite接口,那麼是否有辦法讓我更好地控制它的渲染方式?

作爲一個方面說明,我也研究過使用幾何着色器來創建實際的四邊形,所以我只需要傳入一系列點而不是管理頂點和實例緩衝區。不過,除非有辦法控制哪些紋理應用於生成的四邊形,否則我只能回到通過紋理進行精靈的批處理。

回答

3

有幾種方法(像往常一樣)去做你所描述的。

請注意,使用

Texture2D textures[10]; 

不會允許你使用一個變量指數中的Pixel Shader查找(因爲技術上這個聲明將按每紋理插槽)。

所以你需要的是創建一個Texture2DArray。這有點像體積紋理,但z分量是一個完整的數字,並且沒有采樣。

雖然你需要生成這個紋理數組。簡單的方法是在啓動時進行一次全屏四畫圖調用,將每個紋理繪製到數組的一個切片中(您可以爲特定切片創建一個RenderTargetView)。 Shader在這裏將是一個簡單的passtrough。

要創建一個紋理陣列(代碼在SlimDX但是,選項是相似的):

var texBufferDesc = new Texture2DDescription 
    { 
      ArraySize = TextureCount, 
      BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource, 
      CpuAccessFlags = CpuAccessFlags.None, 
      Format = format, 
      Height = h, 
      Width = w, 
      OptionFlags = ResourceOptionFlags.None, 
      SampleDescription = new SampleDescription(1,0), 
      Usage = ResourceUsage.Default, 
}; 

然後着色器資源的看法是這樣的:

ShaderResourceViewDescription srvd = new ShaderResourceViewDescription() 
    { 
     ArraySize = TextureCount, 
     FirstArraySlice = 0, 
     Dimension = ShaderResourceViewDimension.Texture2DArray, 
     Format = format, 
     MipLevels = 1, 
     MostDetailedMip = 0 
    }; 

最後,得到渲染目標對於特定切片:

RenderTargetViewDescription rtd = new RenderTargetViewDescription() 
{ 
     ArraySize = 1, 
     FirstArraySlice = SliceIndex, 
     Dimension = RenderTargetViewDimension.Texture2DArray, 
     Format = this.Format 
}; 

將它綁定到您的passtrough着色器,將所需紋理設置爲輸入並作爲輸出切片並繪製全屏四邊形(或全屏三角形)。

請注意,此紋理也可以保存在dds格式(所以它可以節省您每次啓動程序時重新生成)。

仰望你的紋理就像是:

Texture2DArray myarray; 

在像素着色器:

myarray.Sample(mySampler, float2(uv,SliceIndex); 

現在關於渲染精靈,你也有GS擴展的選項。

所以,你創建一個頂點緩衝區,只包含位置/大小/紋理索引/無論你需要什麼,每個精靈一個頂點。

發送帶有n個精靈的繪圖調用(需要將拓撲設置爲點列表)。

傳遞從頂點着色器到幾何着色器的數據。

將你的觀點擴展到幾何着色器中的四邊形,你可以在Microsoft SDK中找到一個ParticlesGS的例子,這對你的情況有點矯枉過正,因爲你只需要渲染部分而不是動畫。如果你需要一些乾淨的代碼,讓我知道我會快速做一個dx10兼容的樣本(在我的情況下,我使用StructuredBuffers而不是VertexBuffer)

做一個預先製作的Quad,並在每個實例VertexBuffer中傳遞上述數據可能的,但是如果你有大量的精靈,它會很容易炸掉你的顯卡(高到我的意思是說有超過300萬個粒子,這在目前的標準中並不多,但如果你的精靈數量低於50萬,會很好;)

+0

非常好,謝謝!關於GS方法,根據您的經驗,與每個實例VB方法相比,是否存在邊際性能差異?我已經讀過某些卡在某些情況下對GS有麻煩。你有沒有經歷過這個?根據您的經驗,性能增益是否值得? –

+0

從我的測試中我發現有些牌不能很好地處理GS。在你的情況下,你有一個非常小的GS擴展幾個頂點。技術也非常平行(在那裏沒有分支,只有很少的操作和推動2個三角形)。所以在你的情況下,你應該沒問題,我遇到過問題,但在移動卡上有更復雜的場景,但簡單的四通道總是很好。排除快速(甚至更快)的事實,我還發現它更加靈活(特別是當您開始使用dx11和計算着色器/結構化緩衝區時)。 – catflier

0

包含實例緩衝器內的紋理索引並使用它來選擇從每個實例紋理陣列正確質地:

struct VS 
{ 
    float3 Position: POSITION; 
    float2 TexCoord: TEXCOORD0; 
    float TexIndex: TexIndex; // From the instance buffer not the vertex buffer 
} 

然後在通過將該值傳遞給所述像素着色器

struct PS 
{ 
    float4 Positon: SV_POSITION; 
    float3 TexCoord: TEXCOORD0; 
} 

.. 

vout.TexCoord = float3(vin.TexCoord, vin.TexIndex); 
相關問題