プラグインとして新規グローバル シェーダーを作成する

プラグインとして新規グローバル シェーダーを作成する

Unreal Engine プラグイン システムを使うと、エンジン全体の再コンパイルおよび配布をせずに誰でも幅広い新機能を追加することができます。以下の How - To で、Lens Distortion グローバル シェーダー プラグインを再生しして、ブループリントで操作可能なグローバル シェーダーの実装方法を実演します。

クリックしてフルサイズで表示。

Engine\Plugins\Compositing\LensDistortion にある既存の Lens Distortion プラグインを Foo と呼ばれる新規プラグインに再度作成して、このワークフローを実行する方法を実演します。

このクイックスタートでは、グローバル シェーダーをプラグインとして作成および使用する方法を体験します。

1 - コード設定

Unreal Engine 用のプラグインの新規作成を始める前に、Visual Studio がインストールされていることを確認してください。この操作ガイドでは、プラグインのコードをコンパイルして実行可能にする必要があるので、Visual Studio が必要になります。インストール方法が分からない場合は Unreal Engine 用に Visual Studio を設定する を参照してください。

  1. まず、新しい Games プロジェクトを作成し、Blank (空の) テンプレートを選択します。 [Maximum Quality][No Starter Content] を必ず有効にしてください。

  2. プロジェクトが作成されたら Visual Studio を開きます。ShadersInPlugins プロジェクトを右クリックして [Build] オプションを選択し、プロジェクトをコンパイルします。

    HT_ShadersInPlugins_01.png

  3. プロジェクトのコンパイルが完了したら、Visual Studio の F5 キーを押して Unreal Engine エディタで ShadersInPlugins プロジェクトを起動します。

  4. Unreal Engine エディタのロード処理が完了したら、[Edit] > [Plugins] から Plugins マネージャを開いて [Plugin] ウィンドウの右下にある [New Plugin] オプションをクリックして新規プラグイン作成ウインドウを開きます。

    クリックしてフルサイズで表示。

  5. [New Plugin] ウィンドウから [Blank] プラグインを選択し、[Name] 欄に 「Foo」 と入力し、すべての設定をデフォルトにします。すべての設定をデフォルトにしたら、[Create Plugin (プラグインを作成)] ボタンを押して、最初にプラグインが必要とするすべてのコンテンツを作成します。

    クリックしてフルサイズで表示。

  6. これが完了したら、Unreal Engine と Visual Studio を閉じます。次にプロジェクト フォルダ内に作成された [Plugins] > 「Foo plugin」 フォルダを開きます。

  7. 「Foo plugin」フォルダ内に 「Shaders」 という名前の新規フォルダを追加し、その中に 「Private」 という名前の新規フォルダを作成します。

    クリックしてフルサイズで表示。

  8. 「Private」フォルダの中にテキスト ファイルを新規作成し 「MyShader.USF」 と名前を付けたら、以下の HLSL コードをこのファイルにコピーペーストして、完了したらファイルを閉じます。

    ファイルの拡張子を .USF に必ず変更してください。そうしないと、これがシェーダーファイルであることを Unreal Engine が認識しません。

        // Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.
    
        /*=============================================================================
            LensDistortionUVGeneration.usf:Generate lens distortion and undistortion
            UV displacement map into a render target.
    
            The pixel shader directly compute the distort viewport UV to undistort
            viewport UV displacement using Sv_Position and the reference equations and
            store them into the red and green channels.
    
            However to avoid resolving with a ferrari method, or doing a newton method
            on the GPU to compute the undistort viewport UV to distort viewport UV
            displacement, this couple of shaders works as follow:The vertex shader
            undistort the grid's vertices, and pass down to the pixel shader the viewport
            UV of where they should have been on screen without undistortion.The pixel
            shader can then generate the undistort viewport UV to distort viewport UV
            displacement by just subtracting the pixel's viewport UV.
        =============================================================================*/
    
        #include "/Engine/Public/Platform.ush"
    
        // Size of the pixels in the viewport UV coordinates.
        float2 PixelUVSize;
    
        // K1, K2, K3
        float3 RadialDistortionCoefs;
    
        // P1, P2
        float2 TangentialDistortionCoefs;
    
        // Camera matrix of the undistorted viewport.
        float4 UndistortedCameraMatrix;
    
        // Camera matrix of the distorted viewport.
        float4 DistortedCameraMatrix;
    
        // Output multiply and add to the render target.
        float2 OutputMultiplyAndAdd;
    
        // Undistort a view position at V.z=1.
        float2 UndistortNormalizedViewPosition(float2 V)
        {
            float2 V2 = V * V;
            float R2 = V2.x + V2.y;
    
            // Radial distortion (extra parenthesis to match MF_Undistortion.uasset).
            float2 UndistortedV = V * (1.0 + R2 * (RadialDistortionCoefs.x + R2 * (RadialDistortionCoefs.y + R2 * RadialDistortionCoefs.z)));
    
            // Tangential distortion.
            UndistortedV.x += TangentialDistortionCoefs.y * (R2 + 2 * V2.x) + 2 * TangentialDistortionCoefs.x * V.x * V.y;
            UndistortedV.y += TangentialDistortionCoefs.x * (R2 + 2 * V2.y) + 2 * TangentialDistortionCoefs.y * V.x * V.y;
    
            return UndistortedV;
        }
    
        // Returns the undistorted viewport UV of the distorted's viewport UV.
        //
        // Notes:
        //        UVs are bottom left originated.
        float2 UndistortViewportUV(float2 ViewportUV)
        {
            // Distorted viewport UV -> Distorted view position (z=1)
            float2 DistortedViewPosition = (ViewportUV - DistortedCameraMatrix.zw) / DistortedCameraMatrix.xy;
    
            // Compute undistorted view position (z=1)
            float2 UndistortedViewPosition = UndistortNormalizedViewPosition(DistortedViewPosition);
    
            // Undistorted view position (z=1) -> Undistorted viewport UV.
            return UndistortedCameraMatrix.xy * UndistortedViewPosition + UndistortedCameraMatrix.zw;
        }
    
        // Flip UV's y component.
        float2 FlipUV(float2 UV)
        {
            return float2(UV.x, 1 - UV.y);
        }
    
        void MainVS(
            in uint GlobalVertexId : SV_VertexID,
            out float2 OutVertexDistortedViewportUV :TEXCOORD0,
            out float4 OutPosition : SV_POSITION
            )
        {
            // Compute the cell index.
            uint GridCellIndex = GlobalVertexId / 6;
    
            // Compute row and column id of the cell within the grid.
            uint GridColumnId = GridCellIndex / GRID_SUBDIVISION_Y;
            uint GridRowId = GridCellIndex - GridColumnId * GRID_SUBDIVISION_Y;
    
            // Compute the vertex id within a 2 triangles grid cell.
            uint VertexId = GlobalVertexId - GridCellIndex * 6;
    
            // Compute the bottom left originated UV coordinate of the triangle's vertex within the cell.
            float2 CellVertexUV = float2(0x1 & ((VertexId + 1) / 3), VertexId & 0x1);
    
            // Compute the top left originated UV of the vertex within the grid.
            float2 GridInvSize = 1.f / float2(GRID_SUBDIVISION_X, GRID_SUBDIVISION_Y);
            float2 GridVertexUV = FlipUV(
                GridInvSize * (CellVertexUV + float2(GridColumnId, GridRowId)));
    
            // The standard doesn't have half pixel shift.
            GridVertexUV -= PixelUVSize * 0.5;
    
            // Output vertex position.
            OutPosition = float4(FlipUV(
                UndistortViewportUV(GridVertexUV) + PixelUVSize * 0.5) * 2 - 1, 0, 1);
    
            // Output top left originated UV of the vertex.
            OutVertexDistortedViewportUV = GridVertexUV;
        }
    
        void MainPS(
            in noperspective float2 VertexDistortedViewportUV : TEXCOORD0,
            in float4 SvPosition :SV_POSITION,
            out float4 OutColor :SV_Target0
            )
        {
            // Compute the pixel's top left originated UV.
            float2 ViewportUV = SvPosition.xy * PixelUVSize;
    
            // The standard doesn't have half pixel shift.
            ViewportUV -= PixelUVSize * 0.5;
    
            float2 DistortUVtoUndistortUV = (UndistortViewportUV((ViewportUV))) - ViewportUV;
            float2 UndistortUVtoDistortUV = VertexDistortedViewportUV - ViewportUV;
    
            // Output displacement channels.
            OutColor = OutputMultiplyAndAdd.y + OutputMultiplyAndAdd.x * float4(
                DistortUVtoUndistortUV, UndistortUVtoDistortUV);
        }
  9. 次に「Foo.uplugin」ファイルの中にあるテキスト エディタの中を開き、その中にある情報を以下のテキストで置き換えて、最後にファイルを保存します。

        {
            "FileVersion" :3,
            "Version" :1,
            "VersionName" :"1.0",
            "FriendlyName" :"Foo",
            "Description" :"Plugin to play around with shaders.",
            "Category" :"Sandbox",
            "CreatedBy" :"Epic Games, Inc.",
            "CreatedByURL" : "http://epicgames.com",
            "DocsURL" : "",
            "MarketplaceURL" : "",
            "SupportURL" : "",
            "EnabledByDefault" : false,
            "CanContainContent" : true,
            "IsBetaVersion" : false,
            "Installed" : false,
            "Modules" :
            [
                {
                    "Name" :"Foo",
                    "Type" :"Developer",
                    "LoadingPhase" :"PostConfigInit"
                }
            ]
        }
  10. 次に Plugins\Foo\Source\Foo を開き 「Classes」 という名前の新規フォルダを作成し、「LensDistortionAPI.h」「LensDistortionBlueprintLibrary.h」 ファイルを Engine\Plugins\Compositing\LensDistortion から新規作成したこのフォルダへコピーします。

    コピーするファイルは Engine\Plugins\Compositing\LensDistortion にあります。

    • Classes - Create New Folder

      • Copy - LensDistortionAPI.h

      • Copy - LensDistortionBlueprintLibrary.h

      HT_ShadersInPlugins_05.png

  11. Private」フォルダを開き「LensDistortionBlueprintLibrary.cpp」と「LensDistortionRendering.cpp」ファイルの両方をこの「Private」フォルダにコピーします。

    • Private - Existing Folder

      • Copy - LensDistortionBlueprintLibrary.cpp

      • Copy - LensDistortionRendering.cpp

      HT_ShadersInPlugins_06.png

  12. Unreal Engine と Visual Studio を両方とも閉じて、今度は「projects .U」ファイルを探します。見つかったら、右クリックして [Generate Visual Studio project files] オプションを選択します。

    HT_ShadersInPlugins_07.png

  13. Visual Studio ソリューションを再度開き、[Foo] > [Classes] から「LensDistortionAPI.h」ファイルを開きます。このファイル内で FLensDistortionCameraModelFFooCameraModel に置き換えます。

    このファイルの FLensDistortionCameraModel を FFooCameraModel に 4 回置き換えなければなりません。

        // Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.
    
        #pragma once
    
        #include "CoreMinimal.h"
        #include "LensDistortionAPI.generated.h"
    
        /** Mathematic camera model for lens distortion/undistortion.
        *
        * Camera matrix =
        *  | F.X  0  C.x |
        *  |  0  F.Y C.Y |
        *  |  0   0   1  |
        */
        USTRUCT(BlueprintType)
        struct FFooCameraModel
        {
            GENERATED_USTRUCT_BODY()
            FFooCameraModel()
            {
                K1 = K2 = K3 = P1 = P2 = 0.f;
                F = FVector2D(1.f, 1.f);
                C = FVector2D(0.5f, 0.5f);
            }
    
            /** Radial parameter #1. */
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float K1;
    
            /** Radial parameter #2. */
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float K2;
    
            /** Radial parameter #3. */
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float K3;
    
            /** Tangential parameter #1. */
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float P1;
    
            /** Tangential parameter #2. */
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float P2;
    
            /** Camera matrix's Fx and Fy. */
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            FVector2D F;
    
            /** Camera matrix's Cx and Cy. */
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            FVector2D C;
    
            /** Undistorts 3d vector (x, y, z=1.f) in the view space and returns (x', y', z'=1.f). */
            FVector2D UndistortNormalizedViewPosition(FVector2D V) const;
    
            /** Returns the overscan factor required for the undistort rendering to avoid unrendered distorted pixels. */
            float GetUndistortOverscanFactor(
                float DistortedHorizontalFOV,
                float DistortedAspectRatio) const;
    
            /** Draws UV displacement map within the output render target.
            * - Red & green channels hold the distortion displacement;
            * - Blue & alpha channels hold the undistortion displacement.
            * @param World Current world to get the rendering settings from (such as feature level).
            * @param DistortedHorizontalFOV The desired horizontal FOV in the distorted render.
            * @param DistortedAspectRatio The desired aspect ratio of the distorted render.
            * @param UndistortOverscanFactor The factor of the overscan for the undistorted render.
            * @param OutputRenderTarget The render target to draw to.Don't necessarily need to have same resolution or aspect ratio as distorted render.
            * @param OutputMultiply The multiplication factor applied on the displacement.
            * @param OutputAdd Value added to the multiplied displacement before storing the output render target.
            */
            void DrawUVDisplacementToRenderTarget(
                class UWorld* World,
                float DistortedHorizontalFOV,
                float DistortedAspectRatio,
                float UndistortOverscanFactor,
                class UTextureRenderTarget2D* OutputRenderTarget,
                float OutputMultiply,
                float OutputAdd) const;
    
            /** Compare two lens distortion models and return whether they are equal. */
            bool operator == (const FFooCameraModel& Other) const
            {
                return (
                    K1 == Other.K1 &&
                    K2 == Other.K2 &&
                    K3 == Other.K3 &&
                    P1 == Other.P1 &&
                    P2 == Other.P2 &&
                    F == Other.F &&
                    C == Other.C);
            }
    
            /** Compare two lens distortion models and return whether they are different. */
            bool operator != (const FFooCameraModel& Other) const
            {
                return !(*this == Other);
            }
        };
  14. 次に「LensDistortionBlueprintLibrary.h」ファイルを開きます。このファイルは、ブループリントでのこのノードの見え方を調節するので、FLensDistortionCameraModelFFooCameraModel に置き換えるだけでなく、Category = "Lens Distortion" Category = "Foo | Lens Distortion" に変更する必要もあります。

    このファイルの中で、FLensDistortionCameraModel を FFooCameraModel に 6 回置き換えな変えればなりません。

        // Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.
    
        #pragma once
    
        #include "CoreMinimal.h"
        #include "UObject/ObjectMacros.h"
        #include "Classes/Kismet/BlueprintFunctionLibrary.h"
        #include "LensDistortionAPI.h"
        #include "LensDistortionBlueprintLibrary.generated.h"
    
        UCLASS(MinimalAPI)
        class ULensDistortionBlueprintLibrary : public UBlueprintFunctionLibrary
        {
            GENERATED_UCLASS_BODY()
    
            /** Returns the overscan factor required for the undistort rendering to avoid unrendered distorted pixels. */
            UFUNCTION(BlueprintPure, Category = "Foo | Lens Distortion")
            static void GetUndistortOverscanFactor(
                const FFooCameraModel& CameraModel,
                float DistortedHorizontalFOV,
                float DistortedAspectRatio,
                float& UndistortOverscanFactor);
    
            /** Draws UV displacement map within the output render target.
            * - Red & green channels hold the distortion displacement;
            * - Blue & alpha channels hold the undistortion displacement.
            * @param DistortedHorizontalFOV The desired horizontal FOV in the distorted render.
            * @param DistortedAspectRatio The desired aspect ratio of the distorted render.
            * @param UndistortOverscanFactor The factor of the overscan for the undistorted render.
            * @param OutputRenderTarget The render target to draw to.Don't necessarily need to have same resolution or aspect ratio as distorted render.
            * @param OutputMultiply The multiplication factor applied on the displacement.
            * @param OutputAdd Value added to the multiplied displacement before storing into the output render target.
            */
            UFUNCTION(BlueprintCallable, Category = "Foo | Lens Distortion", meta = (WorldContext = "WorldContextObject"))
            static void DrawUVDisplacementToRenderTarget(
                const UObject* WorldContextObject,
                const FFooCameraModel& CameraModel,
                float DistortedHorizontalFOV,
                float DistortedAspectRatio,
                float UndistortOverscanFactor,
                class UTextureRenderTarget2D* OutputRenderTarget,
                float OutputMultiply = 0.5,
                float OutputAdd = 0.5
                );
    
            /* Returns true if A is equal to B (A == B) */
            UFUNCTION(BlueprintPure, meta=(DisplayName = "Equal (LensDistortionCameraModel)", CompactNodeTitle = "==", Keywords = "== equal"), Category = "Foo | Lens Distortion")
            static bool EqualEqual_CompareLensDistortionModels(
                const FFooCameraModel& A,
                const FFooCameraModel& B)
            {
                return A == B;
            }
    
            /* Returns true if A is not equal to B (A != B) */
            UFUNCTION(BlueprintPure, meta = (DisplayName = "NotEqual (LensDistortionCameraModel)", CompactNodeTitle = "!=", Keywords = "!= not equal"), Category = "Foo | Lens Distortion")
            static bool NotEqual_CompareLensDistortionModels(
                const FFooCameraModel& A,
                const FFooCameraModel& B)
            {
                return A != B;
            }
        };
  15. 次に「Private」フォルダの「LensDistortionBlueprintLibrary.cpp」ファイルを開いて以下を置き換えます。

    • FLensDistortionCameraModelFFooCameraModel に置き換える

    • ULensDistortionBlueprintLibraryUFooBlueprintLibrary に置き換える

    FLensDistortionCameraModel を FFooCameraModel に 2 回置き換えて ULensDistortionBlueprintLibrary を UFooBlueprintLibrary に 4 回置き換えます。

        // Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.
    
        #include "LensDistortionBlueprintLibrary.h"
    
        ULensDistortionBlueprintLibrary::ULensDistortionBlueprintLibrary(const FObjectInitializer& ObjectInitializer)
            :Super(ObjectInitializer)
        { }
    
        // static
        void ULensDistortionBlueprintLibrary::GetUndistortOverscanFactor(
            const FFooCameraModel& CameraModel,
            float DistortedHorizontalFOV,
            float DistortedAspectRatio,
            float& UndistortOverscanFactor)
        {
            UndistortOverscanFactor = CameraModel.GetUndistortOverscanFactor(DistortedHorizontalFOV, DistortedAspectRatio);
        }
    
        // static
        void ULensDistortionBlueprintLibrary::DrawUVDisplacementToRenderTarget(
            const UObject* WorldContextObject,
            const FFooCameraModel& CameraModel,
            float DistortedHorizontalFOV,
            float DistortedAspectRatio,
            float UndistortOverscanFactor,
            class UTextureRenderTarget2D* OutputRenderTarget,
            float OutputMultiply,
            float OutputAdd)
        {
            CameraModel.DrawUVDisplacementToRenderTarget(
                WorldContextObject->GetWorld(),
                DistortedHorizontalFOV, DistortedAspectRatio,
                UndistortOverscanFactor, OutputRenderTarget,
                OutputMultiply, OutputAdd);
        }
  16. 次に「Private」フォルダの「LensDistortionRendering.cpp」ファイルを開いて FLensDistortionCameraModelFFooCameraModel に置き換えます。

    このファイルの中で、FLensDistortionCameraModel を FFooCameraModel に 6 回置き換えな変えればなりません。

        // Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.
    
        #include "LensDistortionAPI.h"
        #include "Classes/Engine/TextureRenderTarget2D.h"
        #include "Classes/Engine/World.h"
        #include "Public/GlobalShader.h"
        #include "Public/PipelineStateCache.h"
        #include "Public/RHIStaticStates.h"
        #include "Public/SceneUtils.h"
        #include "Public/SceneInterface.h"
        #include "Public/ShaderParameterUtils.h"
    
        static const uint32 kGridSubdivisionX = 32;
        static const uint32 kGridSubdivisionY = 16;
    
        /**
        * Internal intermediary structure derived from FFooCameraModel by the game thread
        * to hand to the render thread.
        */
        struct FCompiledCameraModel
        {
            /** Orignal camera model that has generated this compiled model. */
            FFooCameraModel OriginalCameraModel;
    
            /** Camera matrices of the lens distortion for the undistorted and distorted render.
            *  XY holds the scales factors, ZW holds the translates.
            */
            FVector4 DistortedCameraMatrix;
            FVector4 UndistortedCameraMatrix;
    
            /** Output multiply and add of the channel to the render target. */
            FVector2D OutputMultiplyAndAdd;
        };
    
        /** Undistorts top left originated viewport UV into the view space (x', y', z'=1.f) */
        static FVector2D LensUndistortViewportUVIntoViewSpace(
            const FFooCameraModel& CameraModel,
            float TanHalfDistortedHorizontalFOV, float DistortedAspectRatio,
            FVector2D DistortedViewportUV)
        {
            FVector2D AspectRatioAwareF = CameraModel.F * FVector2D(1, -DistortedAspectRatio);
            return CameraModel.UndistortNormalizedViewPosition((DistortedViewportUV - CameraModel.C) / AspectRatioAwareF);
        }
    
        class FLensDistortionUVGenerationShader : public FGlobalShader
        {
        public:
            static bool ShouldCache(EShaderPlatform Platform)
            {
                return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4);
            }
    
            static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
            {
                FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
                OutEnvironment.SetDefine(TEXT("GRID_SUBDIVISION_X"), kGridSubdivisionX);
                OutEnvironment.SetDefine(TEXT("GRID_SUBDIVISION_Y"), kGridSubdivisionY);
            }
    
            FLensDistortionUVGenerationShader() {}
    
            FLensDistortionUVGenerationShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                :FGlobalShader(Initializer)
            {
                PixelUVSize.Bind(Initializer.ParameterMap, TEXT("PixelUVSize"));
                RadialDistortionCoefs.Bind(Initializer.ParameterMap, TEXT("RadialDistortionCoefs"));
                TangentialDistortionCoefs.Bind(Initializer.ParameterMap, TEXT("TangentialDistortionCoefs"));
                DistortedCameraMatrix.Bind(Initializer.ParameterMap, TEXT("DistortedCameraMatrix"));
                UndistortedCameraMatrix.Bind(Initializer.ParameterMap, TEXT("UndistortedCameraMatrix"));
                OutputMultiplyAndAdd.Bind(Initializer.ParameterMap, TEXT("OutputMultiplyAndAdd"));
            }
    
            template<typename TShaderRHIParamRef>
            void SetParameters(
                FRHICommandListImmediate& RHICmdList,
                const TShaderRHIParamRef ShaderRHI,
                const FCompiledCameraModel& CompiledCameraModel,
                const FIntPoint& DisplacementMapResolution)
            {
                FVector2D PixelUVSizeValue(
                    1.f / float(DisplacementMapResolution.X), 1.f / float(DisplacementMapResolution.Y));
                FVector RadialDistortionCoefsValue(
                    CompiledCameraModel.OriginalCameraModel.K1,
                    CompiledCameraModel.OriginalCameraModel.K2,
                    CompiledCameraModel.OriginalCameraModel.K3);
                FVector2D TangentialDistortionCoefsValue(
                    CompiledCameraModel.OriginalCameraModel.P1,
                    CompiledCameraModel.OriginalCameraModel.P2);
    
                SetShaderValue(RHICmdList, ShaderRHI, PixelUVSize, PixelUVSizeValue);
                SetShaderValue(RHICmdList, ShaderRHI, DistortedCameraMatrix, CompiledCameraModel.DistortedCameraMatrix);
                SetShaderValue(RHICmdList, ShaderRHI, UndistortedCameraMatrix, CompiledCameraModel.UndistortedCameraMatrix);
                SetShaderValue(RHICmdList, ShaderRHI, RadialDistortionCoefs, RadialDistortionCoefsValue);
                SetShaderValue(RHICmdList, ShaderRHI, TangentialDistortionCoefs, TangentialDistortionCoefsValue);
                SetShaderValue(RHICmdList, ShaderRHI, OutputMultiplyAndAdd, CompiledCameraModel.OutputMultiplyAndAdd);
            }
    
            virtual bool Serialize(FArchive& Ar) override
            {
                bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
                Ar << PixelUVSize << RadialDistortionCoefs << TangentialDistortionCoefs << DistortedCameraMatrix << UndistortedCameraMatrix << OutputMultiplyAndAdd;
                return bShaderHasOutdatedParameters;
            }
    
        private:
            FShaderParameter PixelUVSize;
            FShaderParameter RadialDistortionCoefs;
            FShaderParameter TangentialDistortionCoefs;
            FShaderParameter DistortedCameraMatrix;
            FShaderParameter UndistortedCameraMatrix;
            FShaderParameter OutputMultiplyAndAdd;
    
        };
    
        class FLensDistortionUVGenerationVS : public FLensDistortionUVGenerationShader
        {
            DECLARE_SHADER_TYPE(FLensDistortionUVGenerationVS, Global);
    
        public:
    
            /** Default constructor. */
            FLensDistortionUVGenerationVS() {}
    
            /** Initialization constructor. */
            FLensDistortionUVGenerationVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                :FLensDistortionUVGenerationShader(Initializer)
            {
            }
        };
    
        class FLensDistortionUVGenerationPS : public FLensDistortionUVGenerationShader
        {
            DECLARE_SHADER_TYPE(FLensDistortionUVGenerationPS, Global);
    
        public:
    
            /** Default constructor. */
            FLensDistortionUVGenerationPS() {}
    
            /** Initialization constructor. */
            FLensDistortionUVGenerationPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                :FLensDistortionUVGenerationShader(Initializer)
            { }
        };
    
        IMPLEMENT_SHADER_TYPE(, FLensDistortionUVGenerationVS, TEXT("/Plugin/Foo/Private/MyShader.usf"), TEXT("MainVS"), SF_Vertex)
        IMPLEMENT_SHADER_TYPE(, FLensDistortionUVGenerationPS, TEXT("/Plugin/Foo/Private/MyShader.usf"), TEXT("MainPS"), SF_Pixel)
    
        static void DrawUVDisplacementToRenderTarget_RenderThread(
            FRHICommandListImmediate& RHICmdList,
            const FCompiledCameraModel& CompiledCameraModel,
            const FName& TextureRenderTargetName,
            FTextureRenderTargetResource* OutTextureRenderTargetResource,
            ERHIFeatureLevel::Type FeatureLevel)
        {
            check(IsInRenderingThread());
    
        #if WANTS_DRAW_MESH_EVENTS
            FString EventName;
            TextureRenderTargetName.ToString(EventName);
            SCOPED_DRAW_EVENTF(RHICmdList, SceneCapture, TEXT("LensDistortionDisplacementGeneration %s"), *EventName);
        #else
            SCOPED_DRAW_EVENT(RHICmdList, DrawUVDisplacementToRenderTarget_RenderThread);
        #endif
    
            // Set render target.
            SetRenderTarget(
                RHICmdList,
                OutTextureRenderTargetResource->GetRenderTargetTexture(),
                FTextureRHIRef(),
                ESimpleRenderTargetMode::EUninitializedColorAndDepth,
                FExclusiveDepthStencil::DepthNop_StencilNop);
    
            FIntPoint DisplacementMapResolution(OutTextureRenderTargetResource->GetSizeX(), OutTextureRenderTargetResource->GetSizeY());
    
            // Update viewport.
            RHICmdList.SetViewport(
                0, 0, 0.f,
                DisplacementMapResolution.X, DisplacementMapResolution.Y, 1.f);
    
            // Get shaders.
            TShaderMap<FGlobalShaderType>* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
            TShaderMapRef< FLensDistortionUVGenerationVS > VertexShader(GlobalShaderMap);
            TShaderMapRef< FLensDistortionUVGenerationPS > PixelShader(GlobalShaderMap);
    
            // Set the graphic pipeline state.
            FGraphicsPipelineStateInitializer GraphicsPSOInit;
            RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
            GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
            GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
            GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
            GraphicsPSOInit.PrimitiveType = PT_TriangleList;
            GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
            GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
            GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
            SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
    
            // Update viewport.
            RHICmdList.SetViewport(
                0, 0, 0.f,
                OutTextureRenderTargetResource->GetSizeX(), OutTextureRenderTargetResource->GetSizeY(), 1.f);
    
            // Update shader uniform parameters.
            VertexShader->SetParameters(RHICmdList, VertexShader->GetVertexShader(), CompiledCameraModel, DisplacementMapResolution);
            PixelShader->SetParameters(RHICmdList, PixelShader->GetPixelShader(), CompiledCameraModel, DisplacementMapResolution);
    
            // Draw grid.
            uint32 PrimitiveCount = kGridSubdivisionX * kGridSubdivisionY * 2;
            RHICmdList.DrawPrimitive(PT_TriangleList, 0, PrimitiveCount, 1);
    
            // Resolve render target.
            RHICmdList.CopyToResolveTarget(
                OutTextureRenderTargetResource->GetRenderTargetTexture(),
                OutTextureRenderTargetResource->TextureRHI,
                false, FResolveParams());
        }
    
        FVector2D FFooCameraModel::UndistortNormalizedViewPosition(FVector2D EngineV) const
        {
            // Engine view space -> standard view space.
            FVector2D V = FVector2D(1, -1) * EngineV;
    
            FVector2D V2 = V * V;
            float R2 = V2.X + V2.Y;
    
            // Radial distortion (extra parenthesis to match MF_Undistortion.uasset).
            FVector2D UndistortedV = V * (1.0 + (R2 * K1 + (R2 * R2) * K2 + (R2 * R2 * R2) * K3));
    
            // Tangential distortion.
            UndistortedV.X += P2 * (R2 + 2 * V2.X) + 2 * P1 * V.X * V.Y;
            UndistortedV.Y += P1 * (R2 + 2 * V2.Y) + 2 * P2 * V.X * V.Y;
    
            // Returns engine V.
            return UndistortedV * FVector2D(1, -1);
        }
    
        /** Compiles the camera model. */
        float FFooCameraModel::GetUndistortOverscanFactor(
            float DistortedHorizontalFOV, float DistortedAspectRatio) const
        {
            // If the lens distortion model is identity, then early return 1.
            if (*this == FFooCameraModel())
            {
                return 1.0f;
            }
    
            float TanHalfDistortedHorizontalFOV = FMath::Tan(DistortedHorizontalFOV * 0.5f);
    
            // Get the position in the view space at z'=1 of different key point in the distorted Viewport UV coordinate system.
            // This very approximative to know the required overscan scale factor of the undistorted viewport, but works really well in practice.
            //
            //  Undistorted UV position in the view space:
            //                 ^ View space's Y
            //                 |
            //        0        1        2
            //
            //        7        0        3 --> View space's X
            //
            //        6        5        4
            FVector2D UndistortCornerPos0 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(0.0f, 0.0f));
            FVector2D UndistortCornerPos1 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(0.5f, 0.0f));
            FVector2D UndistortCornerPos2 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(1.0f, 0.0f));
            FVector2D UndistortCornerPos3 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(1.0f, 0.5f));
            FVector2D UndistortCornerPos4 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(1.0f, 1.0f));
            FVector2D UndistortCornerPos5 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(0.5f, 1.0f));
            FVector2D UndistortCornerPos6 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(0.0f, 1.0f));
            FVector2D UndistortCornerPos7 = LensUndistortViewportUVIntoViewSpace(
                *this, TanHalfDistortedHorizontalFOV, DistortedAspectRatio, FVector2D(0.0f, 0.5f));
    
            // Find min and max of the inner square of undistorted Viewport in the view space at z'=1.
            FVector2D MinInnerViewportRect;
            FVector2D MaxInnerViewportRect;
            MinInnerViewportRect.X = FMath::Max3(UndistortCornerPos0.X, UndistortCornerPos6.X, UndistortCornerPos7.X);
            MinInnerViewportRect.Y = FMath::Max3(UndistortCornerPos4.Y, UndistortCornerPos5.Y, UndistortCornerPos6.Y);
            MaxInnerViewportRect.X = FMath::Min3(UndistortCornerPos2.X, UndistortCornerPos3.X, UndistortCornerPos4.X);
            MaxInnerViewportRect.Y = FMath::Min3(UndistortCornerPos0.Y, UndistortCornerPos1.Y, UndistortCornerPos2.Y);
    
            check(MinInnerViewportRect.X < 0.f);
            check(MinInnerViewportRect.Y < 0.f);
            check(MaxInnerViewportRect.X > 0.f);
            check(MaxInnerViewportRect.Y > 0.f);
    
            // Compute tan(VerticalFOV * 0.5)
            float TanHalfDistortedVerticalFOV = TanHalfDistortedHorizontalFOV / DistortedAspectRatio;
    
            // Compute the required un-distorted viewport scale on each axis.
            FVector2D ViewportScaleUpFactorPerViewAxis = 0.5 * FVector2D(
                TanHalfDistortedHorizontalFOV / FMath::Max(-MinInnerViewportRect.X, MaxInnerViewportRect.X),
                TanHalfDistortedVerticalFOV / FMath::Max(-MinInnerViewportRect.Y, MaxInnerViewportRect.Y));
    
            // Scale up by 2% more the undistorted viewport size in the view space to work
            // around the fact that odd undistorted positions might not exactly be at the minimal
            // in case of a tangential distorted barrel lens distortion.
            const float ViewportScaleUpConstMultiplier = 1.02f;
            return FMath::Max(ViewportScaleUpFactorPerViewAxis.X, ViewportScaleUpFactorPerViewAxis.Y) * ViewportScaleUpConstMultiplier;
        }
    
        void FFooCameraModel::DrawUVDisplacementToRenderTarget(
            UWorld* World,
            float DistortedHorizontalFOV,
            float DistortedAspectRatio,
            float UndistortOverscanFactor,
            UTextureRenderTarget2D* OutputRenderTarget,
            float OutputMultiply,
            float OutputAdd) const
        {
            check(IsInGameThread());
    
            // Compiles the camera model to know the overscan scale factor.
            float TanHalfUndistortedHorizontalFOV = FMath::Tan(DistortedHorizontalFOV * 0.5f) * UndistortOverscanFactor;
            float TanHalfUndistortedVerticalFOV = TanHalfUndistortedHorizontalFOV / DistortedAspectRatio;
    
            // Output.
            FCompiledCameraModel CompiledCameraModel;
            CompiledCameraModel.OriginalCameraModel = *this;
    
            CompiledCameraModel.DistortedCameraMatrix.X = 1.0f / TanHalfUndistortedHorizontalFOV;
            CompiledCameraModel.DistortedCameraMatrix.Y = 1.0f / TanHalfUndistortedVerticalFOV;
            CompiledCameraModel.DistortedCameraMatrix.Z = 0.5f;
            CompiledCameraModel.DistortedCameraMatrix.W = 0.5f;
    
            CompiledCameraModel.UndistortedCameraMatrix.X = F.X;
            CompiledCameraModel.UndistortedCameraMatrix.Y = F.Y * DistortedAspectRatio;
            CompiledCameraModel.UndistortedCameraMatrix.Z = C.X;
            CompiledCameraModel.UndistortedCameraMatrix.W = C.Y;
    
            CompiledCameraModel.OutputMultiplyAndAdd.X = OutputMultiply;
            CompiledCameraModel.OutputMultiplyAndAdd.Y = OutputAdd;
    
            const FName TextureRenderTargetName = OutputRenderTarget->GetFName();
            FTextureRenderTargetResource* TextureRenderTargetResource = OutputRenderTarget->GameThread_GetRenderTargetResource();
    
            ERHIFeatureLevel::Type FeatureLevel = World->Scene->GetFeatureLevel();
    
            ENQUEUE_RENDER_COMMAND(CaptureCommand)(
                [CompiledCameraModel, TextureRenderTargetResource, TextureRenderTargetName, FeatureLevel](FRHICommandListImmediate& RHICmdList)
                {
                    DrawUVDisplacementToRenderTarget_RenderThread(
                        RHICmdList,
                        CompiledCameraModel,
                        TextureRenderTargetName,
                        TextureRenderTargetResource,
                        FeatureLevel);
                }
            );
        }
  17. 最後に「LensDistortionRendering.cpp」ファイルの 155 行と 156 行の間を、以下の 2 行のコードに変更して、予め作成しておいた新しい MyShader.USF ファイルに指定します。

    変更前:

    • IMPLEMENT_SHADER_TYPE(, FLensDistortionUVGenerationVS, TEXT("/Plugin/LensDistortion/Private/UVGeneration.usf"), TEXT("MainVS"), SF_Vertex)

    変更後:

    • IMPLEMENT_SHADER_TYPE(, FLensDistortionUVGenerationVS, TEXT("/Plugin/Foo/Private/MyShader.usf"), TEXT("MainVS"), SF_Vertex)

    変更前:

    • IMPLEMENT_SHADER_TYPE(, FLensDistortionUVGenerationPS, TEXT("/Plugin/LensDistortion/Private/UVGeneration.usf"), TEXT("MainPS"), SF_Pixel)

    変更後:

    • IMPLEMENT_SHADER_TYPE(, FLensDistortionUVGenerationPS, TEXT("/Plugin/Foo/Private/MyShader.usf"), TEXT("MainPS"), SF_Pixel)

  18. 次に、「Foo/Source」フォルダの「Foo.Build.cs」ファイルを開いて、Foo.Build.cs[PublicDependencyModuleNames.AddRange] セクションに以下のコードを追加します。

        PublicDependencyModuleNames.AddRange(
        new string[]
        {
            "Core",
            "RenderCore",
            "ShaderCore",
            "RHI",
            // ... add other public dependencies that you statically link with here ...
        }
        );
  19. 次に Foo.Build.csPrivateDependencyModuleNames.AddRange セクションで、SlateSlateCore を削除します。すると 「Foo.Build.cs」 ファイルはこのようにないrます。

        // Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.
    
        using UnrealBuildTool;
    
        public class Foo :ModuleRules
        {
            public Foo(ReadOnlyTargetRules Target) : base(Target)
            {
                PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
    
                PublicIncludePaths.AddRange(
                    new string[] {
                        "Foo/Public"
                        // ... add public include paths required here ...
                    }
                    );
                PrivateIncludePaths.AddRange(
                    new string[] {
                        "Foo/Private",
                        // ... add other private include paths required here ...
                    }
                    );
    
                PublicDependencyModuleNames.AddRange(
                    new string[]
                    {
                        "Core",
                        "RenderCore",
                        "ShaderCore",
                        "RHI",
                        // ... add other public dependencies that you statically link with here ...
                    }
                    );
                PrivateDependencyModuleNames.AddRange(
                    new string[]
                    {
                        "CoreUObject",
                        "Engine",
                        // ... add private dependencies that you statically link with here ... ( ... 静的にリンクしている他のプライベート依存をここに追加します。)
                    }
                    );
    
                DynamicallyLoadedModuleNames.AddRange(
                    new string[]
                    {
                        // ... add any modules that your module loads dynamically here ... (モジュールが動的にロードするモジュールをすべてここに追加します。)
                    }
                    );
            }
        }
  20. プロジェクトの Visual Studio ソリューション ファイルを再起動し、CRTL + 5 を押してプロジェクトを再コンパイルします。これが完了したら、F5 を押して Unreal Engine エディタを起動します。

  21. Unreal Engine エディタのロード処理が完了したら、[Edit] > [Plugins] から Plugins マネージャーを開きます。

    HT_ShadersInPlugins_08.png

  22. Plugins マネージャーを一番下までスクロールすると [Project] セクションがあります。そこに Plugin があります。

    HT_ShadersInPlugins_09.png

    プラグが有効になっていない場合、名前の横にあるチェックマーク ボックスをクリックすると Unreal Engine エディタが再起動します。

  23. すべてそろっていることを確認するためには、Level ブループリントを開いてイベントグラフを右クリックし、検索ボックスに「Foo」と入力します。これが完了すると、Foo Camera カテゴリに追加されたすべてのアイテムを見ることができます。

    クリックしてフルサイズで表示。

