本文章是由機器翻譯。

DirectX 要素

用 Direct2D 特效突破 Z 屏障

Charles Petzold

下載代碼示例

作為孩子,我們瞭解如何畫之前我們學會如何讀和寫,和我們無疑收集到幾個經驗教訓。我們發現在畫布上繪畫的時空過程體現在的油漆的分層。我們早些時候油漆可能部分地掩蓋和被我們後來油漆遮蓋。

為此原因,甚至有人完全不熟悉電腦圖形學的力學能大概猜出如何在圖像圖 1 呈現:很顯然,背景是彩色的灰色第一 ; 下一步來的藍色三角形,其次是綠色,,最後由紅色,就是在其他一切。它是沒有驚喜呈現數位從後方到前方的過程被稱為"畫家演算法"。


圖 1 三個重疊的三角形

三個三角形的圖 1 也可能安排在堆疊中的有色建設紙片。如果你是要向堆疊中添加更多和更多的三角形,他們將會建立起成一堆,和什麼開始作為一個二維表面將收購第三維度。

即使在 2D 圖形,還有一個 Z 軸基本概念 — — 一個虛擬的空間正交到二維螢幕或畫布。平面的二維物件的分層被受"Z 順序"的人物。在基於 XAML 的環境中,例如,附加的屬性確定元素似乎坐在其他人,但它真的只是 Canvas.ZIndex 的順序控制元素呈現在螢幕上。

問題是這樣的:在 2D 圖形,Z 索引總是適用于整個圖。不能使用這種類型的 Z 順序來繪製三個人物像那些在圖 2,與在第二個,第二個在第三、 第一次,但第三個在第一次。


圖 2 相互重疊的三角形

一個三角形的一個角的改變似乎是輕微的但它代表了什麼一個不同的世界 !中的圖像圖 2 可能很容易與建築紙,但不是那麼容易與繪畫 — — 無論是在現實生活中,或在 2D 圖形程式設計。這些三角形中之一需要分為兩部分,仔細計算的座標,或使用裁剪的基礎的一個其他三角形呈現。

GPU 和影響

呈現的圖 2 可以極大地説明通過借款的三維圖形世界中的一些概念。

這些數位不能有統一的 Z 指數 ; 相反,這些數位必須允許有 Z 座標的變數在它們的整個表面。繪圖的過程然後可以保持 Z 座標 (稱為 Z 緩衝區或深度緩衝區) 的集合,包含了每個圖元的渲染表面。如呈現每個圖,圖中每個圖元的 Z 座標相比是相應的 Z 座標在此深度緩衝區中。如果該圖元是在深度緩衝區中的 Z 座標,圖元繪製並存儲在深度緩衝區中的新的 Z 座標。如果不是,圖元將被忽略。

這聽起來計算代價高昂 — — 不僅為本身的比較而計算的 Z 座標為每個圖元的每個圖形的圖 — — 這是準確的評價。這就是為什麼它是一個理想的工作移交給現代 GPU 的平行計算能力。

計算的 Z 座標為每個圖元的圖從概念上講是相當容易的如果這個數位恰好是一個三角形 — — 並牢記每個多邊形可以分解為三角形。一切必要是三維座標點,給每個三角形的三個頂點和三角形內的任何點然後可以作為的三個頂點座標的加權平均計算。(這涉及到重心座標 — — 不唯一在電腦圖形學中使用的概念是由德國數學家 8 月斐迪南莫比烏斯.)

同一的插值過程中可以遮陽的三角形。如果每個頂點被分配一個特定的顏色,那麼該三角形內的任何圖元是加權的平均數的這三種顏色,如中所示圖 3


圖 3 ThreeTriangles 程式顯示

色彩坡形的類型也是一個重要特徵的三維程式設計,因為它允許三角形網底類似于彎曲的表面。但它不是在傳統的 2D 程式設計中常見的漸變類型。

