技術ブログ - historia-Inc/WindowTransparency GitHub Wiki
公開予定のブログの内容と同じです。順番の関係上ブログの公開がだいぶ後になるためこちらで公開します。
Windowsにおけるウィンドウ透過処理の基本的な考え方は、DWM (Desktop Window Manager) APIなどを利用してウィンドウの特定領域を透明に描画させることです。以下の記事やリポジトリで解説されている内容が参考になります。
- デスクトップが透過するアプリ・ライブ壁紙アプリを Unity で作る - Qiita
- GitHub - kirurobo/UniWindowController: Makes your Unity window transparent and allows you to drop files
上記の記事を参考に今回は主にWindowsAPIの呼び出し方やレンダリング設定などUE特有の部分を中心に解説していきます。
まずは、WindowsAPIを使用できるようにUnreal Engineプロジェクトをセットアップします。 新規にC++プロジェクトを作成してください。
プロジェクト設定(Edit > Project Settings...)を開き、「Engine」 > 「Rendering」セクションにある「Alpha Output」にチェックを入れます。これでバックバッファにAlphaチャンネルが書き込まれるようになります。
Note: RenderDocなどのグラフィックスデバッガを使用すれば、バックバッファにアルファが書き込まれているか確認できます。アルファチャンネルが書き込まれていてもウィンドウ設定が正しくないと透過されませんが、問題の切り分けに役立ちます。
プロジェクト設定の「Platforms」 > 「Windows」 > 「Targeted RHIs」セクションで、「Default RHI」をDirectX11に変更します。今回の手法ではDirectX12では背景を透過することができません。
Warning: DirectX11を使用するため、LumenなどDirectX12 (SM6) の機能を必要とする一部のレンダリング機能は使用できなくなります。
Config/DefaultEngine.ini
ファイルを開き、[/Script/Engine.RendererSettings]
セクションに以下の行を追加します。
DefaultEngine.ini
r.D3D11.UseAllowTearing=0
この設定はエディターのプロジェクト設定UIからは変更できないため、直接iniファイルを編集する必要があります。
Note: この設定をすることでDXGIを使用したフリップモードを無効化できます。DXGIフリップモードは透明度をサポートしていないため無効化する必要があります。Unityにおける「Use DXGI flip model swapchain for D3D11」設定に該当します。
参考:
- Unreal Engine Console Variables Reference | Unreal Engine 5.5 Documentation | Epic Developer Community
- PlayerSettings-useFlipModelSwapchain - Unity スクリプトリファレンス
プロジェクトのソースフォルダ内にある[プロジェクト名].Build.cs
ファイル(例: MyProject.Build.cs
)に設定を追加します。
PrivateDependencyModuleNames
にEngine
、Slate
、SlateCore
を追加します。 これらはアプリケーションのウィンドウ情報を取得するために必要です。
WindowsAPIを使用するために、PublicAdditionalLibraries.Add("Dwmapi.lib");
をWindowsプラットフォーム限定で追加します。
myProjectName.Build.cs
using UnrealBuildTool;
public class WindowTransparency : ModuleRules // クラス名は実際のプロジェクト/モジュール名に合わせる
{
public WindowTransparency(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine", // GEngine
"Slate", // SWindow
"SlateCore", // SWidget
}
);
// Windowsプラットフォーム固有の設定
if (Target.Platform == UnrealTargetPlatform.Win64)
{
// Dwmapi.lib をリンク
PublicAdditionalLibraries.Add("Dwmapi.lib");
}
}
}
これでソースコード内でWindowsAPIを使用することができるようになります。
Windows APIを使用して、ゲームウィンドウのスタイルを変更し、透過を有効にします。
透過処理を実装するC++クラスを作成します。 今回実装する処理はエンジンのグローバル変数を使用するためどのクラスからでも呼び出せます。(例: UObject
を継承したクラスなど)。
作成したクラスの.cpp
ファイルに必要なヘッダーファイルをインクルードします。
ウィンドウ情報を取得するためにWidgets/SWindow.h
とEngine/Engine.h
が必要です。Windows API関連のヘッダーは#if PLATFORM_WINDOWS
で囲み、UEの型システムとの衝突を避けるためにAllowWindowsPlatformTypes.h
/ HideWindowsPlatformTypes.h
でラップします。
myClass.cpp
#include "Widgets/SWindow.h"
#include "Engine/Engine.h"
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#include <dwmapi.h>
#include <winuser.h> // SetWindowLongPtr などで必要
#include "Windows/HideWindowsPlatformTypes.h"
#pragma comment(lib, "Dwmapi.lib")
#endif
Windows APIでウィンドウを操作するには、対象ウィンドウのハンドル (HWND
) が必要になります。エンジンのグローバル変数「GEngine」からゲームのウィンドウのハンドルを取得できます。
myClass.cpp
if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWindow().IsValid())
{
TSharedPtr<SWindow> GameSWindow = GEngine->GameViewport->GetWindow();
if (GameSWindow.IsValid() && GameSWindow->GetNativeWindow().IsValid())
{
void* Handle = GameSWindow->GetNativeWindow()->GetOSWindowHandle();
HWND hwnd = static_cast<HWND>(Handle);
// ここで hwnd を使用して処理
}
}
後は通常のWindowsAPIと同じ使い方をすることができます。
取得したウィンドウハンドル (hwnd
) に対して、DWM APIまたは標準のWindows API関数を使用して透過設定を適用します。以下の2つの方法があります。
DWM (Desktop Window Manager) APIのDwmExtendFrameIntoClientArea
関数を使用します。この関数は、ウィンドウのフレームをクライアント領域に拡張し、ガラス効果(Aero Glass)を適用します。引数のMARGINS
構造体に-1
を設定すると、クライアント領域全体が透明になります。
参考: DwmExtendFrameIntoClientArea 関数 (dwmapi.h) - Win32 apps
myClass.cpp
// (hwnd取得後)
MARGINS margins = { -1 };
HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &margins);
ウィンドウの拡張ウィンドウスタイルにWS_EX_LAYERED
を追加することでも透過ウィンドウを実現できます。このスタイルを設定した後、SetLayeredWindowAttributes
関数またはUpdateLayeredWindow
関数で透過方法(特定の色を透明にする、アルファ値で半透明にするなど)を指定します。
参考:
myClass.cpp
// (hwnd取得後)
// 既存の拡張スタイルにWS_EX_LAYEREDを追加
LONG_PTR currentExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, currentExStyle | WS_EX_LAYERED);
Note: これらの関数をゲーム開始時や特定のタイミング(例: ボタンクリック時)に実行するようにします。ブループリントから呼び出せるようにUFUNCTIONとして公開する方法については、サンプルコードを参照してください。
C++側のウィンドウ設定が完了したら、Unreal Engine側でレンダリング結果のアルファ値を制御します。これはポストプロセスマテリアルを使用して行います。
まず、C++コードをビルドし、Unreal Editorを起動します。
-
ポストプロセスマテリアルの作成:
- コンテンツブラウザで右クリックし、「マテリアル」を新規作成します。
- 作成したマテリアルを開き、詳細パネルの「Material」セクションで「Material Domain」を「Post Process」に変更します。
- 同じく詳細パネルの「Post Process Material」セクションで「Output Alpha」にチェックを入れます。
-
マテリアルグラフの編集:
- マテリアルグラフで、最終的なアルファ値を「Opacity」入力に接続します。例えば、深度やCustomStencilなどをOpacityに設定します。
下の画像では深度を取得して一定距離以上なら透過するという簡単な例です。
Warning: 透過部分にEmissiveColorが含まれているとOpacityが0でも半透明で描画されてしまうため注意。
-
ポストプロセスボリュームへの適用:
- レベルに配置されている「Post Process Volume」を選択します。(ない場合は新規に配置します。)
- 詳細パネルの「Rendering Features」 > 「Post Process Materials」セクションで、「Array elements」に新しい要素を追加し、「Asset Reference」として作成したポストプロセスマテリアルを指定します。
- ボリュームがシーン全体に影響するように、「Infinite Extent (Unbound)」にチェックを入れるか、影響範囲を調整します。
上記の設定が完了したら、実際に透過されるか確認します。
- C++で実装した透過設定を有効にする関数を実行します。
- ゲームを**「スタンドアローンゲーム (Standalone Game)」**で起動します。PIE (Play In Editor) では、ウィンドウ透過は正しく機能しません。
正しく設定されていれば、ゲームウィンドウの背景が透過され、デスクトップや他のウィンドウが見えるようになります。
動画: 確認結果 2025-05-27-12-54-36.mp4
透過設定をする関数を呼び出すと透過されてデスクトップが見えました。
アンチエイリアシング(AA)を無効化する: 特にTAA (Temporal Anti-Aliasing) やTSR (Temporal Super Resolution) は、境界線のチラツキが目立ちます。プロジェクト設定でアンチエイリアシング方法を「None」や「FXAA」に変更するとある程度改善します。
ビルドはできてエディタ上では問題なく動作するが、プラグインをパッケージ化しようとするとビルドエラーが発生する。
考えられる原因
-
プラットフォーム固有コードの処理不足: Windows API関連のコード (
#include <windows.h>
やAPI呼び出しなど) はWindows専用です。プラグインは他のプラットフォーム (Mac, Linuxなど) 向けにもビルドが試みられるため、これらのコードを#if PLATFORM_WINDOWS
と#endif
で囲む必要があります。ヘッダーのインクルードだけでなく、関連する処理全体を囲ってください。 -
ヘッダーファイルやモジュールの不足:
- ヘッダーファイル: 使用している関数や型に必要なヘッダーファイルがインクルードされていない可能性があります。エラーメッセージをよく読み、どのファイルが見つからないか確認してください。UEの公式ドキュメントやGitHubソースコードで関数名を検索すると、必要なヘッダーパスがわかることがあります。
-
モジュール依存関係: 使用しているクラスや機能が特定のモジュールに属している場合、
.Build.cs
ファイルでそのモジュールへの依存関係 (PublicDependencyModuleNames
またはPrivateDependencyModuleNames
) を追加する必要があります。例えば、SWindow
はSlateCore
やSlate
モジュールに依存します。
MFAAの設定: NVIDIA製のGPUを使用している場合、NVIDIAコントロールパネルの「3D設定の管理」で「マルチフレームサンプリングAA (MFAA)」がオンになっていると、透過が正しく機能しないことがあります。MFAAをオフに設定変更して試してみてください。