DirectX 要素

畫布及相機

Charles Petzold

下載代碼示例

Charles Petzold大約 15 年前,英國演出者DavidHockney 開始發展有關原來是頗有爭議的文藝復興藝術的理論。 列奧納多 · 范 · 艾克和 Velázquez,等老主人是如何能夠呈現在畫布上的視覺世界的角度和網底的驚人準確性與 Hockney 希奇。 他成為了相信這個精度是僅可能的與由透鏡和反射鏡,暗箱和攝影機亮葉等製成的光學工具的説明。 這些設備拼合 3D 場景,並使其更接近這位演出者的再生產。 肯尼發表他的論文在華麗和令人信服的書,題為,"秘密知識:重新發現老主人的丟失的技術"(Viking,2001年)。

最近,發明人Tim詹尼森 (NewTek 的創始人,是開發視頻烤箱和光波 3D 的公司),開始癡迷于使用光學工具重新創建 Jan Vermeer 350 歲繪畫"音樂的教訓"。他建造一個類似于原始的房間、 裝飾與傢俱和道具再生產、 招聘現場模型 (包括他的女兒),和他自己發明的一個簡單的光學工具用於繪製現場。下巴-­滴結果記載中的令人著迷的紀錄片,Tim維梅爾"。

為什麼是必要使用光學設備 — — 或在這些日子裡,一個簡單的攝像頭 — — 準確地捕捉 3D 場景 2D 表面上的嗎?我們認為我們在現實世界中看到的大部分被建造在大腦中從相對較稀疏的視覺資訊。我們認為我們看到整個現實世界場景中一個大全景,但在任何時間點上,我們真的很專注于只有它的一個小細節。它幾乎是不可能,這些單獨的視覺片段拼湊成複合畫類似于真實世界。當畫布上輔以一種機制很像一個攝像頭是容易得多 — — 本身模仿光學的人的眼睛,但沒有眼的不足之處。

一個類似的過程發生在電腦圖形學:當呈現 2D 圖形,隱喻的畫布上工作只是正常。畫布是一個繪圖表面上,並以其最明顯的形式,它直接對應的行和列組成視頻顯示的圖元。

但在移動時從 2D 到 3D,隱喻畫布需要輔以隱喻的相機。像真實世界攝像機,這隱喻相機捕捉 3D 空間中的物件和將它們展平到一個曲面,然後可以轉移到畫布上。

攝像機特性

可以有幾個特點,輕鬆地轉換為所需的 3D 圖形的數學描述描述真實世界攝像機。相機有空間,可以表示為一個 3D 點中的特定位置。

相機也被為了在特定的方向:3D 向量。您可以通過獲取物件位置的攝像機直接對準,並減去攝像機的位置來計算此向量。

如果你把相機固定在特定的位置,並指出在一個特定的方向,你仍然有自由傾斜相機一邊到另一邊,並實際上將它旋轉 360 度。這意味著另一個向量是需要以指示"上"與相機。此向量是相機方向垂直的。

建立後如何在空間中定位相機,你能擺弄觀景窗上的旋鈕。

今天的觀景窗往往能夠縮放:您可以調整相機鏡頭從廣角涵蓋到長焦的視圖,為結束了縮小了大場面。區別基於捕捉的圖像,在飛機和協調中心,如中所示是光線穿過,單點之間的距離圖 1。焦平面與協調中心之間的距離叫做焦距長度。

The Focal Plane, Focal Point and Focal Length圖 1 的焦平面、 聯絡中心和焦距長度

焦平面和焦距的大小意味著角的視野所產生的協調中心。長焦視圖 (更加正確地稱為"長焦點") 是通常與視野小於 35 度,雖然廣角是大於 65 度,與更多普通欄位的視圖之間。

電腦圖形添加攝像機不可能在現實生活中的一個特徵:在 3D 圖形程式設計你常常有一個實現的角度或正交投影的攝像機之間的選擇。視角就像真實生活:物件從相機似乎較小,因為欄位的視圖包含進一步協調中心從更大的範圍。

與正交投影,那不是這種情況。一切都是在該物件的實際規模大小,其與相機的距離大小中呈現。數學上,這是較簡單的兩種預測,,是最合適的技術和建築圖紙。

