本文章是由機器翻譯。

DirectX 要素

2D 點陣圖上的 3D 轉換

Charles Petzold

下載代碼示例

三維圖形程式設計是主要問題創造光學幻象。圖像呈現在平板組成的二維陣列的圖元為單位),但這些物件必須似乎有第三個層面與深度。

大概網底 3D 幻覺的最大貢獻 — — 的藝術與科學,以便表面類似于真實世界紋理與照明和陰影著色圖元為單位)。

這一切,但是,是基礎設施的三維座標所描述的虛擬物件的下方。最終,這些三維座標將拼合到 2D 空間,但直到這最後一步,三維座標經常有系統地修改以各種方式通過變換使用。數學上,變換的矩陣代數中的操作。實現這些 3D 轉換流利想要成為一個 3D 圖形程式師的人來說至關重要。

最近,我一直探索 3D 支援 DirectX 的 Direct2D 元件中的幾種方法。探索 3D 內的相對熟悉性和舒適性的 Direct2D 允許您熟悉 3D 概念在 Direct3D 非常可怕深陷入之前。

在 3D 中的點陣圖嗎?

在 Direct2D 支援 3D 的幾種方式之一被藏作為對由 ID2D1DeviceCoNtext 定義的 DrawBitmap 方法的最後一個參數。此參數允許您對 2D 點陣圖應用 3D 轉換。(此功能是特別為 ID2D1DeviceCoNtext。它不支援由 ID2D1RenderTarget 或其他介面派生的從那所定義的 DrawBitmap 方法)。

由 ID2D1DeviceCoNtext 定義的 DrawBitmap 方法有最後一個參數的類型 D2D1_MATRIX_4X4_F,這是一個 4 × 4 變換矩陣,執行一個 3D 轉換點陣圖圖像呈現到螢幕:

void DrawBitmap(ID2D1Bitmap *bitmap,
                D2D1_RECT_F *destinationRectangle,
                FLOAT opacity,
                D2D1_INTERPOLATION_MODE interpolationMode,
                const D2D1_RECT_F *sourceRectangle,
                const D2D1_MATRIX_4X4_F *perspectiveTransform)

這似乎是 D2D1_MATRIX_4X4_F 的唯一目的。它不是在 DirectX 中其他地方使用。在 Direct3D 程式設計中,DirectX 數學庫用於相反表示 3D 轉換。

DD2D1_MATRIX_4X4_F 是一個為 D2D_MATRIX_4X4_F,在中定義的 typedef 圖 1。它基本上是在四行四列中排列的 16 的浮點型值的集合。您可以引用的第三行和使用資料成員 _32,第二列中的值或你可以在從零開始的陣列元素 m [2] [1] 的值相同。

圖 1 3D 變換矩陣應用於點陣圖

typedef struct D2D_MATRIX_4X4_F
{
  union
  {
    struct
    {
      FLOAT _11, _12, _13, _14;
      FLOAT _21, _22, _23, _24;
      FLOAT _31, _32, _33, _34;
      FLOAT _41, _42, _43, _44;
    } ;
    FLOAT m[4][4];
  };
} D2D_MATRIX_4X4_F;

然而,當您顯示變換的數學,我會相反指的第三行和第二列的矩陣中的元素作為 m32。整個矩陣可以用傳統的矩陣標記法,就像這樣:

3D 點陣圖變換在 Direct2D 是在 Windows 運行時,它出現的 Matrix3D 結構和由 UIElement 定義的投影屬性作為一個類似設施的基礎。這兩種機制是很相似的你可以相互促進你的知識和經驗之間的兩種環境。

線性變換

很多人遇到 3D 變換的第一次問:為什麼是一個 4 × 4 矩陣?一個 3 × 3 的矩陣是應該足夠的 3D 嗎?

要回答這個問題,同時在 DrawBitmap 上探索 3D 變換,我創建了一個名為 BitmapTransformExperiment,包括與這篇文章的可下載代碼的程式。此套裝程式含自製微調框控制項,允許您選擇值的 16 元素的變換矩陣,並查看矩陣是如何影響顯示的點陣圖。圖 2 顯示了典型的顯示。


圖 2 BitmapTransformExperiment 程式

您最初的實驗,將限制您注意頂部三行和最左側的三個列的矩陣。這些組成了一個 3 × 3 矩陣,執行下列轉換:

在左側的 1 × 3 矩陣表示三維座標。為點陣圖,點陣圖的 x 值範圍為從 0 到點陣圖的寬度 ; y 範圍從 0 到的點陣圖高度 ; 和 z 是 0。

