Skip to content

Releases: routersys/YMM4-PerspectiveShadow

v1.0.0

14 May 09:50
89b0e5c

Choose a tag to compare

v1.0.0 - 影(立体投影)for YMM4

YukkuriMovieMaker4 向けの透視投影落ち影エフェクトプラグインの初回リリースです。
仮想点光源と接地面を定義し、透視変換に基づく立体的な落ち影を GPU 上でリアルタイム生成します。
HLSL ピクセルシェーダーによる逆投影サンプリング・距離連動動的ブラー・黄金角スパイラルサンプリング・
距離減衰・シルエットしきい値によるソフトシルエット生成・
全パラメータのキーフレームアニメーション対応・ビューポートコントローラーポイント・
8 言語対応 UI を備えた映像エフェクトプラグインです。


新機能

1. エフェクト定義(PerspectiveShadowEffect)

PerspectiveShadowEffectVideoEffectBase を継承します。

[VideoEffect] 属性は以下のパラメーターで宣言されます。

  • 表示名:Texts.PerspectiveShadowEffectName(ローカライズキー)
  • カテゴリー:VideoEffectCategories.Decoration
  • 検索タグ:"影(立体投影)""透視投影""3D影""落ち影""影"
  • IsAviUtlSupported = false により AviUtl 向け EXO 出力は非対応です。
  • ResourceType = typeof(Texts) でローカライズリソースを指定します。
    Label プロパティは Texts.PerspectiveShadowEffectName を返却します。

公開プロパティは以下のとおりです。

光源グループ(Texts.PerspectiveShadowGroupLight

  • LightXAnimation、デフォルト 0、内部範囲 VerySmallValueVeryLargeValue):
    [AnimationSlider("F1", "px", -1000d, 1000d)] でスライダー操作範囲 −1000〜1000 px として表示されます。
    光源の水平位置を指定します。

  • LightYAnimation、デフォルト -500、内部範囲 VerySmallValueVeryLargeValue):
    [AnimationSlider("F1", "px", -1000d, 1000d)] でスライダー操作範囲 −1000〜1000 px として表示されます。
    光源の垂直位置を指定します。

  • LightHeightAnimation、デフォルト 2000、内部範囲 1VeryLargeValue):
    [AnimationSlider("F1", "px", 50d, 2000d)] でスライダー操作範囲 50〜2000 px として表示されます。
    光源の仮想的な高さを指定します。小さいほど影が長く伸びます。

  • GroundYAnimation、デフォルト 0、内部範囲 VerySmallValueVeryLargeValue):
    [AnimationSlider("F1", "px", -1000d, 1000d)] でスライダー操作範囲 −1000〜1000 px として表示されます。
    影が落ちる接地面の Y 位置を素材下端からのオフセット (px) で指定します。
    影の見え方グループ(Texts.PerspectiveShadowGroupAppearance

  • OpacityAnimation、デフォルト 75、内部範囲 0100):
    [AnimationSlider("F1", "%", 0d, 100d)] でスライダー操作範囲 0〜100 % として表示されます。
    影全体の不透明度を指定します。

  • FalloffAnimation、デフォルト 40、内部範囲 0100):
    [AnimationSlider("F1", "%", 0d, 100d)] でスライダー操作範囲 0〜100 % として表示されます。
    影の先端に向かって薄くなる強さを指定します。

  • BlurRadiusAnimation、デフォルト 2、内部範囲 0256):
    [AnimationSlider("F1", "px", 0d, 32d)] でスライダー操作範囲 0〜32 px として表示されます。
    影のソフトエッジ基本半径を指定します。

  • SpreadAnimation、デフォルト 0.30、内部範囲 01):
    [AnimationSlider("F2", "", 0d, 1d)] でスライダー操作範囲 0〜1 として表示されます。
    距離に応じてブラーエッジが拡散する強さを指定します。

  • AlphaThresholdAnimation、デフォルト 0.05、内部範囲 01):
    [AnimationSlider("F2", "", 0d, 1d)] でスライダー操作範囲 0〜1 として表示されます。
    シルエットとして扱う不透明度のしきい値を指定します。

  • ShadowColorColor、デフォルト Color.FromArgb(255, 0, 0, 0)):
    [ColorPicker] でカラーピッカーとして表示されます。デフォルト値は不透明黒(A=255, R=0, G=0, B=0)です。
    プロパティ変更時は Set メソッドで変更通知が発行されます。
    CreateExoVideoFilters は空の IEnumerable<string> を返却し、EXO 向けフィルター出力は実装されていません。

CreateVideoEffect(IGraphicsDevicesAndContext devices)new PerspectiveShadowEffectProcessor(devices, this) を返却します。

GetAnimatablesLightXLightYLightHeightGroundYOpacityFalloffBlurRadiusSpreadAlphaThreshold の 9 アニメーションプロパティを返却します。
ShadowColorColor 型であり、GetAnimatables に含まれずキーフレームアニメーションの対象外です。


