• 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!!! ==

    作者:zdd
    出处:http://www.cnblogs.com/graphics/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Python 爬虫 —— BeautifulSoup
    sklearn、theano、TensorFlow 以及 theras 的理解
    sklearn、theano、TensorFlow 以及 theras 的理解
    keras 的使用
    keras 的使用
    古人的字、号、别称
    古人的字、号、别称
    hdu1226 超级密码 (BFS,里面用了大数取余原理)
    2013渣打科营编程马拉松赛样题
    对象序列化实现深度克隆
  • 原文地址:https://www.cnblogs.com/graphics/p/2689801.html
Copyright © 2020-2023  润新知