Direct2D教程(十一)幾何變換


什么是幾何變換(Transform)

在圖形學中,主要有三種幾何變換,分別是平移(Translate),旋轉(Rotation)和縮放(Scaling)。在D2D中,這三種變換都有實現,而且還有一種不太常見的變換,傾斜(Skewing)。

Illustration of a square moved 20 units along the positive x-axis and 10 units along the positive y-axis    Illustration of a square rotated 45 degrees clockwise about the center of the original square    Illustration of a square scaled 130%    Illustration of a square skewed 30 degrees counterclockwise from the y-axis

Transform是指將一個點從一個坐標系映射到另一個坐標系,或者將一個點從同一個坐標系的一個位置映射到另外一個位置。在實際應用中,通常是將幾何圖形從一個位置變換到另外一個位置,而圖形是由多個頂點組成的,只要將圖形中的所有頂點都變換一下,那么整個圖形就完成了變換,所以變換的本質是對頂點的變換。在D2D中,用如下三維矩陣M來表示這種變換。

矩陣M的默認值是單位矩陣。

由於仿射變換矩陣的第三列一定是0.0, 0.0, 1.0,而D2D只支持仿射變換,所以我們可以將第三列省略。則上面的矩陣就變成了一個3X2的矩陣,在D2D中用D2D1_MATRIX_3X2來表示這樣一個矩陣。如下

D2D坐標系

Direct2D使用左手坐標系,X軸向右為正,Y軸向下為正。如下圖。

變換的對象

在Direct2D中有三種方式來實現幾何變換,分別是

  • Geometry Transform
  • Render target Transform
  • Brush Transform

其中前兩者比較常見,后者比較少見。Geometry Transform很好理解,與D3D中的變換類似,就是直接變換圖形本身。Render target Transform變換的是Render target,可以將Render target想象成一塊大畫布,如果我們想把圖形(相當於畫布上的畫)從一個位置移動到另外一個位置,有兩種方法,一種是在新位置重新畫一個模型,這相當於Geometry Transform,另一種是獎畫布移動一定的偏移量(新舊位置之差),然后在原來的位置上畫圖,這就相當於Render target變換了。至於畫刷變換,則是這三者中最難理解的,畫刷變換常常用於圖片的繪制,稍候詳述。

Geometry Transform

幾何圖形變換的本質是根據原來的幾何圖形生成變換后的圖形,函數ID2D1Factory::CreateTransformedGeometry就是用來干這件事的。該函數的定義如下:第一參數是變換前的幾何圖形,第二個參數是變換矩陣,第三個參數是個輸出參數,用來接收變換后的幾何圖形。

virtual HRESULT CreateTransformedGeometry(
  [in]            ID2D1Geometry *sourceGeometry,
  [in, optional]  const D2D1_MATRIX_3X2_F *transform,
  [out]           ID2D1TransformedGeometry **transformedGeometry
) = 0;

下面代碼演示了如何使用CreateTransformedGeometry變換一個幾何圖形。

// 創建新的變換矩形
// m_pRectangleGeometry 是變換前的矩形
// m_pTransformedGeometry 是變換后的矩形
// D2D1::Matrix3x2F::Scale 用來生成變換矩陣,以(175, 175)為中心,放大3倍
hr = m_pD2DFactory->CreateTransformedGeometry(
 m_pRectangleGeometry,
 D2D1::Matrix3x2F::Scale(
     D2D1::SizeF(3.f, 3.f),
     D2D1::Point2F(175.f, 175.f)),
 &m_pTransformedGeometry
 );

// 清除render target上原有的變換。
m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());

// 繪制變換后的矩形。
m_pRenderTarget->DrawGeometry(m_pTransformedGeometry, m_pBlackBrush, 1);

Render target Transform

Render target是從接口ID2D1RenderTarget繼承下來的一種資源類型,它負責創建繪圖所需的資源並執行實際的繪制操作。當然它也提供了坐標變換的方法,可以調用函數ID2D1RenderTarget::SetTransform對render target進行變換。變換之后,所有后續的繪制操作都會在變換后的render target中進行。對render target進行變換會影響render target上的圖形,如果想變換某個圖形而不影響其它的,可以先保存當前的世界矩陣,待變換完畢后再應用一次這個矩陣即可。函數ID2D1RenderTarget::SetTransform()的定義如下:只有一個參數,就是變換用的矩陣。

virtual void SetTransform(
  [in]  const D2D1_MATRIX_3X2_F *transform
) = 0;

下面的代碼演示了如何使用render target變換