2. エフェクトプロセッサー(PerspectiveShadowEffectProcessor)

PerspectiveShadowEffectProcessorVideoEffectProcessorBase を継承し、
コンストラクターで IGraphicsDevicesAndContext devicesPerspectiveShadowEffect item を受け取ります。

内部フィールドとして effectPerspectiveShadowCustomEffect?)・isFirstbool)・
lightXlightYlightHeightgroundYopacityfalloffblurRadiusspreadalphaThreshold(各 double)・
shadowColorColor)を保持します。

CreateEffect メソッド

CreateEffect(IGraphicsDevicesAndContext devices) は以下の手順でエフェクトを構築します。

  1. new PerspectiveShadowCustomEffect(devices) でシェーダーエフェクトを生成します。
  2. IsEnabledfalse の場合は Dispose() して null を返却します。
  3. 有効と判断した場合は disposer.Collect(effect) で破棄管理に登録します。
  4. effect.Output を取得・disposer.Collect 後、これを戻り値として返却します。
    本エフェクトはシングルパス構成です。トライトーンのような複数エフェクトチェーン(Composite / Blend / CrossFade)は持たず、シェーダー単体で影の生成・合成をすべて完結させます。

setInput / ClearEffectChain メソッド

setInput(ID2D1Image? input)effect?.SetInput(0, input, true) でスロット 0 に入力を設定します。

ClearEffectChaineffect?.SetInput(0, null, true) でスロット 0 の入力を解除します。

Update メソッド

Update(EffectDescription effectDescription) は毎フレーム呼び出されます。

IsPassThroughEffecttrue、または effectnull の場合は effectDescription.DrawDescription をそのまま返却します。

effectDescription から frameItemPosition.Frame)・lengthItemDuration.Frame)・fpsFPS)を取得し、各アニメーション値を評価します。

  • lightXitem.LightX.GetValue(frame, length, fps)

  • lightYitem.LightY.GetValue(frame, length, fps)

  • lightHeightitem.LightHeight.GetValue(frame, length, fps)

  • groundYitem.GroundY.GetValue(frame, length, fps)

  • opacityitem.Opacity.GetValue(frame, length, fps)

  • falloffitem.Falloff.GetValue(frame, length, fps)

  • blurRadiusitem.BlurRadius.GetValue(frame, length, fps)

  • spreaditem.Spread.GetValue(frame, length, fps)

  • alphaThresholditem.AlphaThreshold.GetValue(frame, length, fps)

  • shadowColoritem.ShadowColor を直接参照します。
    isFirsttrue の場合、または前フレームからの差分がある場合に限り、以下の更新を行います。

  • effect.LightX = (float)lightX

  • effect.LightY = (float)lightY

  • effect.LightHeight = (float)lightHeight

  • effect.GroundY = (float)groundY

  • effect.Opacity = (float)opacity / 100f(0〜100 → 0〜1 正規化)

  • effect.Falloff = (float)falloff / 100f(0〜100 → 0〜1 正規化)

  • effect.BlurRadius = (float)blurRadius

  • effect.Spread = (float)spread

  • effect.AlphaThreshold = (float)alphaThreshold

  • effect.ShadowColor = new Vector4(shadowColor.R / 255f, shadowColor.G / 255f, shadowColor.B / 255f, shadowColor.A / 255f)
    更新後は isFirst = false をセットし、全キャッシュフィールドを現在値で上書きします。

ビューポートコントローラーポイント

Update の最後に VideoEffectController を生成し、DrawDescriptionControllers に追加して返却します。
コントローラーポイントは 1 点で、座標は (lightX, lightY, 0f) です。
ドラッグ操作時のデルタは item.LightX.AddToEachValues(arg.Delta.X) および item.LightY.AddToEachValues(arg.Delta.Y) として全キーフレームに一括適用されます。


3. カスタムシェーダーエフェクト(PerspectiveShadowCustomEffect)

PerspectiveShadowCustomEffectD2D1CustomShaderEffectBase を継承します。
コンストラクターは Create<EffectImpl>(devices) を呼び出します。

公開プロパティとして LightXLightYLightHeightGroundYOpacityFalloff
BlurRadiusSpreadShadowColorVector4)・AlphaThreshold の 10 個のプロパティを持ちます。
float 型プロパティは SetValue((int)EffectImpl.Properties.XXX, value) / GetFloatValue(...) で、
Vector4 型の ShadowColorSetValue((int)EffectImpl.Properties.ShadowColor, value) / GetVector4Value(...) で Direct2D プロパティシステムと通信します。

EffectImpl 内部クラス

EffectImplD2D1CustomShaderEffectImplBase<EffectImpl> を継承し、
[CustomEffect(1)] 属性により入力テクスチャ 1 枚のカスタムエフェクトとして宣言されます。

ConstantBuffer 構造体