最終結果

これで Lense Distortion Unreal Engine プラグインの新バージョンの再生成とコンパイルが無事完了しました。次のステップでは、この新しいグローバル シェーダーをブループリントから呼び出して、レンダー ターゲットをどのように変形するのか見てみましょう。

2 - ブループリントの設定

プラグインの動作が検証されたので、このステップではブループリントを使ってグローバル シェーダーを呼び出す方法を説明します。

  1. 機能の仕方を確認するために コンテンツ ブラウザ を右クリックして、親クラスとして名前のついた アクタ のある新規 Blueprint クラス を作成します。

    HT_ShadersInPlugins_11.png

  2. 次に、「RT_00」 という名前の レンダー ターゲット および MAT_RT という名前のマテリアルを新規作成します。

    HT_ShadersInPlugins_12.png

  3. MAT_RT マテリアルを開いて、RT_00 レンダー ターゲットをマテリアル グラフに追加し、 Main Material ノードの Base Color 入力に接続し、[Apply][Save] ボタンを押します。

    クリックしてフルサイズで表示。

  4. 次に、DrawUVDisToRenderTarget ブループリントを開き、以下の変数とノードをイベントグラフに作成 / 追加します。

    変数

    デフォルト値

    K1

    1.0

    K2

    1.0

    inc

    1.0

    ノード

    デフォルト値

    W

    N/A

    S

    N/A

    Event Tick

    N/A

    Draw UVDisplacement to Render Target

    FOV、Aspect Ratio、Overscan Factor に**1** を入力します。

    Make FooCameraModel

    N/A

    Clear Render Target 2D

    N/A

    HT_ShadersInPlugins_14.png

  5. Draw UVDisplacement to Render Target が提供されたレンダー ターゲットをラップするか簡単に実演するには、Draw UVDisplacement to Render Target ノードの K1 入力と K2 入力に入力した値をボタンの押下で増加 / 減少できるようにブループリントを設定する必要があります。以下の画像に合わせてイベントグラフのノードを設定すれば完成です。

    Copy Node Graph

    ブループリント コードをブループリントに直接コピー&ペーストすることができます。

    レンダー ターゲットを必ず Output Render Target に入力してください。何も表示されなくなります。

  6. 必要なブループリント ロジックを設定したら、必ずブループリントを コンパイル および 保存 して、コンテンツ ブラウザ から レベル にブループリントをドラッグします。完了したら、ブループリントを選択し、[Input][Auto Receive Input][Disabled] から [Player 0] へ変更します。

    クリックしてフルサイズで表示。

  7. 次にフロアを選択し CTRL + W を押して複製し X 軸で -90 度 回転させます。マテリアル MAT_RT をそこにドラッグすれば Draw UVDisplacement to Render Target の効果を確認することができます。

    クリックしてフルサイズで表示。

  8. すべてを設定したら、[Play] ボタンを押して、[W] キーと [S] キーを押して Draw UVDisplacement to Render Target ノードに入力されたレンダー ターゲットを以下の動画のようにワープします。

最終結果

レンダー ターゲット内のコンテンツをブループリントから変形して、面白い形状に自由に作成できるようになりました。次は、新しいプラグインを作成、または既存のプラグインを編集してどんな種類の画像とエフェクトを作成することができるのか見てみましょう。

このページは Unreal Engine の前のバージョン用です。現在リリースされている Unreal Engine 5.3 に対して更新は行われていません。
Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
キャンセル