// 創建矩形
D2D1_RECT_F rectangle = D2D1::Rect(438.0f, 301.5f, 498.0f, 361.5f);

// 繪制矩形
m_pRenderTarget->DrawRectangle(
    rectangle,
    m_pOriginalShapeBrush,
    1.0f,
    m_pStrokeStyleDash
    );

// 對render target進行旋轉變換
m_pRenderTarget->SetTransform(
    D2D1::Matrix3x2F::Rotation(
        45.0f,
        D2D1::Point2F(468.0f, 331.5f))
    );

// 填充矩形
m_pRenderTarget->FillRectangle(rectangle, m_pFillBrush);

// 繪制矩形
m_pRenderTarget->DrawRectangle(rectangle, m_pTransformedShapeBrush);

效果如下圖

Illustration of a square rotated clockwise 45 degrees about the center of the original square

Brush Transform

使用畫刷繪制時,采用的是render target的坐標空間,畫刷不會自動與繪制區域對齊,而是從render target的原點(0, 0)開始繪制。對於ID2D1BitmapBrush類型的畫刷,可以使用SetTransform方法將位圖移動到指定區域然后進行繪制,這一變換只影響畫刷本身,不影響render target上的其他對象。

下圖是一個畫刷變換的例子,這里使用ID2D1BitmapBrush畫刷來填充一個矩形,矩形的起始位置是(100, 100),長寬都是100。左圖顯示的是畫刷變換前的繪制效果,可以看到位圖是從render target的原點開始繪制的,矩形只有一部分被填充。而右圖是畫刷經過變換(x,y方向各平移50像素)后的繪制效果,可以看到位圖從位置(50, 50)開始繪制,這樣整個矩形都被填充了,而不是像左圖那樣,未填充區域由邊界像素拉伸而成。所以畫刷變換可以將畫刷想象成一張大紙,而獎待填充矩形想象成紙上的模板,進行變換時,模板不動,下面的紙可以來回拉動,進而產生不同的效果。

下面的代碼演示了如何使用畫刷變換。

// Demonstrate the effect of transforming a bitmap brush.
m_pBitmapBrush->SetTransform(
 D2D1::Matrix3x2F::Translation(D2D1::SizeF(50,50))
 );

// To see the content of the rcTransformedBrushRect, comment
// out this statement.
m_pRenderTarget->FillRectangle(
 &rcTransformedBrushRect, 
 m_pBitmapBrush
 );

m_pRenderTarget->DrawRectangle(rcTransformedBrushRect, m_pBlackBrush, 1, NULL);

對於ID2D1LinearGradientBrush類型的畫刷,可以通過改變gradient中的start point和endpoint來進行變換。

對於ID2D1RadialGradientBrush類型的畫刷,可以通過改變其中心和半徑來實現變換。

關於以上兩種畫刷的詳細介紹,請看我之前的一篇博文

幾何圖形變換與render target變換的區別

  • 幾何圖形變換只影響當前變換的圖形,而render target變換則影響所有圖形。
  • 幾何圖形變換只影響圖形的fill(填充),不影響圖形的stroke(邊線),因為變換是在stroke之前進行的。但render target變換則兩者都影響。

解釋一下上面第二點,比如對一個矩形進行縮放變換,如果使用Geometry Transform,那么變換前后的效果圖如下:

如果使用Render target Transform,那么變換前后的效果圖如下:

 

Render target變換對剪裁(Clip)的影響

Render target變換會影響剪裁區域(clipRect)包圍矩形的計算,在D2D中,clipRect是一個矩形,它的包圍矩形(Bounding box)是一個與坐標軸對齊的矩形。如果函數PushAxisAlignedClip被調用了,那么對render target的變換都會按相同效果施加於剪裁區域(clipRect)。在變換完成后,將重新計算clipRect的包圍矩形。為了提高性能,render target所繪制的圖形都由這個包圍矩形進行剪裁,而不是由原始的clipRect進行剪裁。下圖說明了如何計算新的bounding box。

1 下面這個矩形表示一個render target

2 下圖表示對render target進行一次旋轉變換,紅色虛線矩形表示變換后的render target

3 當PushAxisAlignedClip被調用后,旋轉變換也將作用於剪裁區域(clipRect),下圖中藍色矩形表示變換后的clipRect。

4 根據變換后的心cipRect,重新計算其Bounding box,下圖中綠色虛線矩形即新的包圍矩形,render target上繪制的內容將被這個包圍矩形所剪裁。

== Happy Coding!!! ==


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM