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);


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


只有陰影...


下載地址:點擊這裏


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