Direct2D 1.1 开发笔记 特效篇(二) 简单的自定义特效

(转载请注明出处)


这节就来一个简单的自定义特效作为概念的入门。

首先需要头文件

#include <d2d1effectauthor.h>
#include <d2d1effecthelpers.h>

为了实现一个自定义的D2D特效,需要继承ID2D1EffectImpl并实现其接口。


好了,这里因为仅仅是介绍一下概念,所以这次的自定义特效就定为下阴影吧,微软也是这么干的。


实现就用现成的,因为特效的输入也能是特效。


Transform: 暂时称为"转变"吧,一个转变表示对图像进行一次操作

Transform Graph: 暂时称为"转变逻辑图"吧 就是将各个转变组织起来的顺序,因为特效是单输出多输入的,

所以内部应该是树


之前一节我们知道利用一个GUID即可创建一个特效,所以之前我们应该注册特效:

使用ID2D1Factory1::RegisterEffectFromString进行特效注册

参数1: 需要注册的自定义GUID

参数2: 一个XML字符串,用来形容本效果

参数3: 绑定的控制变量数组

参数4: 数组长度

参数5: 创建对象对调函数的指针,建议是私有化的静态成员方法


生成GUID的工具VS Express for Windows Desktop自带了。

工具-创建GUID

即可创建一个GUID。使用DEFINE_GUID的话,建议尽可能的推迟"initguid.h"文件的包含地点,否则会链接失败




一个XML的例子如下:

    // 第一行不能有\n不知道是不是bug
    const WCHAR* pszXml = LR"xml(<?xml version = "1.0" ?>
<Effect>
    <!--系统属性 请注意 name不支持汉字-->
    <Property name = "DisplayName" type = "string" value = "下阴影效果" />
    <Property name = "Author" type = "string" value = "dustpg" />
    <Property name = "Category" type = "string" value = "Transform" />
    <Property name = "Description" type = "string" value = "如题" />
    <Inputs>
        <Input name = "Source" />
    </Inputs>
    <!--自定义属性 可以写 对输入变量的 说明-->
    <Property name='Offset' type='vector2'>
        <Property name='DisplayName' type='string' value='Image Offset'/>
        <Property name='Min' type='vector2' value='(-1000.0, -1000.0)' />
        <Property name='Max' type='vector2' value='(1000.0, 1000.0)' />
        <Property name='Default' type='vector2' value='(0.0, 0.0)' />
    </Property>
</Effect>
)xml";

使用C++11的原声字符串很方便创建脚本、XML等字符串。

格式都不用说了,一看就明白。自定义属性不是随意的,请参考如下

(有字节数组不就是随意的么╮( ̄▽ ̄)╭ )

Data type Corresponding XML value
PWSTR string
BOOL bool
UINT uint32
INT int32
FLOAT float
D2D_VECTOR_2F vector2
D2D_VECTOR_3F vector3
D2D_VECTOR_4F vector4
D2D_MATRIX_3X2_F matrix3x2
D2D_MATRIX_4X3_F matrix4x3
D2D_MATRIX_4X4_F matrix4x4
D2D_MATRIX_5X4_F matrix5x4
BYTE[] blob
IUnknown* iunknown
ID2D1ColorContext* colorcontext
CLSID clsid
Enumeration (D2D1_INTERPOLATION_MODE, etc.) enum


控制变量绑定:

需要: 名称、读回调接口、写回调接口。

两个接口是成员方法。


比如我们XML写的是一个绑定控制变量: 二维向量

const D2D1_PROPERTY_BINDING bindings[] = {
        D2D1_VALUE_TYPE_BINDING(L"Offset", &SetOffset, &GetOffset),
    };


这样创建绑定

2个方法声明如下:

    HRESULT         SetOffset(D2D_VECTOR_2F offset);
    D2D_VECTOR_2F   GetOffset();




创建函数    里面动态生成一个对象即可,就不多说了




这样就能创建一个特效了,接下来就是实现 ID2D1EffectImpl 接口,

ID2D1EffectImpl继承于IUnknown,这个老伙计的3个接口怎么实现就不说了。


ID2D1EffectImpl::Initialize 初始化对象,创建对象后就会调用这个方法。

用来初始化、准备数据,设置最初的转变逻辑图等。


ID2D1EffectImpl::SetGraph 当输入数量改变时会被调用,大多数特效都是一个输入对象,

仅仅需要返回E_NOTIMPL即可。复数个的就需要自行处理


ID2D1EffectImpl::PrepareForRender 再被渲染前会被调用,比如我们修改了高斯模糊的程度值,

为了改变输出,自然需要准备一下。

我们这里修改偏离量即可



实现设置逻辑图:


如果只有一个转变,则仅仅简单地使用ID2D1TransformGraph::SetSingleTransformNode即可。

但是我们的下阴影有两个转换节点:D2D自带的阴影特效与平移转换。


初始化方法有个提供了一个ID2D1EffectContext参数,这个能用

ID2D1EffectContext::CreateEffect创建已经注册的特效,还有一些内建的转变,就不赘述了。



添加转变节点:

使用ID2D1TransformGraph::AddNode先将所有转变节点添加进来...这是静态链表,哦不,静态树的实现?


ID2D1TransformGraph::ConnectToEffectInput 将本效果的指定输入  连接到   指定转变的指定输入

ID2D1TransformGraph::ConnectNode 将前者的输出端 连接到 后者指定的输入端

ID2D1TransformGraph::SetOutputNode 将指定转变的输出端   作为   本特效的输出端


例子: 熟悉连接各个节点

比如我们要实现一个高级的下阴影——能够将原输入图像显示出来


连接图(假设对象已经创建并且已经被AddNode):

        pTransformGraph->ConnectToEffectInput(0, pShadow, 0);
        pTransformGraph->ConnectNode(pShadow, p2DAffineTransform, 0);
        pTransformGraph->ConnectNode(p2DAffineTransform, pComposite, 0);
        pTransformGraph->ConnectToEffectInput(0, pComposite, 1);
        pTransformGraph->SetOutputNode(pComposite);


更复杂的例子:


连接图(假设对象已经创建并且已经被AddNode):

        pTransformGraph->ConnectToEffectInput(0, pArithmeticComposite, 0);

        pTransformGraph->ConnectToEffectInput(0, pShadow, 0);
        pTransformGraph->ConnectNode(pShadow, pCompositeV1, 0);
        pTransformGraph->ConnectNode(pShadow, pPointSpecular, 0);
        pTransformGraph->ConnectNode(pPointSpecular, pCompositeV1, 1);
        pTransformGraph->ConnectNode(pShadow, p2DAffineTransform, 0);

        pTransformGraph->ConnectNode(pCompositeV1, pArithmeticComposite, 1);

        pTransformGraph->ConnectNode(pShadow, p2DAffineTransform, 0);


        pTransformGraph->ConnectNode(pArithmeticComposite, pCompositeV2, 0);
        pTransformGraph->ConnectNode(p2DAffineTransform, pCompositeV2, 1);

        pTransformGraph->SetOutputNode(pCompositeV2);


好了,下阴影的例子就提供在下面。


只有阴影...


下载地址:点击这里


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章