在WPF中使用著色器

ggtc發表於2024-06-29

概念類比

範疇 CPU GPU
二進位制檔案 .exe .cso / .ps
二進位制指令 機器碼 CSO(shader指令)
助記符 彙編 SL
高階語言 C# HLSL
高階語言檔案 .cs .hlsl / .fx
高階語言編譯器 csc.exe fxc.exe
API .NET API DirectX API
執行時環境 CLR DirectX
除錯工具 Visual Studio Debugger RenderDoc
  • 著色器型別
著色器簡稱 著色器名 解釋
cs_4_0 Compute Shader model 4.0 計算著色器,用於處理非圖形計算任務
ds_5_0 Domain Shader model 5.0 域著色器,用於曲面細分技術中,生成頂點後處理頂點資料
fx_2_0 Effect model 2.0 效果檔案,用於組合多個渲染狀態和著色器程式,方便管理和使用
gs_4_0 Geometry Shader model 4.0 幾何著色器,能接收一些圖形形狀作為輸入,並輸出其他形狀,用於生成新頂點和圖形
hs_5_0 Hull Shader model 5.0 曲面控制著色器,用於圖形的曲面細分
ps_2_0 Pixel Shader model 2.0 畫素著色器,用於計算畫素顏色
tx_1_0 Texture Shader model 1.0 (software) 紋理著色器,主要用於處理紋理對映
vs_1_1 Vertex Shader model 1.1 頂點著色器,用於處理每個頂點資料

3DS Max HLSL編寫與預覽

  • 首先,為了避免折騰和跟上b站的影片教程,下載3DS Max,接著新增一個茶壺
    只是教程用的是Direct9,我們現在用的是Direct11,語法有點差異
    image
    開啟3DS Max,按下快捷鍵M,或者點選材質編輯器
    image
    然後切換模式,換成精簡材質編輯器
    image
    點選物理材質切換自己寫的shader
    image
    選擇DirectX Shader材質
    image
    點選確定
    image
    點選路徑,可選擇自定義材質
    image

可以事先在桌面上新建一個txt檔案,然後把副檔名改為.fx,可以使用vscode或者visualStudio下載HLSL擴充套件進行編輯
這列我提供一個Direct11的最簡單的純色著色器效果檔案solidColor.fx

// solidColor.fx


//世界投影矩陣
//用來將頂點從模型空間轉換到最終的裁剪空間
float4x4 WorldViewProjection : WorldViewProjection < string UIWidget="None"; >;

//UI皮膚專案
float4 SolidColor
<
    string UIWidget = "Color";
    string UIName="Solid Color";
> = float4(1.0f, 1.0f, 1.0f, 1.0f);

struct VertexShaderInput
{
    //頂點著色器輸入用這個語義
    //表示頂點的位置資訊
    //模型空間(或世界空間)中定義的
    float4 Position : POSITION;
};

struct VertexShaderOutput
{
    //頂點著色器輸出用這個語義
    //表示頂點在裁剪空間(Clip Space)中的位置
    //用來決定頂點在螢幕上位置的空間
    float4 Position : SV_Position;
};

struct PixelShaderOutput
{
    float4 Color : SV_TARGET;
};

//================== 簡單的頂點著色器函式
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;

    // 計算最終的頂點位置
    output.Position = mul(input.Position, WorldViewProjection);

    return output;
}


//=============== 基本畫素著色器函式
PixelShaderOutput PixelShaderFunction()
{
    PixelShaderOutput output;

    // 設定畫素顏色為 SolidColor 定義的顏色
    output.Color = SolidColor;

    return output;
}

// 定義渲染效果
//Direct9寫technique
//Direct10寫technique10
//Direct11寫technique11
technique11 SolidColorTechnique
{
    pass P0
    {
        // 基本頂點著色器
        VertexShader = compile vs_5_0 VertexShaderFunction();

        // 基本畫素著色器
        PixelShader = compile ps_5_0 PixelShaderFunction();
    }
}

然後把這個材質拖到模型上
image
引數Solid Color是我們在程式碼中定義的元件,用來選材質顏色

float4 SolidColor
<
    string UIWidget = "Color";
    string UIName="Solid Color";
> = float4(1.0f, 1.0f, 1.0f, 1.0f);

image

WPF著色器編寫與使用

看了下,似乎wpf只支援畫素著色器,不支援頂點著色器。那程式碼就簡化許多了。
第二個問題是wpf中沒有透過HLSL生成UI控制元件,怎麼調整SolidColor?
我看了下HLSL變數宣告語法,原來<DataType名稱 = 值;... ;>是批註語法效果框架能識別,但會被hlsl忽略
https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-syntax

[Storage_Class] [Type_Modifier] Type Name[Index] [: Semantic] [: Packoffset] [: Register]; [Annotations] [= Initial_Value]

wpf中使用的則是Register可選部分,從暫存器讀取輸入

wpfSolidColor.fx

struct PixelShaderOutput
{
    float4 Color : SV_TARGET;
};
float4 SolidColor : register(c0) = float4(1.0f, 1.0f, 1.0f, 1.0f);

//=============== 基本畫素著色器函式
PixelShaderOutput PixelShaderFunction()
{
    PixelShaderOutput output;

    // 設定畫素顏色為 SolidColor 定義的顏色
    output.Color = SolidColor;

    return output;
}
  • 編譯
    然後使用效果編譯工具fxc.exe編譯這個檔案
    ./fxc /T ps_3_0 /E PixelShaderFunction /Fo TextEffect2.ps wpfSolidColor.fx
    注意,wpf支援的directx版本比較老,這裡只能用ps_3_0ps_2_0
    https://learn.microsoft.com/zh-cn/windows/win32/direct3dtools/dx-graphics-tools-fxc-syntax
    之後把TextEffect2.ps複製到專案,把生成方式改為資源
    在新增一個效果類
namespace 你的名稱空間
{
    public class SolidShader:ShaderEffect
    {
        public static readonly DependencyProperty SolidColorProperty = DependencyProperty.Register("SolidColor", typeof(Color), typeof(SolidShader), new UIPropertyMetadata(Color.FromArgb(255, 0, 0, 0), PixelShaderConstantCallback(1)));
        public SolidShader()
        {
            PixelShader pixelShader = new PixelShader();
            pixelShader.UriSource = new Uri("pack://application:,,,/程式集名稱空間;component/路徑/TextEffect2.ps", UriKind.Absolute);
            this.PixelShader = pixelShader;

            this.UpdateShaderValue(SolidColorProperty);
        }
        public Color SolidColor
        {
            get
            {
                return ((Color)(this.GetValue(SolidColorProperty)));
            }
            set
            {
                this.SetValue(SolidColorProperty, value);
            }
        }
    }
}

最後看到畫素著色器正常執行
image

總結

  • 自定義
    著色器型別很多,3ds max中能自定義完整的渲染管線,包括頂點著色器和畫素著色器。但是wpf只支援畫素著色器的自定義。
  • 著色器編譯入口
    使用fxc.exe我們可以自及指定入口函式,但是使用Shazzam Shader Editor 看起來已經在程式碼中固定了入口函式。
    Shazzam Shader Editor使用Shazzam Shader Editor 的好處是編譯和預覽方便
  • 版本
    Direct已經更新到11了,但wpf只支援Direct9

相關文章