觀景窗變換

在 3D 圖形程式設計中,攝像機是數學構造。相機組成的類似操作物件在 3D 空間中的那兩個矩陣轉換。兩個觀景窗變換稱為視圖和投影。

視圖矩陣有效職位和安置觀景窗在 3D 空間 ; 投影矩陣描述什麼攝像頭"看見"和它如何看待它。畢竟應用轉換這些相機其他矩陣轉換是用來在 3D 空間中,物件的位置通常稱為"世界空間"。在所有其他轉換之後首先應用視圖變換,和投影變換的最後。

DirectX 程式設計中 — — 是否 Direct3D 或 3D 概念在 Direct2D 中的探索 — — 這是最簡單的方法構建這些矩陣轉換使用 DirectX 數學庫,DirectX 命名空間中的函數,以 XM 字母開頭,並使用 XMVECTOR 和 XMMATRIX 的資料類型的集合。這兩種資料類型是 CPU 寄存器的代理,因此這些函數往往是相當迅速。

四個功能可以用來計算視圖矩陣:

  • XMMatrixLookAtRH (EyePosition,FocusPosition,UpDirection)
  • XMMatrixLookAtLH (EyePosition,FocusPosition,UpDirection)
  • XMMatrixLookToRH (EyePosition,EyeDirection,UpDirection)
  • XMMatrixLookToLH (EyePosition,EyeDirection,UpDirection)

函數的參數包括"眼"一詞,但文檔使用 word"相機"。

縮寫,LH 和 RH 立場為左手和右手。我會假設這些例子左手座標系統。如果你的 X 軸和你中指的正面 Y 方向的積極方向點的你的左手食指,拇指將指向正 Z。如果正 X 軸轉右,並積極 Y 向上 (在 3D 程式設計中的共同方向) 然後 — — Z 軸來的螢幕。

所有四個函數需要三個類型的物件的 XMVECTOR 並返回一個 XMMATRIX 類型的物件。在所有四個函數中,這些參數的兩個顯示攝像機的位置 (標記為 EyePosition 的函數範本中) 和 UpDirection。看功能包括一個 FocusPosition 參數 — — 一個攝像機正對著的位置 — — 雖然 LookTo 函數有 EyeDirection,是一個向量。它是只是一個簡單的計算,要從一種形式轉換到另。

例如,假設您想要放置相機,在點 (0,0,– 100),用相機頂部的向上指指點向原點方向 (和,因此,在積極的 Z 軸方向)。你可以調用任一

XMMATRIX view =
  XMMatrixLookAtLH(XMVectorSet(0, 0, -100, 0),
                   XMVectorSet(0, 0, 0, 0),
                   XMVectorSet(0, 1, 0, 0));

XMMATRIX view =
  XMMatrixLookToLH(XMVectorSet(0, 0, -100, 0),
                   XMVectorSet(0, 0, 1, 0),
                   XMVectorSet(0, 1, 0, 0));

在任一情況下,該函數創建此視圖矩陣:

此特定的視圖矩陣只是轉移中的正 Z 軸方向的整個 3D 場景 100 單位。 許多視圖矩陣還將涉及各種類型,但無縮放的旋轉。 視圖變換應用到 3D 場景後,可以假定 (與相機頂部的指出的正面 Y 方向) 在原點定位相機,並指出,在積極的 Z 方向 (為左手的系統) 或 (右側) — — Z 方向。 這種定位允許投影變換要簡單得多會以其它方式比。

投影公約

在此列以前進軍 3D 內 Direct2D 程式設計,我過的物件從 3D 空間轉換到 2D 視頻顯示簡單地通過忽略的 Z 座標。

它是從 3D 轉換為 2D 以更專業的方式在標準攝像機投影矩陣中使用封裝的約定的時間。 2D 到 3D 的標準轉換實際上發生在兩個階段:第一次從三維座標到歸一化的三維座標,然後到二維座標。 由程式指定的投影變換控制第一個轉換。 在 Direct3D 程式設計中,第二個轉換是通常會自動執行呈現系統。 使用 Direct2D 來顯示 3D 圖形的程式,必須執行此第二個轉換本身。

投影變換的目的是部分實現正常化場景中的所有三維座標。 這種正常化定義哪些物件中是可見的最終呈現和哪些被排除。 之後這種正常化,最終渲染的場景涵蓋範圍從 – 1 的左側和右側 1 的座標,Y 座標從 – 1 底部到頂部,和 Z 座標範圍從 0 (靠近相機) 至 (距離最遠相機) 1 1 X。 Z 座標也用於確定哪些物件掩蓋與檢視器中的其他物件。

一切都不在這一空間被丟棄,然後的歸一化座標 X 和 Y 座標映射到的寬度和高度的顯示圖面,而忽略的 Z 座標。

正常化的 Z 座標,總是計算投影矩陣的函數需要參數類型的浮命名 NearZ 和 FarZ 表示沿 Z 軸的攝像機的距離。 這些兩個距離被分別轉換為正常化 Z 座標的 0 和 1。

這是似乎違反常理的因為它意味著有一個區域是太接近的 3D 空間的觀景窗是可見的,太遠的另一個領域。 但由於實際原因有必要限制以這種方式的深度。 相機背後的一切必須予以消除,例如,與物體過於接近相機會掩蓋其他一切。 如果 Z 座標出到無窮大被允許,將徵稅浮點數的決議,確定哪些物件重疊他人時。

由於相機視圖矩陣占可能平移和旋轉的相機,投影矩陣總是基於攝像機位於原點和沿 Z 軸指向。 我會用左手座標為這些示例中,這意味著相機指出積極的 Z 軸的方向。 左手座標時處理投影變換因為 NearZ 和 FarZ 都是等於沿正 Z 軸,而不是 — — Z 軸座標變得簡單一點。

DirectX 數學庫定義計算的投影矩陣 10 職能 — — 四項正投影及六個角度預測,左手座標的一半,一半的右側。

在 XMMatrixOrthographicRH 和 LH 功能,可以指定 ViewWidth 和 ViewHeight 以及 NearZ 和 FarZ。 圖 2 是從積極的 Y 軸上的位置往下看 3D 坐標系上的視圖。 此圖顯示了這些參數如何定義在左手坐標系中可查看的眼球在原點的長方體。

A Top View of an Orthographic Transform
圖 2 頂視圖的正交變換

通常,ViewWidth 到 ViewHeight 的比例是顯示的相同,用於呈現長寬比。 投影變換縮放一切從 — — ViewWidth / 2 到 ViewWidth / 2 系列 – 1 到 1,和以後那些歸一化的座標進行縮放顯示表面呈現的半個圖元寬度。 計算是類似的 ViewHeight。

這裡對 XMMatrixOrthographicLH 與 ViewWidth 和 ViewHeight 的調用設置為 40 和 20 和 NearZ 和 FarZ 設置為 50 和 100,相匹配假設每 10 個單位的刻度線的關係圖:

XMMATRIX orthographic =
  XMMatrixOrthographicLH(40, 20, 50, 100);

這將導致下面的矩陣:

變換公式是:

您可以看到-20 ℃ 和 20 的值將轉換為 – 1 和 1,分別,那 x 和-10 和 10,y 值將轉換為 – 1 和 1,分別。 50 一個 Z 值轉換為 0,和 100 一個 Z 值轉換為 1。

兩個額外的正射投影函數包含單詞偏心,讓您指定左、 右、 頂部,和底部的座標,而不是寬度和高度。

XMMatrixPerspectiveRH 和 LH 函數具有相同的參數作為 XMMatrixOrthograhicRH 和 LH,但定義四個邊的可視範圍 (像一個金字塔頂尖切斷) 中所示圖 3

A Top View of a Perspective Transform
圖 3 頂視圖的透視變換

變換函數中的 ViewWidth 和 ViewHeight 的參數控制的寬度和高度在 NearZ,錐但的寬度和高度在 FarZ 是按比例更大基於 FarZ 到 NearZ 的比率。 此圖還演示如何更多的 x 和 y 座標從相機將映射到相同的空間 (和,因此,使更小) 作為 x 和 y 座標接近相機。

這裡是以相同的參數,使用 XMMatrixOrthographicLH 作為對 XMMatrixPerspectiveLH 的調用:

XMMATRIX perspective =
  XMMatrixPerspectiveLH(40, 20, 50, 100);

此調用創建的矩陣是:

第四列指示非仿射變換的通知 ! 在仿射變換的 m14、 m24 和 m34 值均為 0,和 m44 是 1。 在這裡,m34 是 1 和 m44 是 0。

這是在 3D 程式設計環境中如何實現的角度,那麼讓我們看看詳細的變換乘法:

下面的變換公式中的矩陣乘法運算結果:

請注意 w´ 的值。 在此列的 4 月分期付款的如上所述,3D 轉換中的 w 座標的使用表面上可容納的翻譯,但也帶來了在數學中的同質 (或投影) 座標。 仿射變換總是發生在 3D 4 D 空間哪裡 w 等於 1,但此非仿射變換已移到座標之外,3D 空間的子集。 座標必須搬回入那 3D 空間除以它們所有 w´。 納入這一必要調整的變換公式改為如下:

Z 等於 NearZ 或 50,變換公式時正交投影相同:

從-20 到 20 x 的值被轉換為 x´ 值從­-1 到 1,例如。

對於其他的 z 值,變換公式是不同的和當 z 等於 FarZ 或 100,他們看起來像這樣:

在這個距離相機,從 x 的值從­-40 至 40 變成 x´ 值從-1 到 1。 較大範圍的 x 和 y 值在聯邦共和國武裝部隊總指揮在 NearZ 佔據同一視覺領域作為一個規模較小的範圍。

因為與正射投影功能,兩個附加功能中的函數名稱的單詞偏心和讓你集左、 右、 上、 和底部座標而不是寬度和高度。

XMMatrixPerspectiveFovRH 和 LH 功能讓您指定角的視野 (FOV) 而不是寬度和高度。 此欄位的視圖有可能沿 X 軸和 Y 軸不同。 您需要指定它沿 Y 軸,並且還提供寬度與高度的比例。

若要創建一個透視矩陣一致與前面的示例中,可以用 atan2 函數,等於半高度在 NearZ,一個 y 參數與 x 參數等於 NearZ,然後翻了一番,結果計算欄位的視圖:

float angleY = 2 * atan2(10.0f, 50.0f);

第二個參數是寬度的比率和高度或在此示例 2:

XMMATRIX perspective =
  XMMatrixPerspectiveFovLH(angleY, 2, 50, 100);

此調用的結果只是使用 XMMatrixPerspectiveLH 創建的相同的角度看矩陣中。

圓的文本

在 2 月份的一部分的此列中,我演示了如何創建一個 3D 的文本,圈子和其旋轉進行動畫處理。 然而,我 Direct2D 幾何用於這項工作,遇到性能很差。 3 月列中,我演示了如何 tessellate 成三角形的集合,其中似乎有性能大大優於幾何形狀的文本。 在 5 月的專欄中,我演示了如何使用三角形創建和渲染 3D 物件。

它是時間把這些不同的技術。 為此列的可下載的原始程式碼包含一個叫做 TessellatedText3D 的 Windows 8 Direct2D 專案。 此程式我定義了一個 3D 的三角形,使用 XMFLOAT3 物件:

struct Triangle3D
{
  DirectX::XMFLOAT3 point1;
  DirectX::XMFLOAT3 point2;
  DirectX::XMFLOAT3 point3;
};

TessellatedText3DRenderer 類的建構函式在字體檔中載入、 創建字體並生成 ID2D1Path­從使用任意字體大小為 100 的 GetGlyphRunOutline 方法的幾何物件。 這個幾何然後轉換成三角形與自訂鑲嵌接收器使用 Tessellate 方法的集合。 用特定的字體,字體大小和標誌符號索引指定了,Tessellate 生成 1,741 的三角形。

2D 三角形,然後轉換成 3D 三角形文字環繞一圈。 基於這種任意字體大小的 100,這個圈子碰巧有一半徑約 200 (存儲在 m_source­半徑),和圈子的 3D 起源上居中顯示。

TessellatedText3DRenderer 類的更新方法,在 XMMatrixRotationY 和 XMMatrixRotationX 函數提供兩個變換進行動畫處理的文本圍繞 X 軸和 Y 軸的旋轉。 這些存儲在 XMMATRIX 物件分別命名為 rotateMatrix 和 tiltMatrix。