1 × 3 矩陣乘以 3 × 3 變換矩陣,標準矩陣乘法結果在轉換後的座標:

恒等矩陣 — — 在 m11、 m22 和 m33 的對角元素是所有 1 和一切都是 0 — — 在沒有轉換的結果。

因為我開始與平面點陣圖,z 座標是 0,因此 m31、 m32 和 m33 已對結果沒有影響。當變換的點陣圖呈現到螢幕上,(x*',y',z') 結果折疊到一個平面的二維座標系統上通過忽略 z'* 協調,也就是說 m13、 m23 和 m33 有沒有影響。這就是為什麼第三行和第三列顯示為灰色的 BitmapTransformExperiment 程式中。您可以設置這些矩陣元素的值,但它們不會影響該點陣圖的呈現方式。

你會發現的是那 m11 是預設值為 1 的水準縮放因數。使它更大或更小,以增加或減少的點陣圖的寬度,或使它消極翻轉繞垂直軸的點陣圖。同樣,m22 是垂直的縮放因數。

M21 值是垂直傾斜因數:當非 0 值作為右邊緣被轉移向上或向下將矩形點陣圖變成一個平行四邊形。同樣,m12 是一個水準的扭曲因素。

水準傾斜和垂直傾斜的組合可以導致旋轉。若要旋轉點陣圖順時針由一個特定的角度,可以設置 m11 和 m22,為這種視角設置 m21 為角度的正弦值,設置 m12 到負的正弦值的余弦值。圖 2 顯示旋轉 30 度。

在 3D 中所示的旋轉的上下文中圖 2 是實際上圍繞 Z 軸,在概念上延伸出從螢幕的旋轉。變換矩陣 α 的角度來說,看起來像這樣:

它也是可能要旋轉點陣圖的 Y 軸或 X 軸的周圍。繞 Y 軸旋轉的角度並不影響的 y 座標值,因此,變換矩陣是:

如果您嘗試此與 BitmapTransformExperiment 程式,你會發現只有 m11 值有影響。只將它設置為旋轉角度的余弦值減少呈現點陣圖的寬度。這減少的寬度是符合繞 Y 軸旋轉的角度。

同樣地,這是繞 X 軸旋轉的角度:

在 BitmapTransformExperiment 程式中,這會導致在減少點陣圖的高度。

變換矩陣中的兩個正弦因素的跡象控制旋轉方向。從概念上講,正 Z 軸,則假定在螢幕中,擴展和旋轉遵循左手的規則:對齊旋轉軸的左手的拇指和使其朝向正面的價值觀 ; 曲線的其他手指指示為正面角度的旋轉方向。

由這 3 × 3 變換矩陣表示 3D 變換的類型稱為線性變換。變換只涉及常量乘以 x、 y 和 z 座標。無論您在第三行和變換矩陣的列中輸入什麼號碼,點陣圖從來沒有轉化為任何更多比一個平行四邊形 ; 異國情調 三個維度,一個多維資料集總是被變換成一個方柱體。

你見過如何點陣圖可以縮放、 傾斜和旋轉,但在本練習中,整個點陣圖左上角一直固定在一個單一的地點。(該位置被受在 DrawBitmap 調用之前的 Render 方法中設置一個 2D 轉換)。無法移動的點陣圖左上角結果從線性變換的數學。沒有什麼可以轉移的變換公式中 (0,0,0) 點到另一個位置,是一種類型的變換稱為翻譯。

實現翻譯

若要瞭解如何獲取翻譯在 3D 中的,讓我們簡要地認為有關 2D 轉換。

在兩個維度是一個 2 × 2 矩陣,線性變換和它是能夠縮放、 傾斜和旋轉。若要獲取以及翻譯,假定 2D 圖形的存在在 3D 空間中,但其中的 z 座標始終等於 1 2D 平面上。以容納額外的維度,2D 線性變換矩陣在擴展成一個 3 × 3 的矩陣,但通常固定的最後一行:

在這些矩陣乘法結果變換公式:

M31 和 m32 的因素,翻譯的因素。這一過程背後的秘密是在兩個維度的翻譯是相當於三個方面傾斜。

一個類似的過程用於 3D 圖形:實際上假定的 3D 座標是其中的第四度空間的座標是 1 4 D 空間中存在。但若要表示一個 4 D 座標點,你有傻一點實際的問題:三維點是 (x,y,z) 和無字母 z 之後, 來所以什麼字母你用第四維度?最接近的可用字母是 w,所以 4 D 座標是 (x、 y、 z、 w)。

3D 線性變換矩陣被擴大到 4 × 4 以容納額外的維度:

現在是變換公式:

您現在可以沿 X、 Y 和 Z 軸與 m41、 m42、 m43 的翻譯。計算似乎發生在 4 D 的空間,但它實際上只限于 4 D 空間的 w 座標總是 1 3D 剖面。

同質座標

如果你玩的 m41 和 m42 的值在 BitmapTransformExperiment 中,您會看到他們的確導致在水準和垂直的翻譯。

但那最後矩陣的行嗎?如果您不將那最後一行限制為 0 和 1,將會發生什麼呢?這裡是全 4 × 4 變換應用於三維點:

X 的公式*',y'* 和 z*'* 仍然是相同的但 w*'* 現在這樣計算:

這是一個真正的問題。以前,一個小技巧被雇用要使用其中的 w 座標始終等於 1 4 D 空間 3D 剖面。但現在的 W 座標不再是 1,並且你已經被推進了那 3D 剖截面。你迷失在 4 D 空間中,你需要回到那 3D 剖截面其中 w 等於 1。

沒有需要建立一維跨時空機,然而。幸運的是,您可以跨越數學上所有轉換後的座標除以 w*'*:

現在 w*'* 等於 1 和你回來回家 !

但是代價呢?你現在在變換公式中,有司,很容易看到那分母怎麼可能在某些情況下 0。這會導致無限座標。

嗯,也許這是一件好事。

當德國數學家 8 月斐迪南莫比烏斯 (1790–1868) 發明了剛剛描述的系統 (稱為"同質座標"射影座標") 時,他的目標之一是代表無限座標使用有限的數位。

矩陣的 0 和 1 中的最後一行稱為仿射變換,也就是說它不會導致無限,所以能夠無限的變換稱為非仿射變換。

在 3D 圖形中,非仿射變換是極其重要的因為這是如何實現的角度。大家都知道現實生活中,從你的眼睛的物件似乎較小。在 3D 圖形中,你獲得這種效果並不是一個常量 1 分母與。

試試看在 BitmapTransformExperiment 中:如果你讓 m14 一個小的正數 — — 只有小值為需和有趣的結果 — — 然後值的 x 和 y 都按比例減少,因為 x 獲取更大。使 m14 小的負數和較大值的 x 和 y 增加。圖 3 顯示結合一個非零 m12 值的影響。呈現的點陣圖不再是一個平行四邊形和角度表明的右邊緣有搖擺你的眼睛更接近。


圖 3 中 BitmapTransformExperiment 的觀點

同樣,m24 非零值可以使的頂部或底部的點陣圖看似擺動朝向您或更遠。在真正的 3D 程式設計中,它是人們常用的 m34 值為,允許增加或減少的大小根據其 z 座標的物件 — — 他們從觀眾的眼睛的距離。

當 3D 變換應用到 2D 物件時,m44 值通常是 1,但它可以作為一個整體的比例因數。在真實 3D 程式設計中,m44 是通常設置為 0 涉及的角度來看,因為在概念上相機是在原點。值為 0 的 m44 僅適用如果 3D 物件不具有 z 座標為 0,但工作時與 2D 物件,z 座標始終是 0。

任何凸四邊形

在將此 4 × 4 變換矩陣應用於平面點陣圖,你只有一半的矩陣元素的使用。即便如此, 圖 3 顯示的東西你不能做與正常二維 Direct2D 變換矩陣,它是應用一個變換,變成一個平行四邊形以外的一個矩形。事實上,在大多數的 Direct2D 使用的變換物件稱為 D2D1_MATRIX_3X2_F 和 Matrix3x2F,強調的第三行和無法執行非仿射變換的不便。

與 D2D1_MATRIX_4X4_F,它有可能得出一個變換,將點陣圖映射到任何凸四邊形 — — 這就是,任何任意四面的圖哪裡,雙方別交叉和內角頂點上哪裡小於 180 度。

如果你不相信我,試著玩,NonAffineStretch 程式。請注意此程式適應從 Windows 運行程式,也稱為 NonAffineStretch,我的書,"Windows 程式設計,第 6 版"的第 10 章中 (微軟出版社,2013年)。

圖 4 NonAffineStretch 顯示中使用。您可以使用滑鼠或手指要在螢幕上拖動到任何位置的綠色圓點。只要你保持圖凸四邊形,4 × 4 變換可以獲得基於點的位置上。該轉換用於繪製點陣圖,也顯示在右下角。涉及只有八個值 ; 中的第三行和第三列的元素的預設值,並總是 m44 始終是 1。


圖 4 NonAffineStretch 程式

這背後的數學是有點嚇人,但演算法的推導我的書的第 10 章中所示。

Matrix4x4F 類

若要使工作與 D2D1_MATRIX_4X4_F 的結構更容易一點,D2D1 命名空間中的 Matrix4x4F 類派生自該結構。此類定義一個建構函式和乘法運算子 (這我 NonAffineStretch 演算法中使用),和幾個有用靜態的方法用於創建共同變換矩陣。例如,Matrix4x4F::RotationZ 方法接受一個參數,是以度為單位的角度,並返回一個矩陣,它表示由這個角度圍繞 Z 軸旋轉:

其他 Matrix4x4F 函數創建為繞 X 軸和 Y 軸,旋轉並繞任意軸,這是一個困難得多的矩陣旋轉矩陣。

調用 Matrix4x4F::PerspectiveProjection 函數有一個參數命名為深度。它返回的矩陣是:

這意味著 x 的變換公式是:

同樣 y*'* 和 z*'*,這意味著每當 z 座標等於深度,分母為 0,和所有座標變都得無限。

從概念上講,這意味著您正在查看的深度單位,距離相同,螢幕本身含義圖元或設備無關的單位,單位在哪裡從電腦螢幕。如果一個繪圖物件有等於深度的 z 座標,它就是深度單位在螢幕上,這樣是正確的你的眼球 !該物件應顯示給你非常大 — — 數學上無限。

不過請等一下:D2D1_MATRIX_4X4_Fstructure 和 Matrix4x4F 類的唯一目的就是為 DrawBitmap,對的調用和點陣圖總是有 z 座標為 0。所以如何不會 – 1/深度此 m34 值有任何影響?

如果 PerspectiveProjection 矩陣由本身的 DrawBitmap 調用中使用的實際上也會沒有效果。但它已打算與其他矩陣變換一起使用。可以通過將它們相乘加劇矩陣變換。雖然原始點陣圖已沒有 z 座標,z 座標值將忽略呈現,z 座標可以肯定在發揮作用複合變換。

讓我們看一個例子。RotatingText 程式創建的點陣圖與文本"旋轉",只是大約一半的寬度是螢幕的寬度。很多的更新和渲染方法所示圖 5

圖 5 代碼從 RotatingTextRenderer.cpp

void RotatingTextRenderer::Update(DX::StepTimer const& timer)
{
  ...
  // Begin with the identity transform
  m_matrix = Matrix4x4F();
  // Rotate around the Y axis
  double seconds = timer.GetTotalSeconds();
  float angle = 360 * float(fmod(seconds, 7) / 7);
  m_matrix = m_matrix * Matrix4x4F::RotationY(angle);
  // Apply perspective based on the bitmap width
  D2D1_SIZE_F bitmapSize = m_bitmap->GetSize();
  m_matrix = m_matrix * 
    Matrix4x4F::PerspectiveProjection(bitmapSize.width);
}
void RotatingTextRenderer::Render()
{
  ...
  ID2D1DeviceContext* context = 
    m_deviceResources->GetD2DDeviceContext();
  Windows::Foundation::Size logicalSize = 
    m_deviceResources->GetLogicalSize();
  context->SaveDrawingState(m_stateBlock.Get());
  context->BeginDraw();
  context->Clear(ColorF(ColorF::DarkMagenta));
  // Move origin to top center of screen
  Matrix3x2F centerTranslation =
    Matrix3x2F::Translation(logicalSize.Width / 2, 0);
  context->SetTransform(centerTranslation *
    m_deviceResources->GetOrientationTransform2D());
  // Draw the bitmap
  context->DrawBitmap(m_bitmap.Get(),
                      nullptr,
                      1.0f,
                      D2D1_INTERPOLATION_MODE_LINEAR,
                      nullptr,
                      &m_matrix);
  ...
}

在更新方法中,Matrix4x4F::RotationY 方法創建下列轉換:

這乘以所示的矩陣從的 Matrix4x4F::PerspectiveProjection 方法,較早前返回,你會得到:

變換公式是:

這些必然牽涉的角度看,,你可以看到的結果圖 6


圖 6 RotatingText 顯示

小心:Matrix4x4F::Perspective 的深度參數­投影設置為點陣圖寬度,所以當旋轉點陣圖,在它附近波動可能會非常接近你的鼻子。

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

感謝以下 Microsoft 技術專家對本文的審閱:JimGalasyn 和邁克財富