中的圖像圖 3 由一個叫做 ThreeTriangles 運行在 Windows 8.1 和 Windows Phone 8.1 下的可下載程式創建。(解決方案被創建在Visual Studio2013年更新 2 使用的新的通用應用程式範本,允許共用很多的 Windows 8.1 和 Windows Phone 8.1 之間的代碼)。

ThreeTriangles 程式中的圖形都做完全在 Direct2D,使用的 Direct2D 稱為效果或 (當你的代碼他們自己) 的一個功能自訂效果。使用自訂的效果你可以得到更接近真實的 3D 程式設計比否則為可能與 Direct2D。

為 Direct2D 編寫自訂效果時, 你獲得一種特權通常只限于 3D 程式師:你可以寫在 GPU 執行的代碼。此代碼的形式被稱為著色器,你寫使用高級著色語言 (HLSL),類似于 C.的小程式這些著色器是由Visual Studio編譯到已編譯的著色器物件 (.cso) 檔正常專案生成期間,然後在 GPU 上運行,當程式執行時。

事實上,Direct2D 效果有時被稱為小更比包裝為著色器 !自訂效果是你可以使用著色器的 Direct2D 程式設計範圍內實現類似三維的圖像的唯一方式。

三種不同類型的著色器是可供 Direct2D 影響使用:

  • 在頂點執行操作的頂點著色器。每個三角形都有三個頂點。頂點總是涉及到一個座標點,但可能包括其他資訊,如顏色。
  • 在這些三角形內的所有圖元執行操作的圖元著色器。任何提供頂點的資訊自動插在表面的圖元著色器的準備中的三角形。
  • 計算著色器使用 GPU 來執行繁重的並行處理。我不會討論這篇文章中的,計算著色器。

用於 Direct2D 效果的著色器有稍有不同的要求比與 Direct3D 程式設計,相關聯的著色器,但許多概念是相同的。

內置的效果和自訂效果

Direct2D 包括約 40 預定義的內置效果,其中在點陣圖上執行各種影像處理操作,如模糊或銳化或各種類型的彩色處理。

每個這些內置效果是由您使用來創建該類型的效果類 ID 標識。例如,假設您想要使用的顏色矩陣影響,允許指定一個可改變點陣圖中的顏色轉換。您可能會在你渲染類作為一個私有欄位聲明一個 ID2D1Effect 類型的物件:

Microsoft::WRL::ComPtr<ID2D1Effect> m_colorMatrixEffect;

在 CreateDeviceDependentResources 方法中,您可以通過引用的記錄的類 ID 創建這種效果:

d2dContext->CreateEffect(
  CLSID_D2D1ColorMatrix, &m_colorMatrixEffect);

在這一點上,您可致電 SetInput 要設置點陣圖,點陣圖和 SetValue 來指定一個變換矩陣的作用物件。 你通過調用渲染此顏色轉移的點陣圖:

d2dContext->DrawImage(m_colorMatrixEffect.Get());

所有的內置效果涉及點陣圖輸入和 Direct2D 效應的特徵之一是您可以連結在一起,對點陣圖應用一系列的影響。

如果你興趣是在編寫您自己的自訂效果,還有寶貴的 Windows 8.1Visual Studio解決方案稱為 Direct2D 自訂的圖像效果的示例,其中包含三個單獨的專案,展示著色器可用於 Direct2D 效應的三種類型。 三個專案都需要點陣圖作為輸入。

因此,你會被原諒的假設 Direct2D 效果總是執行操作點陣圖的輸入。 但這並不是這樣。 ThreeTriangles 程式,創建的圖像在圖 3 不需要點陣圖的輸入。

你也會原諒為假設 Direct2D 影響涉及只是一種類型的著色器。 當然,內置的效果似乎涉及到頂點著色器和圖元著色器,但不是能同時。 然而,ThreeTriangles 程式是不同的在這方面,以及:它定義了一個自訂的效果,使用頂點著色器和圖元著色器。

註冊、 創建、 繪製

