Last updated: August 20, 2025
YukkuriMovieMaker.Controls.dll
YukkuriMovieMaker.Plugin.dll
SharpGen.Runtime.dll
SharpGen.Runtime.COM.dll
Vortice.Direct2D1.dll
Vortice.DirectX.dll
Vortice.Mathematics.dll
エフェクトのプロパティなどの情報を設定する。
GitHubより引用
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Controls;
using YukkuriMovieMaker.Exo;
using YukkuriMovieMaker.Player.Video;
using YukkuriMovieMaker.Plugin.Effects;
namespace YMM4SamplePlugin.VideoEffect.SampleD2DVideoEffect
{
/// <summary>
/// 映像エフェクト
/// 映像エフェクトには必ず[VideoEffect]属性を設定してください。
/// </summary>
[VideoEffect("サンプルD2Dエフェクト", ["サンプル"], [])]
internal class SampleD2DVideoEffect : VideoEffectBase
{
/// <summary>
/// エフェクトの名前
/// </summary>
public override string Label => "サンプルD2Dエフェクト";
/// <summary>
/// アイテム編集エリアに表示するエフェクトの設定項目。
/// [Display]と[AnimationSlider]等のアイテム編集コントロール属性の2つを設定する必要があります。
/// [AnimationSlider]以外のアイテム編集コントロール属性の一覧はSamplePropertyEditorsプロジェクトを参照してください。
/// </summary>
[Display(Name = "ぼかし", Description = "ぼかしの強さ")]
[AnimationSlider("F0", "", 0, 100)]
public Animation Blur { get; } = new Animation(10, 0, 100);
/// <summary>
/// ExoFilterを作成する
/// </summary>
/// <param name="keyFrameIndex">キーフレーム番号</param>
/// <param name="exoOutputDescription">exo出力に必要な各種パラメーター</param>
/// <returns></returns>
public override IEnumerable<string> CreateExoVideoFilters(int keyFrameIndex, ExoOutputDescription exoOutputDescription)
{
var fps = exoOutputDescription.VideoInfo.FPS;
return
[
$"_name=ぼかし\r\n" +
$"_disable={(IsEnabled ?1:0)}\r\n" +
$"範囲={Blur.ToExoString(keyFrameIndex, "F0", fps)}\r\n" +
$"縦横比=0.0\r\n" +
$"光の強さ=0\r\n" +
$"サイズ固定=0\r\n",
];
}
/// <summary>
/// 映像エフェクトを作成する
/// </summary>
/// <param name="devices">デバイス</param>
/// <returns>映像エフェクト</returns>
public override IVideoEffectProcessor CreateVideoEffect(IGraphicsDevicesAndContext devices)
{
return new SampleD2DVideoEffectProcessor(devices, this);
}
/// <summary>
/// クラス内のIAnimatableの一覧を取得する
/// </summary>
/// <returns></returns>
protected override IEnumerable<IAnimatable> GetAnimatables() => [Blur];
}
}
return [];
エフェクトの実行処理を定義する。
GitHubより引用
using Vortice.Direct2D1;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Player.Video;
namespace YMM4SamplePlugin.VideoEffect.SampleD2DVideoEffect
{
internal class SampleD2DVideoEffectProcessor : IVideoEffectProcessor
{
readonly SampleD2DVideoEffect item;
readonly Vortice.Direct2D1.Effects.GaussianBlur blurEffect;
public ID2D1Image Output { get; }
bool isFirst = true;
double blur;
public SampleD2DVideoEffectProcessor(IGraphicsDevicesAndContext devices, SampleD2DVideoEffect item)
{
this.item = item;
blurEffect = new Vortice.Direct2D1.Effects.GaussianBlur(devices.DeviceContext);
Output = blurEffect.Output;//EffectからgetしたOutputは必ずDisposeする。Effect側ではDisposeされない。
}
/// <summary>
/// エフェクトに入力する映像を設定する
/// </summary>
/// <param name="input"></param>
public void SetInput(ID2D1Image? input)
{
blurEffect.SetInput(0, input, true);
}
/// <summary>
/// エフェクトに入力する映像をクリアする
/// </summary>
public void ClearInput()
{
blurEffect.SetInput(0, null, true);
}
/// <summary>
/// エフェクトを更新する
/// </summary>
/// <param name="effectDescription">エフェクトの描画に必要な各種設定項目</param>
/// <returns>描画関連の設定項目</returns>
public DrawDescription Update(EffectDescription effectDescription)
{
var frame = effectDescription.ItemPosition.Frame;
var length = effectDescription.ItemDuration.Frame;
var fps = effectDescription.FPS;
var blur = item.Blur.GetValue(frame, length, fps);
if (isFirst || this.blur != blur)
blurEffect.StandardDeviation = (float)blur;
isFirst = false;
this.blur = blur;
return effectDescription.DrawDescription;
}
public void Dispose()
{
blurEffect.SetInput(0, null, true);//Inputは必ずnullに戻す。
Output.Dispose();//EffectからgetしたOutputは必ずDisposeする。Effect側ではDisposeされない。
blurEffect.Dispose();
}
}
}
public ID2D1Image Output { get; }
VideoEffectBase
の継承オブジェクトをクラスのインスタンス内で保持する。エフェクトの出力をOutput
プロパティに代入する。VideoEffectBase
から設定項目の値を読み取り、エフェクトのパラメータを設定する。Output
を開放する。YukkuriMovieMaker.Controls.dll
YukkuriMovieMaker.Plugin.dll
SharpGen.Runtime.dll
SharpGen.Runtime.COM.dll
Vortice.Direct2D1.dll
Vortice.DirectX.dll
Vortice.Mathematics.dll
csproj
の依存関係のアイテムグループに次のコードを挿入する方法もあります:
<PropertyGroup>
<ShaderObjectDirectory>$(IntermediateOutputPath)Shaders\</ShaderObjectDirectory>
</PropertyGroup>
<ItemGroup>
<HLSLFile Include="Shaders\*.hlsl" />
</ItemGroup>
<Target Name="ResolveFxcToolPath" BeforeTargets="CompileHLSLFiles">
<PropertyGroup>
<FxcToolPath Condition="'$(FxcToolPath)' == '' AND '$(WindowsSdkDir)' != '' AND '$(TargetPlatformVersion)' != ''">$([System.IO.Path]::Combine('$(WindowsSdkDir)', 'bin', '$(TargetPlatformVersion)', 'x64', 'fxc.exe'))</FxcToolPath>
<FxcToolPath Condition="'$(FxcToolPath)' == '' OR !Exists('$(FxcToolPath)')">fxc.exe</FxcToolPath>
</PropertyGroup>
<Error Text="シェーダーコンパイラ fxc.exe が見つかりませんでした。Windows SDKが正しくインストールされているか、fxc.exeへのパスが通っているか確認してください。検索パス: $(FxcToolPath)"
Condition="!Exists('$(FxcToolPath)') AND '$(FxcToolPath)' != 'fxc.exe'"/>
<Message Text="Using fxc.exe: $(FxcToolPath)" Importance="high" />
</Target>
<Target Name="CompileHLSLFiles"
BeforeTargets="BeforeCompile"
Inputs="@(HLSLFile)"
Outputs="$(ShaderObjectDirectory)%(HLSLFile.Filename).cso">
<MakeDir Directories="$(ShaderObjectDirectory)" />
<Message Text="Compiling HLSL: %(HLSLFile.FullPath)" Importance="normal"/>
<Exec Command=""$(FxcToolPath)" /T ps_5_0 /E main /Fo "$(ShaderObjectDirectory)%(HLSLFile.Filename).cso" "%(HLSLFile.FullPath)"" />
<ItemGroup>
<EmbeddedResource Include="$(ShaderObjectDirectory)%(HLSLFile.Filename).cso">
<LogicalName>$(RootNamespace).Shaders.%(Filename)%(Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
コンパイラがC:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe
の時、以下のバッチファイルに対象のシェーダーファイルを指定して実行することで、バイナリファイルが生成されます。
@echo off
if "%1"=="" (
echo Usage: CompileShader.bat input_shader.hlsl
pause
exit /b 1
)
set INPUT_SHADER=%1
if not exist "%INPUT_SHADER%" (
echo Input file "%INPUT_SHADER%" not found.
pause
exit /b 1
)
set OUTPUT_SHADER=%~dpn1.cso
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe" /T ps_4_1 /E main /Fo %OUTPUT_SHADER% /O3 %INPUT_SHADER%
if %ERRORLEVEL% NEQ 0 (
echo Shader compilation failed.
) else (
echo Compilation successful: %OUTPUT_SHADER%
)
pause
上記と同様。
GitHubより引用
using Vortice.Direct2D1;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Player.Video;
namespace YMM4SamplePlugin.VideoEffect.SampleHLSLShaderVideoEffect
{
internal class SampleHLSLShaderVideoEffectProcessor : IVideoEffectProcessor
{
readonly SampleHLSLShaderVideoEffect item;
bool isFirst = true;
double value;
readonly SampleHLSLShaderCustomEffect? effect;
readonly ID2D1Image? output;
ID2D1Image? input;
/// <summary>
/// エフェクトの出力画像
/// </summary>
public ID2D1Image Output => output ?? input ?? throw new NullReferenceException();
public SampleHLSLShaderVideoEffectProcessor(IGraphicsDevicesAndContext devices, SampleHLSLShaderVideoEffect item)
{
this.item = item;
effect = new SampleHLSLShaderCustomEffect(devices);
if (!effect.IsEnabled)
{
//GPU性能によってエフェクトの読み込みに失敗することがある
effect.Dispose();
effect = null;
}
else
{
output = effect.Output;//EffectからgetしたOutputは必ずDisposeする必要がある。Effect内部では開放されない。
}
}
/// <summary>
/// エフェクトの入力画像を変更する
/// </summary>
/// <param name="input"></param>
public void SetInput(ID2D1Image? input)
{
this.input = input;
effect?.SetInput(0, input, true);
}
/// <summary>
/// エフェクトの入力画像をクリアする
/// </summary>
public void ClearInput()
{
effect?.SetInput(0, null, true);
}
/// <summary>
/// エフェクトを更新する
/// </summary>
/// <param name="effectDescription">エフェクトの描画に必要な各種情報</param>
/// <returns>描画位置等</returns>
public DrawDescription Update(EffectDescription effectDescription)
{
if (effect is null)
return effectDescription.DrawDescription;
var frame = effectDescription.ItemPosition.Frame;
var length = effectDescription.ItemDuration.Frame;
var fps = effectDescription.FPS;
var value = item.Value.GetValue(frame, length, fps) / 100;
if (isFirst || this.value != value)
effect.Value = (float)value;
isFirst = false;
this.value = value;
return effectDescription.DrawDescription;
}
/// <summary>
/// エフェクトの各種リソースを開放する
/// </summary>
public void Dispose()
{
output?.Dispose();//EffectからgetしたOutputは必ずDisposeする必要がある。Effect内部では開放されない。
effect?.SetInput(0, null, true);//Inputは必ずnullに戻す。
effect?.Dispose();
}
}
}
public ID2D1Image Output { get; }
effect.IsEnabled
で判定し、失敗していたらインスタンスを開放する。また、上で定義したVideoEffectBase
の継承オブジェクトをクラスのインスタンス内で保持する。エフェクトの出力をOutput
プロパティに代入する。VideoEffectBase
から設定項目の値を読み取り、エフェクトのパラメータを設定する。Output
を開放する。GitHubより引用
using System.Runtime.InteropServices;
using Vortice.Direct2D1;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Player.Video;
namespace YMM4SamplePlugin.VideoEffect.SampleHLSLShaderVideoEffect
{
/// <summary>
/// ID2D1Effectとして動作するカスタムエフェクト
/// D2D1CustomShaderEffectBaseを継承する
/// </summary>
internal class SampleHLSLShaderCustomEffect : D2D1CustomShaderEffectBase
{
public float Value
{
set => SetValue((int)EffectImpl.Properties.Value, value);
get => GetFloatValue((int)EffectImpl.Properties.Value);
}
public SampleHLSLShaderCustomEffect(IGraphicsDevicesAndContext devices) : base(Create<EffectImpl>(devices))
{
}
/// <summary>
/// エフェクトの実装
/// [CustomEffect]を設定する必要がある。inputCountは入力画像の数。
/// </summary>
[CustomEffect(1)]
class EffectImpl : D2D1CustomShaderEffectImplBase<EffectImpl>
{
/// <summary>
/// HLSLに渡すバッファ
/// </summary>
ConstantBuffer constantBuffer;
/// <summary>
/// エフェクトのプロパティ
/// [CustomEffectProperty]属性でプロパティの型とIDを設定する必要がある。
/// </summary>
[CustomEffectProperty(PropertyType.Float, (int)Properties.Value)]
public float Value
{
get => constantBuffer.Value;
set
{
constantBuffer.Value = value;
UpdateConstants();
}
}
public EffectImpl() : base(ShaderResourceLoader.GetShaderResource("PixelShader.cso")/*ここでシェーダーのbyte列を渡す*/)
{
}
/// <summary>
/// 設定をシェーダーに渡す
/// </summary>
protected override void UpdateConstants()
{
drawInformation?.SetPixelShaderConstantBuffer(constantBuffer);
}
/// <summary>
/// 入力画像の範囲から出力画像の範囲を計算する
/// 例:
/// 画像に対して10pxの縁取りエフェクトを掛ける場合、outputRectをinputRectsの範囲から10px大きくする
/// 画像に対して10pxのモザイクエフェクトをかける場合、出力範囲は変わらないのでinputRects[0]をそのままoutputRectに設定する
/// </summary>
/// <param name="inputRects">入力画像の範囲。inputの数だけ渡される。最適化のため、入力画像の範囲がそのまま渡されるわけではなく、分割されることもある。</param>
/// <param name="inputOpaqueSubRects">入力画像の不透明な部分の範囲。最適化のため、入力画像の範囲がそのまま渡されるわけではなく、分割されることもある。</param>
/// <param name="outputRect">入力画像をもとに計算した出力画像の範囲。</param>
/// <param name="outputOpaqueSubRect">入力画像を元に計算した出力画像の不透明な部分</param>
public override void MapInputRectsToOutputRect(Vortice.RawRect[] inputRects, Vortice.RawRect[] inputOpaqueSubRects, out Vortice.RawRect outputRect, out Vortice.RawRect outputOpaqueSubRect)
{
base.MapInputRectsToOutputRect(inputRects, inputOpaqueSubRects, out outputRect, out outputOpaqueSubRect);
}
/// <summary>
/// 出力画像を生成するために入力する必要のある入力画像の範囲を計算する
/// 例:
/// 画像に対して10pxの縁取りエフェクトを掛ける場合、縁取りの計算に周囲10pxの画像が必要なのでinputRects[0]をoutputRectから10px大きくしたものに設定する
/// 画像に対して10pxのモザイクエフェクトを掛ける場合、モザイクの計算に周囲10pxの画像が必要なのでinputRects[0]をoutputRectから10px大きくしたものに設定する
/// </summary>
/// <param name="outputRect">出力画像の範囲。最適化のため、出力画像の範囲がそのまま渡されるわけではなく、分割されることもある。</param>
/// <param name="inputRects">出力画像を生成するために入力する必要のある入力画像の範囲。</param>
public override void MapOutputRectToInputRects(Vortice.RawRect outputRect, Vortice.RawRect[] inputRects)
{
base.MapOutputRectToInputRects(outputRect, inputRects);
}
[StructLayout(LayoutKind.Sequential)]
struct ConstantBuffer
{
public float Value;
}
public enum Properties
{
Value = 0
}
}
}
}
D2D1CustomShaderEffectImplBase
を継承して定義・実装する。
[StructLayout(LayoutKind.Sequential)]
UpdateConstants()
ShaderResourceLoader.GetShaderResource()
関数は、アセンブリのリソースから名前でその内容を抽出し、byte[]
に代入する関数。詳しくはGitHubを参照。UpdateConstants()
は、コード例をそのままコピー。MapInputRectsToOutputRect()
では、引数の入力画像のサイズを利用して、出力される画像のサイズを計算し、これらをbase.MapInputRectsToOutputRect()
関数の呼び出しで渡します。MapOutputRectToInputRects()
では、引数の出力画像のサイズを利用して、必要な入力画像のサイズを計算し、これらをbase.MapOutputRectToInputRects()
関数の呼び出しで渡します。Create()
関数を基底クラスのコンストラクタに与えるコンストラクタを例のように定義する。Last updated: August 20, 2025
YukkuriMovieMaker.Controls.dll
YukkuriMovieMaker.Plugin.dll
SharpGen.Runtime.dll
SharpGen.Runtime.COM.dll
Vortice.Direct2D1.dll
Vortice.DirectX.dll
Vortice.Mathematics.dll
エフェクトのプロパティなどの情報を設定する。
GitHubより引用
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Controls;
using YukkuriMovieMaker.Exo;
using YukkuriMovieMaker.Player.Video;
using YukkuriMovieMaker.Plugin.Effects;
namespace YMM4SamplePlugin.VideoEffect.SampleD2DVideoEffect
{
/// <summary>
/// 映像エフェクト
/// 映像エフェクトには必ず[VideoEffect]属性を設定してください。
/// </summary>
[VideoEffect("サンプルD2Dエフェクト", ["サンプル"], [])]
internal class SampleD2DVideoEffect : VideoEffectBase
{
/// <summary>
/// エフェクトの名前
/// </summary>
public override string Label => "サンプルD2Dエフェクト";
/// <summary>
/// アイテム編集エリアに表示するエフェクトの設定項目。
/// [Display]と[AnimationSlider]等のアイテム編集コントロール属性の2つを設定する必要があります。
/// [AnimationSlider]以外のアイテム編集コントロール属性の一覧はSamplePropertyEditorsプロジェクトを参照してください。
/// </summary>
[Display(Name = "ぼかし", Description = "ぼかしの強さ")]
[AnimationSlider("F0", "", 0, 100)]
public Animation Blur { get; } = new Animation(10, 0, 100);
/// <summary>
/// ExoFilterを作成する
/// </summary>
/// <param name="keyFrameIndex">キーフレーム番号</param>
/// <param name="exoOutputDescription">exo出力に必要な各種パラメーター</param>
/// <returns></returns>
public override IEnumerable<string> CreateExoVideoFilters(int keyFrameIndex, ExoOutputDescription exoOutputDescription)
{
var fps = exoOutputDescription.VideoInfo.FPS;
return
[
$"_name=ぼかし\r\n" +
$"_disable={(IsEnabled ?1:0)}\r\n" +
$"範囲={Blur.ToExoString(keyFrameIndex, "F0", fps)}\r\n" +
$"縦横比=0.0\r\n" +
$"光の強さ=0\r\n" +
$"サイズ固定=0\r\n",
];
}
/// <summary>
/// 映像エフェクトを作成する
/// </summary>
/// <param name="devices">デバイス</param>
/// <returns>映像エフェクト</returns>
public override IVideoEffectProcessor CreateVideoEffect(IGraphicsDevicesAndContext devices)
{
return new SampleD2DVideoEffectProcessor(devices, this);
}
/// <summary>
/// クラス内のIAnimatableの一覧を取得する
/// </summary>
/// <returns></returns>
protected override IEnumerable<IAnimatable> GetAnimatables() => [Blur];
}
}
return [];
エフェクトの実行処理を定義する。
GitHubより引用
using Vortice.Direct2D1;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Player.Video;
namespace YMM4SamplePlugin.VideoEffect.SampleD2DVideoEffect
{
internal class SampleD2DVideoEffectProcessor : IVideoEffectProcessor
{
readonly SampleD2DVideoEffect item;
readonly Vortice.Direct2D1.Effects.GaussianBlur blurEffect;
public ID2D1Image Output { get; }
bool isFirst = true;
double blur;
public SampleD2DVideoEffectProcessor(IGraphicsDevicesAndContext devices, SampleD2DVideoEffect item)
{
this.item = item;
blurEffect = new Vortice.Direct2D1.Effects.GaussianBlur(devices.DeviceContext);
Output = blurEffect.Output;//EffectからgetしたOutputは必ずDisposeする。Effect側ではDisposeされない。
}
/// <summary>
/// エフェクトに入力する映像を設定する
/// </summary>
/// <param name="input"></param>
public void SetInput(ID2D1Image? input)
{
blurEffect.SetInput(0, input, true);
}
/// <summary>
/// エフェクトに入力する映像をクリアする
/// </summary>
public void ClearInput()
{
blurEffect.SetInput(0, null, true);
}
/// <summary>
/// エフェクトを更新する
/// </summary>
/// <param name="effectDescription">エフェクトの描画に必要な各種設定項目</param>
/// <returns>描画関連の設定項目</returns>
public DrawDescription Update(EffectDescription effectDescription)
{
var frame = effectDescription.ItemPosition.Frame;
var length = effectDescription.ItemDuration.Frame;
var fps = effectDescription.FPS;
var blur = item.Blur.GetValue(frame, length, fps);
if (isFirst || this.blur != blur)
blurEffect.StandardDeviation = (float)blur;
isFirst = false;
this.blur = blur;
return effectDescription.DrawDescription;
}
public void Dispose()
{
blurEffect.SetInput(0, null, true);//Inputは必ずnullに戻す。
Output.Dispose();//EffectからgetしたOutputは必ずDisposeする。Effect側ではDisposeされない。
blurEffect.Dispose();
}
}
}
public ID2D1Image Output { get; }
VideoEffectBase
の継承オブジェクトをクラスのインスタンス内で保持する。エフェクトの出力をOutput
プロパティに代入する。VideoEffectBase
から設定項目の値を読み取り、エフェクトのパラメータを設定する。Output
を開放する。YukkuriMovieMaker.Controls.dll
YukkuriMovieMaker.Plugin.dll
SharpGen.Runtime.dll
SharpGen.Runtime.COM.dll
Vortice.Direct2D1.dll
Vortice.DirectX.dll
Vortice.Mathematics.dll
csproj
の依存関係のアイテムグループに次のコードを挿入する方法もあります:
<PropertyGroup>
<ShaderObjectDirectory>$(IntermediateOutputPath)Shaders\</ShaderObjectDirectory>
</PropertyGroup>
<ItemGroup>
<HLSLFile Include="Shaders\*.hlsl" />
</ItemGroup>
<Target Name="ResolveFxcToolPath" BeforeTargets="CompileHLSLFiles">
<PropertyGroup>
<FxcToolPath Condition="'$(FxcToolPath)' == '' AND '$(WindowsSdkDir)' != '' AND '$(TargetPlatformVersion)' != ''">$([System.IO.Path]::Combine('$(WindowsSdkDir)', 'bin', '$(TargetPlatformVersion)', 'x64', 'fxc.exe'))</FxcToolPath>
<FxcToolPath Condition="'$(FxcToolPath)' == '' OR !Exists('$(FxcToolPath)')">fxc.exe</FxcToolPath>
</PropertyGroup>
<Error Text="シェーダーコンパイラ fxc.exe が見つかりませんでした。Windows SDKが正しくインストールされているか、fxc.exeへのパスが通っているか確認してください。検索パス: $(FxcToolPath)"
Condition="!Exists('$(FxcToolPath)') AND '$(FxcToolPath)' != 'fxc.exe'"/>
<Message Text="Using fxc.exe: $(FxcToolPath)" Importance="high" />
</Target>
<Target Name="CompileHLSLFiles"
BeforeTargets="BeforeCompile"
Inputs="@(HLSLFile)"
Outputs="$(ShaderObjectDirectory)%(HLSLFile.Filename).cso">
<MakeDir Directories="$(ShaderObjectDirectory)" />
<Message Text="Compiling HLSL: %(HLSLFile.FullPath)" Importance="normal"/>
<Exec Command=""$(FxcToolPath)" /T ps_5_0 /E main /Fo "$(ShaderObjectDirectory)%(HLSLFile.Filename).cso" "%(HLSLFile.FullPath)"" />
<ItemGroup>
<EmbeddedResource Include="$(ShaderObjectDirectory)%(HLSLFile.Filename).cso">
<LogicalName>$(RootNamespace).Shaders.%(Filename)%(Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
コンパイラがC:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe
の時、以下のバッチファイルに対象のシェーダーファイルを指定して実行することで、バイナリファイルが生成されます。
@echo off
if "%1"=="" (
echo Usage: CompileShader.bat input_shader.hlsl
pause
exit /b 1
)
set INPUT_SHADER=%1
if not exist "%INPUT_SHADER%" (
echo Input file "%INPUT_SHADER%" not found.
pause
exit /b 1
)
set OUTPUT_SHADER=%~dpn1.cso
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe" /T ps_4_1 /E main /Fo %OUTPUT_SHADER% /O3 %INPUT_SHADER%
if %ERRORLEVEL% NEQ 0 (
echo Shader compilation failed.
) else (
echo Compilation successful: %OUTPUT_SHADER%
)
pause
上記と同様。
GitHubより引用
using Vortice.Direct2D1;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Player.Video;
namespace YMM4SamplePlugin.VideoEffect.SampleHLSLShaderVideoEffect
{
internal class SampleHLSLShaderVideoEffectProcessor : IVideoEffectProcessor
{
readonly SampleHLSLShaderVideoEffect item;
bool isFirst = true;
double value;
readonly SampleHLSLShaderCustomEffect? effect;
readonly ID2D1Image? output;
ID2D1Image? input;
/// <summary>
/// エフェクトの出力画像
/// </summary>
public ID2D1Image Output => output ?? input ?? throw new NullReferenceException();
public SampleHLSLShaderVideoEffectProcessor(IGraphicsDevicesAndContext devices, SampleHLSLShaderVideoEffect item)
{
this.item = item;
effect = new SampleHLSLShaderCustomEffect(devices);
if (!effect.IsEnabled)
{
//GPU性能によってエフェクトの読み込みに失敗することがある
effect.Dispose();
effect = null;
}
else
{
output = effect.Output;//EffectからgetしたOutputは必ずDisposeする必要がある。Effect内部では開放されない。
}
}
/// <summary>
/// エフェクトの入力画像を変更する
/// </summary>
/// <param name="input"></param>
public void SetInput(ID2D1Image? input)
{
this.input = input;
effect?.SetInput(0, input, true);
}
/// <summary>
/// エフェクトの入力画像をクリアする
/// </summary>
public void ClearInput()
{
effect?.SetInput(0, null, true);
}
/// <summary>
/// エフェクトを更新する
/// </summary>
/// <param name="effectDescription">エフェクトの描画に必要な各種情報</param>
/// <returns>描画位置等</returns>
public DrawDescription Update(EffectDescription effectDescription)
{
if (effect is null)
return effectDescription.DrawDescription;
var frame = effectDescription.ItemPosition.Frame;
var length = effectDescription.ItemDuration.Frame;
var fps = effectDescription.FPS;
var value = item.Value.GetValue(frame, length, fps) / 100;
if (isFirst || this.value != value)
effect.Value = (float)value;
isFirst = false;
this.value = value;
return effectDescription.DrawDescription;
}
/// <summary>
/// エフェクトの各種リソースを開放する
/// </summary>
public void Dispose()
{
output?.Dispose();//EffectからgetしたOutputは必ずDisposeする必要がある。Effect内部では開放されない。
effect?.SetInput(0, null, true);//Inputは必ずnullに戻す。
effect?.Dispose();
}
}
}
public ID2D1Image Output { get; }
effect.IsEnabled
で判定し、失敗していたらインスタンスを開放する。また、上で定義したVideoEffectBase
の継承オブジェクトをクラスのインスタンス内で保持する。エフェクトの出力をOutput
プロパティに代入する。VideoEffectBase
から設定項目の値を読み取り、エフェクトのパラメータを設定する。Output
を開放する。GitHubより引用
using System.Runtime.InteropServices;
using Vortice.Direct2D1;
using YukkuriMovieMaker.Commons;
using YukkuriMovieMaker.Player.Video;
namespace YMM4SamplePlugin.VideoEffect.SampleHLSLShaderVideoEffect
{
/// <summary>
/// ID2D1Effectとして動作するカスタムエフェクト
/// D2D1CustomShaderEffectBaseを継承する
/// </summary>
internal class SampleHLSLShaderCustomEffect : D2D1CustomShaderEffectBase
{
public float Value
{
set => SetValue((int)EffectImpl.Properties.Value, value);
get => GetFloatValue((int)EffectImpl.Properties.Value);
}
public SampleHLSLShaderCustomEffect(IGraphicsDevicesAndContext devices) : base(Create<EffectImpl>(devices))
{
}
/// <summary>
/// エフェクトの実装
/// [CustomEffect]を設定する必要がある。inputCountは入力画像の数。
/// </summary>
[CustomEffect(1)]
class EffectImpl : D2D1CustomShaderEffectImplBase<EffectImpl>
{
/// <summary>
/// HLSLに渡すバッファ
/// </summary>
ConstantBuffer constantBuffer;
/// <summary>
/// エフェクトのプロパティ
/// [CustomEffectProperty]属性でプロパティの型とIDを設定する必要がある。
/// </summary>
[CustomEffectProperty(PropertyType.Float, (int)Properties.Value)]
public float Value
{
get => constantBuffer.Value;
set
{
constantBuffer.Value = value;
UpdateConstants();
}
}
public EffectImpl() : base(ShaderResourceLoader.GetShaderResource("PixelShader.cso")/*ここでシェーダーのbyte列を渡す*/)
{
}
/// <summary>
/// 設定をシェーダーに渡す
/// </summary>
protected override void UpdateConstants()
{
drawInformation?.SetPixelShaderConstantBuffer(constantBuffer);
}
/// <summary>
/// 入力画像の範囲から出力画像の範囲を計算する
/// 例:
/// 画像に対して10pxの縁取りエフェクトを掛ける場合、outputRectをinputRectsの範囲から10px大きくする
/// 画像に対して10pxのモザイクエフェクトをかける場合、出力範囲は変わらないのでinputRects[0]をそのままoutputRectに設定する
/// </summary>
/// <param name="inputRects">入力画像の範囲。inputの数だけ渡される。最適化のため、入力画像の範囲がそのまま渡されるわけではなく、分割されることもある。</param>
/// <param name="inputOpaqueSubRects">入力画像の不透明な部分の範囲。最適化のため、入力画像の範囲がそのまま渡されるわけではなく、分割されることもある。</param>
/// <param name="outputRect">入力画像をもとに計算した出力画像の範囲。</param>
/// <param name="outputOpaqueSubRect">入力画像を元に計算した出力画像の不透明な部分</param>
public override void MapInputRectsToOutputRect(Vortice.RawRect[] inputRects, Vortice.RawRect[] inputOpaqueSubRects, out Vortice.RawRect outputRect, out Vortice.RawRect outputOpaqueSubRect)
{
base.MapInputRectsToOutputRect(inputRects, inputOpaqueSubRects, out outputRect, out outputOpaqueSubRect);
}
/// <summary>
/// 出力画像を生成するために入力する必要のある入力画像の範囲を計算する
/// 例:
/// 画像に対して10pxの縁取りエフェクトを掛ける場合、縁取りの計算に周囲10pxの画像が必要なのでinputRects[0]をoutputRectから10px大きくしたものに設定する
/// 画像に対して10pxのモザイクエフェクトを掛ける場合、モザイクの計算に周囲10pxの画像が必要なのでinputRects[0]をoutputRectから10px大きくしたものに設定する
/// </summary>
/// <param name="outputRect">出力画像の範囲。最適化のため、出力画像の範囲がそのまま渡されるわけではなく、分割されることもある。</param>
/// <param name="inputRects">出力画像を生成するために入力する必要のある入力画像の範囲。</param>
public override void MapOutputRectToInputRects(Vortice.RawRect outputRect, Vortice.RawRect[] inputRects)
{
base.MapOutputRectToInputRects(outputRect, inputRects);
}
[StructLayout(LayoutKind.Sequential)]
struct ConstantBuffer
{
public float Value;
}
public enum Properties
{
Value = 0
}
}
}
}
D2D1CustomShaderEffectImplBase
を継承して定義・実装する。
[StructLayout(LayoutKind.Sequential)]
UpdateConstants()
ShaderResourceLoader.GetShaderResource()
関数は、アセンブリのリソースから名前でその内容を抽出し、byte[]
に代入する関数。詳しくはGitHubを参照。UpdateConstants()
は、コード例をそのままコピー。MapInputRectsToOutputRect()
では、引数の入力画像のサイズを利用して、出力される画像のサイズを計算し、これらをbase.MapInputRectsToOutputRect()
関数の呼び出しで渡します。MapOutputRectToInputRects()
では、引数の出力画像のサイズを利用して、必要な入力画像のサイズを計算し、これらをbase.MapOutputRectToInputRects()
関数の呼び出しで渡します。Create()
関数を基底クラスのコンストラクタに与えるコンストラクタを例のように定義する。