[StructLayout(LayoutKind.Sequential)] で宣言された ConstantBuffer 構造体は
12 個の float フィールドおよび 1 個の Vector4 フィールドを以下の順序で保持します。

フィールド レジスター 役割
LightX c0.x 光源の水平位置
LightY c0.y 光源の垂直位置
LightHeight c0.z 光源の仮想高さ
GroundY c0.w 解決済み接地線 Y 座標(絶対座標)
Opacity c1.x 影の不透明度(0〜1)
Falloff c1.y 距離減衰強度(0〜1)
BlurRadius c1.z ぼかし基本半径
Spread c1.w 距離連動ブラー拡散率
ShadowColor c2.xyzw 影の色(RGBA、0〜1 正規化)
AlphaThreshold c3.x シルエットしきい値(0〜1)
Pad0 c3.y パディング
Pad1 c3.z パディング
Pad2 c3.w パディング

この配置は HLSL 定数バッファーの packoffset 宣言と厳密に対応します。

Properties 列挙型

Properties 列挙型はパディングを除く 10 フィールドを int 値 0〜9 でマッピングします。

列挙値
LightX 0
LightY 1
LightHeight 2
GroundY 3
Opacity 4
Falloff 5
BlurRadius 6
Spread 7
ShadowColor 8
AlphaThreshold 9

各プロパティは [CustomEffectProperty(PropertyType.Float, (int)Properties.XXX)] で修飾されます。
ShadowColor のみ PropertyType.Vector4 で宣言されます。

入力値のクランプ

C# 側のセッターは以下のクランプ処理を施します。

  • LightHeightMath.Max(value, 1f)(最小 1 px)
  • OpacityMath.Clamp(value, 0f, 1f)
  • FalloffMath.Clamp(value, 0f, 1f)
  • BlurRadiusMath.Max(value, 0f)
  • SpreadMath.Clamp(value, 0f, 1f)
  • AlphaThresholdMath.Clamp(value, 0f, 1f)
    LightXLightYGroundYShadowColor はクランプなしで直接設定されます。

コンストラクター

base(ShaderResourceUri.Get("PerspectiveShadow")) を呼び出します。
これによりコンパイル済みシェーダーオブジェクト(.cso)が WPF リソースとして読み込まれます。

UpdateConstants

drawInformation?.SetPixelShaderConstantBuffer(_cb) を呼び出して
ConstantBuffer 構造体をピクセルシェーダーの定数バッファーレジスター b0 へ転送します。

MapInputRectsToOutputRect(出力矩形計算)

本エフェクトは影の投影先を包含するよう出力矩形を動的に拡張します。処理は以下の手順で行われます。

  1. _resolvedGroundY = inputRect.Bottom + _groundYOffset として接地線の絶対座標を確定し、_cb.GroundY を更新します。
  2. 素材の四隅(左上・右上・左下・右下)それぞれについて ProjectToGround で影の投影先座標を計算し、
    その座標の最小・最大値を累積して出力矩形の候補範囲を求めます。
  3. 各コーナーで shadowDistspread から ComputeDynamicBlur を評価し、最大のブラー量 maxDynBlur を求めます。
  4. blurMargin = ceil(maxDynBlur) + 2 を余白として四辺に加算します。
  5. 出力矩形は上記の拡張範囲を素材矩形から最大 4096 px の範囲に制限して確定します。
    ProjectToGround は以下の射影計算を行います。
t = min(H / max(H - h, ε), 10.0)   ただし h = max(0, G - Q.Y)
Sx = lightX + (Q.X - lightX) × t
Sy = lightY + (G - lightY) × t

t は最大 10.0 にクランプされ、影が無限遠に伸びることを防ぎます。

ComputeDynamicBlur は以下の式でブラー量を算出します。

expansion = min(shadowDist / max(lightHeight, 1) × spread, 3.0)
dynamicBlur = blurRadius × (1 + expansion)

MapOutputRectToInputRects(入力矩形計算)

MapOutputRectToInputRects は出力矩形の境界上の点群を UnprojectFromShadow で逆変換し、
必要な入力矩形を計算します。処理は以下の手順で行われます。

  1. 出力矩形の境界を 4 × 4Subdivisions = 4)のグリッドに分割した境界上の点(内部点を除く辺上の点のみ)を列挙します。
  2. 各点を UnprojectFromShadow で逆変換し、有効な投影元座標 Q を得ます。
  3. Q の位置に ComputeDynamicBlur のブラー余白を加えた範囲で入力矩形を拡張します。
  4. 光源座標が出力矩形内に含まれる場合は、入力矩形全体を素材の境界まで拡張します(光源が領域内にある場合、任意の座標が素材全体を参照しうるため)。
  5. 最終的な入力矩形に ±2 px のマージンを付加して返却します。

4. HLSL ピクセルシェーダー(PerspectiveShadow.hlsl)

シェーダーモデル ps_5_0・エントリーポイント main でコンパイルされます。

リソース宣言

  • `Texture2D InputTex...
Read more