因為 Direct2D 影響的設計是為了預先註冊和創建的類 ID,自訂效果需要提供那相同的能力。 ThreeTriangles 程式中的自訂效果是命名為 SimpleTriangleEffect,定義為註冊了類的靜態方法的類。 由 ThreeTrianglesRenderer 類的建構函式調用此方法,但效果可以在程式中的任意位置註冊:

SimpleTriangleEffect::RegisterEffectAsync(d2dFactory)

這種註冊方法是非同步因為它需要載入在已編譯的著色器檔中,並為此目的在 DirectXHelper 類中提供的唯一方法是 ReadDataAsync。

就像當使用內置的效果,ThreeTrianglesRenderer 類作為一個私有欄位,在其標頭檔中聲明瞭一個 ID2D1Effect 物件:

Microsoft::WRL::ComPtr<ID2D1Effect> m_simpleTriangleEffect;

CreateDeviceDependentResources 方法創建自訂效果內置效果相同的方式:

d2dContext->CreateEffect(
   CLSID_SimpleTriangleEffect, &m_simpleTriangleEffect)

早些時候註冊的自訂效果與效果相關聯的類 ID。

SimpleTriangleEffect 有沒有輸入。 (那是什麼讓"簡單"的部分 !只是像一個內置效果呈現效果:

d2dContext->DrawImage(m_simpleTriangleEffect.Get());

也許簡單使用此自訂效果表明一些效果類本身中的複雜性。 自訂的效果,如 SimpleTriangleEffect 必須實現 ID2D1EffectImpl (效果執行) 介面。 效果可以由多個傳遞,被稱為變換,組成,每一個通常由 ID2D1DrawTransform 的一個實現。 如果一個類用於這兩個介面 — — 這是與 SimpleTriangleEffect 的情況 — — 然後它需要實現 IUnknown (三種方法),ID2D1EffectImpl (三種方法),ID2D1TransformNode (一種方法),ID2D1Transform (三種方法),和 ID2D1DrawTransform (1 法)。

這是一個相當大的開銷數額,除了一些 XML,標識效果和它的作者當效果是第一次註冊。 幸運的是,對於簡單的效果 — — 和這一個當然有資格 — — 的許多效果方法可以有相當容易實現。 影響類的最重要的工作涉及到載入和註冊已編譯的著色器代碼 (和著色器關聯的 Guid,供以後參考),並定義頂點緩衝區,也必須是一個 GUID 與相關聯。

從頂點緩衝區......

頂點緩衝區是加工組裝的頂點的集合。 每個頂點總是包括一個 2D 或 3D 的座標點,但通常以及其他專案。 資料相關聯的每個頂點和它如何組織被稱為"佈局"頂點緩衝區的總體而言,ThreeTriangles 程式定義了三種不同 — — 但相等 — — 資料類型來描述這個頂點的佈局。

此頂點資料的第一次表示形式所示圖 4。 這是一個簡單的結構命名為頂點,其中包括 3D 協調­nate 點和 RGB 顏色。 這些結構的陣列定義對程式所顯示的三個三角形。 (此陣列是硬編碼在所需的初始化方法的簡單­TriangleEffect 類 ; 在一個真正的程式效果類將允許陣列的頂點將被輸入到影響。)

圖 4 頂點定義在 SimpleTriangleEffect

// Define Vertex for simple initialization
struct Vertex
{
  float x;
  float y;
  float z;
  float r;
  float g;
  float b;
};
// Each triangle has three points and three colors
static Vertex vertices [] =
{
  // Triangle 1
  {    0, -1000, 0.0f, 1, 0, 0 },
  {  985,  -174, 0.5f, 0, 1, 0 },
  {  342,   940, 1.0f, 0, 0, 1 },
  // Triangle 2
  {  866,   500, 0.0f, 1, 0, 0 },
  { -342,   940, 0.5f, 0, 1, 0 },
  { -985,  -174, 1.0f, 0, 0, 1 },
  // Triangle 3
  { -866,   500, 0.0f, 1, 0, 0 },
  { -643,  -766, 0.5f, 0, 1, 0 },
  {  643,  -766, 1.0f, 0, 0, 1 }
};
// Define layout for the effect
static const D2D1_INPUT_ELEMENT_DESC vertexLayout [] =
{
  { "MESH_POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0 },
  { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12 },
};

X 和 y 值基於正弦和余弦的角度增量為 40 度,一個半徑為 1000。 然而,請注意,那的 z 座標是 0 和 1 之間,所有集紅色頂點的 z 值為 1 的綠色的頂點都是 0.5,的藍色的頂點都是 0。 更多關於這晚一點。

之後該陣列是另一個小小的陣列,但這一定義的頂點資訊在更正規的方式所需的創建和註冊的頂點緩衝區。

頂點緩衝區和頂點著色器是在 SimpleTriangleEffect 的 SetDrawInfo 方法中引用的。 每一次呈現時的效果,這些九個頂點被傳遞到頂點著色器。

... 到頂點著色器......

圖 5 顯示的頂點著色器 SimpleTriangleEffect。 它由三個結構和調用主函陣列成。 主要功能被呼籲每個頂點的頂點緩衝區 ; 在這種情況下,這就是只有九個頂點,但往往有很多。

圖 5 SimpleTriangleEffectVertexShader.hlsl 檔

// Per-vertex data input to the vertex shader
struct VertexShaderInput
{
  float3 position : MESH_POSITION;
  float3 color : COLOR0;
};
// Per-vertex data output from the vertex shader
struct VertexShaderOutput
{
  float4 clipSpaceOutput : SV_POSITION;
  float4 sceneSpaceOutput : SCENE_POSITION;
  float3 color : COLOR0;
};
// Information provided for Direct2D vertex shaders
cbuffer ClipSpaceTransforms : register(b0)
{
  float2x1 sceneToOutputX;
  float2x1 sceneToOutputY;
}
// Called for each vertex
VertexShaderOutput main(VertexShaderInput input)
{
  // Output structure
  VertexShaderOutput output;
  // Append a 'w' value of 1 to the 3D input position
  output.sceneSpaceOutput = float4(input.position.xyz, 1);
  // Standard calculations
  output.clipSpaceOutput.x =
    output.sceneSpaceOutput.x * sceneToOutputX[0] +
    output.sceneSpaceOutput.w * sceneToOutputX[1];
  output.clipSpaceOutput.y =
    output.sceneSpaceOutput.y * sceneToOutputY[0] +
    output.sceneSpaceOutput.w * sceneToOutputY[1];
  output.clipSpaceOutput.z = output.sceneSpaceOutput.z;
  output.clipSpaceOutput.w = output.sceneSpaceOutput.w;
  // Transfer the color
  output.color = input.color;
  return output;
}

每個三個結構包含欄位標識與 HLSL 資料類型、 成員名稱和標識的特定欄位的作用的大寫形式語義學。

名為 VertexShaderInput 的結構是主要的輸入,這是你們剛才看到,頂點緩衝區,但 HLSL 資料類型的三維位置及 RGB 顏色的佈局相同。

名為 VertexShaderOutput 的結構定義主要的輸出。 前兩個欄位是必需的 Direct2D 影響。 (第三個必需欄位會存在,如果影響涉及輸入的點陣圖)。我叫領域 sceneSpaceOutput 基於輸入的座標。 一些影響變化的協調 ; 這種效果並沒有,只需進入 4 D w 值為 1 的齊次座標的 3D 輸入的座標:

output.sceneSpaceOutput = float4(input.position.xyz, 1);

頂點著色器輸出還包括一個稱為顏色,只需設置從輸入的顏色的非必要欄位:

output.color = input.color;

我叫必需的輸出欄位 clipSpaceOutput 描述了每個頂點座標歸一化座標在 3D 中使用。 這些座標是從所述的此列的上個月的分期付款的相機投影變換生成的座標相同。 在這些 clipSpaceOutput 座標 x 值的範圍從 – 1 在螢幕的左邊到右邊 ; 1 y 值的範圍從 – 1 的底部到頂部 ; 1 和 z 值的範圍從檢視器中最接近的座標為 0 到 1 的座標遠的那個地方。 顧名思義的欄位的名稱,這些歸一化的座標都用於剪切到螢幕上的三維場景。

協助您在這些 clipSpaceOutput 座標,計算第三個結構是自動提供給你我叫 ClipSpaceTransforms。 這些都是基於圖元的寬度和高度的螢幕上,四個數字和任何設備上下文轉換的有效 DrawImage 呈現效果時。

然而,提供的轉換僅是為 x 和 y 座標,以及那有為什麼我 z 座標定義在原始的頂點緩衝區,要有 0 和 1 之間的值。 另一種方法是使用實際的攝像機投影變換在頂點著色器中 (如在未來的專欄中,我將演示)。

在深度緩衝區中還會自動使用這些這些 z 值,因此,具有較低的 z 座標的圖元模糊具有較高的 z 值的圖元。 但是這只發生如果效果類中的 SetDrawInfo 方法調用 SetVertexProcessing 與 D2D1_VERTEX_OPTIONS_USE_DEPTH_BUFFER 標誌。 (這發生,也會導致 COM 錯誤出現在輸出視窗的Visual Studio雖然該程式正在運行,但也同樣發生在微軟的示例 Direct2D 效果代碼)。

…到圖元著色器

每次呈現效果 (和在一般情況下,即在視頻顯示的畫面播放速率),頂點著色器的調用每個頂點的頂點緩衝區,在這種情況下九倍。

頂點著色器的輸出具有相同的圖元著色器的輸入格式。 正如你可以看到在圖元著色器中圖 6,PixelShaderInput 結構是在頂點著色器的 VertexShaderOutput 結構相同。

圖 6 SimpleTriangleEffectPixelShader.hlsl 檔

// Per-pixel data input to the pixel shader
struct PixelShaderInput
{
  float4 clipSpaceOutput : SV_POSITION;
  float4 sceneSpaceOutput : SCENE_POSITION;
  float3 color : COLOR0;
};
// Called for each pixel
float4 main(PixelShaderInput input) : SV_TARGET
{
  // Simply return color with opacity of 1
  return float4(input.color, 1);
}

然而,圖元著色器呼籲在三角形中的每個圖元和所有欄位的結構有一直都插在該三角形的表面。 圖元著色器中的主函數必須返回四元件顏色,其中包括不透明度,所以插值的 RGB 顏色簡單地修改通過追加不透明度的一個領域。 那種顏色是輸出到顯示器。

這裡是一些有趣的變化為圖元著色器:Z 座標的 sceneSpaceOutput 欄位範圍從 0 到 1,所以它是可能通過使用這個座標構建一個灰色的陰影,並返回它的主要方法從視覺化的每個三角形深度:

float z = input.sceneSpaceOutput.z;
return float4(z, z, z, 1);

增強功能嗎?

SimpleTriangleEffect 一些偷工減料。 而應是更通用的方法包括頂點輸入設置。 其他一些功能不傷身:頂點著色器是一個很大的地方來執行矩陣變換 — — 如旋轉或觀景窗變換 — — 因為矩陣乘法在 GPU 上執行。

很少有程式師都能夠抗拒誘惑,執行代碼增強功能,尤其是那些靜態的圖像變成一個生氣勃勃。

CharlesPetzold 是長期貢獻 MSDN 雜誌和"程式設計視窗,第 6 版"的作者 (微軟出版社,2013年),一本關於編寫應用程式的 Windows 8 書。 他的網站是 charlespetzold.com

感謝以下的微軟技術專家對本文的審閱:Doug埃裡克森
Doug埃裡克森是鉛程式設計作家為微軟的 OSG 開發人員文檔團隊。 當不編寫和開發 DirectX 圖形代碼和內容,他讀像Charles文章 ',因為這是他喜歡怎樣度過他的閒置時間。 好吧,那,和騎摩托車。