Update 方法比繼續在所示的代碼圖 4。 此代碼計算視圖矩陣和投影矩陣。 視圖矩陣基於圓半徑,因此相機是圓形文本外部的半徑長度,但指出朝中心 — — Z 軸上設置攝像機的位置。

圖 4 視圖和投影矩陣在 TessellatedText3D

void TessellatedText3DRenderer::Update(DX::StepTimer const& timer)
{
  ...
// Calculate camera view matrix
  XMVECTOR eyePosition = XMVectorSet(0, 0, -2 * m_sourceRadius, 0);
  XMVECTOR focusPosition = XMVectorSet(0, 0, 0, 0);
  XMVECTOR upDirection = XMVectorSet(0, 1, 0, 0);
  XMMATRIX viewMatrix = XMMatrixLookAtLH(eyePosition,
                                         focusPosition,
                                         upDirection);
  // Calculate camera projection matrix
  float width = 1.5f * m_sourceRadius;
  float nearZ = 1 * m_sourceRadius;
  float farZ = nearZ + 2 * m_sourceRadius;
  XMMATRIX projMatrix = XMMatrixPerspectiveLH(width,
                                              width,
                                              nearZ,
                                              farZ);
  // Calculate composite matrix
  XMMATRIX matrix = rotateMatrix * tiltMatrix *
                    viewMatrix * projMatrix;
  // Apply composite transform to all 3D triangles
  XMVector3TransformCoordStream(
    (XMFLOAT3 *) m_dstTriangles3D.data(),
    sizeof(XMFLOAT3),
    (XMFLOAT3 *) m_srcTriangles3D.data(),
    sizeof(XMFLOAT3),
    3 * m_srcTriangles3D.size(),
    matrix);
  ...
}

帶參數也基於圓半徑,代碼繼續通過計算一個投影矩陣,然後將所有的矩陣相乘在一起。 XMVector3TransformCoordStream 使用並行處理,以將此轉換應用於一個 XMFLOAT3 物件 (實際上的 Triangle3D 物件的陣列) 的陣列,自動執行該司的 w*'*。

更新方法繼續超越所示圖 4 通過轉換那些轉換為 2D 所用的基礎的視頻顯示,一半寬度的縮放因數的三維座標和忽略 z 座標。 更新方法也使用 3D 的每個三角形的頂點計算一個跨產品,是正常的表面 — — 一個向量垂直于表面。 然後,2D 三角形分為兩個組的基礎的 z 座標的曲面。 如果 z 座標為負值,三角形朝向檢視器,和如果是正面,三角正面臨消失。 Update 方法得出結論通過創建兩個基於 2D 三角形的兩個組的 ID2D1Mesh 物件。

Render 方法然後顯示用灰色畫筆的後方三角形網格和前臺網格用藍色畫筆。 結果顯示在圖 5

The TessellatedText3D Display
圖 5 TessellatedText3D 顯示

正如您所看到的正面三角形更接近到檢視器是遠遠大於背面三角形更遠。 這個程式有沒有一個具有呈現幾何形狀時遇到的性能問題。

與光的網底?

在此列 5 月分期付款,我光考慮到了顯示 3D 物件時。 程式假定光來自一個特定的方向和它計算不同網底邊的基於角度的光線照射到每個表面的固體數位。

與此程式是相似的事可能嗎?

理論上是有, 但我的實驗揭示了一些嚴重的性能問題。 在 Update 方法中創建兩個 ID2D1Mesh 物件,而是實施網底的文本的程式需要呈現每個三角形用不同的顏色,而這需要 1,741 不同的 ID2D1Mesh 物件,在更新的每個調用中重新創建和 1,741 對應的 ID2D1SolidColorBrush 物件。 這減慢到每秒大約一幀動畫。

更糟的是,視覺效果並不令人滿意。 每個三角形獲取不同的離散純色基於其角度的光源,但這些離散顏色之間的界限變得可見 ! 三角形用於呈現 3D 物件更加適當地必須以與三個頂點之間的漸變著色,但這種漸變不支援由從 ID2D1Brush 派生的介面。

這意味著我必須甚至深挖 Direct2D 並獲得相同的網底工具,Direct3D 程式師使用。

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

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