在插件 - 代码设置中新建一个全局着色器

设置全局着色器插件的插件和代码

Windows
MacOS
Linux
本页面的内容

开始新建虚幻引擎 4 的插件之前,需要先安装 Visual Studio。此快速入门需要 Visual Studio,用户需要对插件代码进行编译,使其能正常运行。如不了解如何进行此操作,请查阅 设置 UE4 的 Visual Studio 文档。

  1. 首先新建一个 C++ 基础代码(Basic Code) 项目,桌面设置(Desktop Setting) 设为 最高精度(Maximum Quality),且不带 新手内容(Starter Content)

    点击查看全图。

  2. 项目创建后,Visual Studio 将打开,之后即可右键点击 ShadersInPlugins 项目并选择 Build 选项编译项目。

    HT_ShadersInPlugins_01.png

  3. 项目编译完成后,在 Visual Studio 中按下 F5 键在 UE4 编辑器中运行 ShadersInPlugins 项目。

  4. UE4 编辑器加载完成后,前往 Edit > Plugins 打开 Plugins 管理器,然后点击 Plugin 窗口右下侧的 New Plugin 选项呼出新插件创建窗口。

    点击查看全图。

  5. New Plugin 窗口中选择 Blank 插件,然后将其命名为 Foo,保留全部默认设置。所有操作完成后,按下 Create Plugin 键创建插件最初需要的全部内容。

    点击查看全图。

  6. 完成后,关闭 UE4 和 Visual Studio,前往在项目文件夹中创建的 Plugins > Foo 插件文件夹。

  7. 在 Foo plugins 文件夹中添加一个名为 Shaders 的新文件夹,并在其中再新建一个名为 Private 的文件夹。

    点击查看全图。

  8. 在 Private 文件夹中新建一个文本文件并将其命名为 MyShader.USF,然后将以下 HLSL 代码复制粘贴到此文件中,完成操作后保存文件。

    必须将文件扩展名改为 .USF,否则 UE4 无法将其识别为着色器文件。

    // 版权所有 1998-2018 Epic Games, Inc. 保留所有权利。
    
    /*=============================================================================
        LensDistortionUVGeneration.usf:将光学变形和不
        变形 UV 置换贴图生成到一个渲染目标中。
    
        像素着色器直接计算变形视口 UV,
        不对使用 Sv_Position 和参考方程式的视口 UV 置换执行变形,
        并将它们保存到红色和绿色的通道中。
    
        为避免用费拉里法进行解析、
        或在 GPU 上执行牛顿法计算不变形视口 UV 这两种方式
        来对视口 UV 置换进行变形,这对着色器的工作方式如下:顶点着色器不对网格的顶点执行变形,
        并下传到像素着色器视口 UV 应在屏幕上
        所处的位置,而不进行变形。像素
        着色器可生成不变形视口 UV,
        减去像素的视口 UV 后即可对视口 UV 置换进行变形。
    =============================================================================*/
    
    #include "/Engine/Public/Platform.ush"
    
    // 视口 UV 坐标中的像素大小。
    float2 PixelUVSize;
    
    // K1, K2, K3
    float3 RadialDistortionCoefs;
    
    // P1, P2
    float2 TangentialDistortionCoefs;
    
    // 未变形视口的摄像机矩阵。
    float4 UndistortedCameraMatrix;
    
    // 变形视口的摄像机矩阵。
    float4 DistortedCameraMatrix;
    
    // 对渲染目标输出乘法和加法。
    float2 OutputMultiplyAndAdd;
    
    // 不对 V.z=1 视图位置进行变形。
    float2 UndistortNormalizedViewPosition(float2 V)
    {
        float2 V2 = V * V;
        float R2 = V2.x + V2.y;
    
        // 径向变形(额外添加括号是为匹配 MF_Undistortion.uasset)。
        float2 UndistortedV = V * (1.0 + R2 * (RadialDistortionCoefs.x + R2 * (RadialDistortionCoefs.y + R2 * RadialDistortionCoefs.z)));
    
        // 切向变形。
        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;
    }
    
    // 返回变形视口 UV 的未变形视口 UV。
    //
    // 注意:
    //        UV 创建于左下角。
    float2 UndistortViewportUV(float2 ViewportUV)
    {
        // 已变形视口 UV -> 已变形视图位置(z=1)
        float2 DistortedViewPosition = (ViewportUV - DistortedCameraMatrix.zw) / DistortedCameraMatrix.xy;
    
        // 计算未变形的视图位置 (z=1)
        float2 UndistortedViewPosition = UndistortNormalizedViewPosition(DistortedViewPosition);
    
        // 未变形的视图位置 (z=1) -> 未变形的视口 UV。
        return UndistortedCameraMatrix.xy * UndistortedViewPosition + UndistortedCameraMatrix.zw;
    }
    
    // 翻转 UV 的 y 组件。
    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
        )
    {
        // 计算单元索引。
        uint GridCellIndex = GlobalVertexId / 6;
    
        // 计算网格中单元行和列的 ID。
        uint GridColumnId = GridCellIndex / GRID_SUBDIVISION_Y;
        uint GridRowId = GridCellIndex - GridColumnId * GRID_SUBDIVISION_Y;
    
        // 计算双三角形网格单元中的顶点 ID。
        uint VertexId = GlobalVertexId - GridCellIndex * 6;
    
        // 计算单元中三角形顶点源自左下角的 UV 坐标。
        float2 CellVertexUV = float2(0x1 & ((VertexId + 1) / 3), VertexId & 0x1);
    
        // 计算网格中顶点源自左上角的 UV。
        float2 GridInvSize = 1.f / float2(GRID_SUBDIVISION_X, GRID_SUBDIVISION_Y);
        float2 GridVertexUV = FlipUV(
            GridInvSize * (CellVertexUV + float2(GridColumnId, GridRowId)));
    
        // 标准不含半像素位移。
        GridVertexUV -= PixelUVSize * 0.5;
    
        // 输出顶点位置。
        OutPosition = float4(FlipUV(
            UndistortViewportUV(GridVertexUV) + PixelUVSize * 0.5) * 2 - 1, 0, 1);
    
        // 输出顶点源自左上角的 UV。
        OutVertexDistortedViewportUV = GridVertexUV;
    }
    
    void MainPS(
        in noperspective float2 VertexDistortedViewportUV :TEXCOORD0,
        in float4 SvPosition :SV_POSITION,
        out float4 OutColor :SV_Target0
        )
    {
        // 计算像素源自左上角的 UV。
        float2 ViewportUV = SvPosition.xy * PixelUVSize;
    
        // 标准不含半像素位移。
        ViewportUV -= PixelUVSize * 0.5;
    
        float2 DistortUVtoUndistortUV = (UndistortViewportUV((ViewportUV))) - ViewportUV;
        float2 UndistortUVtoDistortUV = VertexDistortedViewportUV - ViewportUV;
    
        // 输出置换通道。
        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,然后从 Engine\Plugins\Compositing\LensDistortion 路径下复制 LensDistortionAPI.hLensDistortionBlueprintLibrary.h 文件到这个新建的文件夹中。

    我们将要复制的文件在 Engine\Plugins\Compositing\LensDistortion 中。

    • 类 - 新建文件夹

      • 复制 - LensDistortionAPI.h

      • 复制 - LensDistortionBlueprintLibrary.h

      HT_ShadersInPlugins_05.png

  11. 然后前往 Private 文件夹并将 LensDistortionBlueprintLibrary.cppLensDistortionRendering.cpp 文件复制到这个 Private 文件夹。

    • Private - 现有文件夹

      • 复制 - LensDistortionBlueprintLibrary.cpp

      • 复制 - LensDistortionRendering.cpp

      HT_ShadersInPlugins_06.png

  12. 现在关闭 UE4 编辑器和 Visual Studio,然后找到项目的 .U 项目文件。找到后,对其单击右键,并选择 Generate Visual Studio project files 选项。

    HT_ShadersInPlugins_07.png

  13. 重新打开 Visual Studio 解决方案,然后前往 Foo > Classes 并打开 LensDistortionAPI.h 文件。在此文件中用 FFooCameraModel 替换 FLensDistortionCameraModel

    您需要在此文件中 四次 用 FFooCameraModel 替换 FLensDistortionCameraModel。

        // 版权所有 1998-2018 Epic Games, Inc. 保留所有权利。
    
        #pragma once
    
        #include "CoreMinimal.h"
        #include "LensDistortionAPI.generated.h"
    
        /** 光学变形/不变形的数学摄像机模型。
         *
         * 摄像机矩阵 =
         *  | 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);
            }
    
            /** 径向参数 #1。*/
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float K1;
    
            /** 径向参数 #2。*/
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float K2;
    
            /** 径向参数 #3。*/
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float K3;
    
            /** 切向参数 #1。*/
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float P1;
    
            /** 切向参数 #2。*/
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            float P2;
    
            /** 摄像机矩阵的 Fx 和 Fy。*/
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            FVector2D F;
    
            /** 摄像机矩阵的 Cx 和 Cy。*/
            UPROPERTY(Interp, EditAnywhere, BlueprintReadWrite, Category = "Lens Distortion|Camera Model")
            FVector2D C;
    
            /** 不在视图空间中进行 3D 矢量变形(x, y, z=1.f)并返回(x', y', z'=1.f)。*/
            FVector2D UndistortNormalizedViewPosition(FVector2D V) const;
    
            /** 返回不变形渲染所需的过扫描因子,避免出现未渲染的变形像素。*/
            float GetUndistortOverscanFactor(
                float DistortedHorizontalFOV,
                float DistortedAspectRatio) const;
    
            /** 在输出渲染目标中绘制 UV 置换贴图。
             * - 红色和绿色通道负责变形置换;
             * - 蓝色和透明通道负责不变形置换。
             * @param World 获取渲染设置的当前世界场景(如特征场景)。
             * @param DistortedHorizontalFOV 变形渲染中理想的水平视野。
             * @param DistortedAspectRatio 变形渲染中理想的高宽比。
             * @param UndistortOverscanFactor 未变形渲染的过扫描因子。
             * @param OutputRenderTarget 进行绘制的渲染目标。不必拥有和变形渲染相同的分辨率或高宽比。
             * @param OutputMultiply 应用在置换上的乘法因子。
             * @param OutputAdd 保存到输出渲染目标中之前被添加到相乘置换的值。
             */
            void DrawUVDisplacementToRenderTarget(
                class UWorld* World,
                float DistortedHorizontalFOV,
                float DistortedAspectRatio,
                float UndistortOverscanFactor,
                class UTextureRenderTarget2D* OutputRenderTarget,
                float OutputMultiply,
                float OutputAdd) const;
    
            /** 对比两个光学变形模型并返回它们是否相同。*/
            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);
            }
    
            /** 对比两个光学变形模型并返回它们是否不同。*/
            bool operator != (const FFooCameraModel& Other) const
            {
                return !(*this == Other);
            }
        };
  14. 接下来打开 LensDistortionBlueprintLibrary.h 文件。此文件控制此节点在蓝图中的显示方式,因此我们不仅需要用 FFooCameraModel 替换 FLensDistortionCameraModel,还需要将 Category = "Lens Distortion” 改为 Category = "Foo | Lens Distortion”

    您需要在此文件中 六次 用 FFooCameraModel 替换 FLensDistortionCameraModel。

    // 版权所有 1998-2018 Epic Games, Inc. 保留所有权利。
    
    #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()
    
        /** 返回不变形渲染所需的过扫描因子,避免出现未渲染的变形像素。*/
        UFUNCTION(BlueprintPure, Category = "Foo | Lens Distortion")
        static void GetUndistortOverscanFactor(
            const FFooCameraModel& CameraModel,
            float DistortedHorizontalFOV,
            float DistortedAspectRatio,
            float& UndistortOverscanFactor);
    
        /** 在输出渲染目标中绘制 UV 置换贴图。
         * - 红色和绿色通道负责变形置换;
         * - 蓝色和透明通道负责不变形置换。
         * @param DistortedHorizontalFOV 变形渲染中理想的水平视野。
         * @param DistortedAspectRatio 变形渲染中理想的高宽比。
         * @param UndistortOverscanFactor 未变形渲染的过扫描因子。
         * @param OutputRenderTarget 进行绘制的渲染目标。不必拥有和变形渲染相同的分辨率或高宽比。
         * @param OutputMultiply 应用在置换上的乘法因子。
         * @param OutputAdd 保存到输出渲染目标中之前被添加到相乘置换的值。
         */
        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
            );
    
        /* 如 A 等于 B,则返回true (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;
        }
    
        /* 如 A 不等于 B,则返回 true (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 进行以下替换:

    • FFooCameraModel 替换 FLensDistortionCameraModel

    • UFooBlueprintLibrary 替换 ULensDistortionBlueprintLibrary

    您应该用 FFooCameraModel 替换 FLensDistortionCameraModel 两次;用 UFooBlueprintLibrary 替换 ULensDistortionBlueprintLibrary 四次

        // 版权所有 1998-2018 Epic Games, Inc. 保留所有权利。
    
        #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 文件并用 FFooCameraModel 替代 FLensDistortionCameraModel

    您需要在此文件中用 FFooCameraModel 替换 FLensDistortionCameraModel 六次

        // 版权所有 1998-2018 Epic Games, Inc. 保留所有权利。
    
        #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;
    
        /**
         * 派生自 FFooCameraModel 的中间结构体,
         * 由游戏线程交付给渲染线程。
         */
        struct FCompiledCameraModel
        {
            /** 生成此编译模型的原始摄像机模型。*/
            FFooCameraModel OriginalCameraModel;
    
            /** 未变形和变形渲染光学变形的摄像机矩阵。
             *  XY 保存缩放因子,ZW 保存平移。
             */
            FVector4 DistortedCameraMatrix;
            FVector4 UndistortedCameraMatrix;
    
            /** 对渲染目标输出通道的乘法和加法。*/
            FVector2D OutputMultiplyAndAdd;
        };
    
        /** 不对源自左上角的视口 UV 进行变形,将其放入视图空间(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:
    
            /** 默认构造函数。*/
            FLensDistortionUVGenerationVS() {}
    
            /** 初始化构造函数。*/
            FLensDistortionUVGenerationVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                :FLensDistortionUVGenerationShader(Initializer)
            {
            }
        };
    
        class FLensDistortionUVGenerationPS : public FLensDistortionUVGenerationShader
        {
            DECLARE_SHADER_TYPE(FLensDistortionUVGenerationPS, Global);
    
        public:
    
            /** 默认构造函数。*/
            FLensDistortionUVGenerationPS() {}
    
            /** 初始化构造函数。*/
            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
    
            // 设置渲染目标
            SetRenderTarget(
                RHICmdList,
                OutTextureRenderTargetResource->GetRenderTargetTexture(),
                FTextureRHIRef(),
                ESimpleRenderTargetMode::EUninitializedColorAndDepth,
                FExclusiveDepthStencil::DepthNop_StencilNop);
    
            FIntPoint DisplacementMapResolution(OutTextureRenderTargetResource->GetSizeX(), OutTextureRenderTargetResource->GetSizeY());
    
            // 更新视口。
            RHICmdList.SetViewport(
                0, 0, 0.f,
                DisplacementMapResolution.X, DisplacementMapResolution.Y, 1.f);
    
            // 获取着色器。
            TShaderMap<FGlobalShaderType>* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
            TShaderMapRef< FLensDistortionUVGenerationVS > VertexShader(GlobalShaderMap);
            TShaderMapRef< FLensDistortionUVGenerationPS > PixelShader(GlobalShaderMap);
    
            // 设置图像管线状态。
            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);
    
            // 更新视口。
            RHICmdList.SetViewport(
                0, 0, 0.f,
                OutTextureRenderTargetResource->GetSizeX(), OutTextureRenderTargetResource->GetSizeY(), 1.f);
    
            // 更新着色器的统一参数。
            VertexShader->SetParameters(RHICmdList, VertexShader->GetVertexShader(), CompiledCameraModel, DisplacementMapResolution);
            PixelShader->SetParameters(RHICmdList, PixelShader->GetPixelShader(), CompiledCameraModel, DisplacementMapResolution);
    
            // 绘制网格。
            uint32 PrimitiveCount = kGridSubdivisionX * kGridSubdivisionY * 2;
            RHICmdList.DrawPrimitive(PT_TriangleList, 0, PrimitiveCount, 1);
    
            // 解析渲染目标。
            RHICmdList.CopyToResolveTarget(
                OutTextureRenderTargetResource->GetRenderTargetTexture(),
                OutTextureRenderTargetResource->TextureRHI,
                false, FResolveParams());
        }
    
        FVector2D FFooCameraModel::UndistortNormalizedViewPosition(FVector2D EngineV) const
        {
            // 引擎视图空间 -> 标准视图空间。
            FVector2D V = FVector2D(1, -1) * EngineV;
    
            FVector2D V2 = V * V;
            float R2 = V2.X + V2.Y;
    
            // 径向变形(额外添加括号是为匹配 MF_Undistortion.uasset)。
            FVector2D UndistortedV = V * (1.0 + (R2 * K1 + (R2 * R2) * K2 + (R2 * R2 * R2) * K3));
    
            // 切向变形。
            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;
    
            // 返回引擎 V。
            return UndistortedV * FVector2D(1, -1);
        }
    
        /** 编译摄像机模型。*/
        float FFooCameraModel::GetUndistortOverscanFactor(
            float DistortedHorizontalFOV, float DistortedAspectRatio) const
        {
            // 如光学变形模型为同一,则会及早返回 1。
            if (*this == FFooCameraModel())
            {
                return 1.0f;
            }
    
            float TanHalfDistortedHorizontalFOV = FMath::Tan(DistortedHorizontalFOV * 0.5f);
    
            // 获取变形视口 UV 坐标系统中不同关键点在 z'=1 处视图空间中所处的位置。
            // 这非常近似于知晓未变形视口所需的过扫描缩放因子,但在实际操作中效果极佳。
            //
            //  视图空间中未变形 UV 位置:
            //                 ^ 视图空间的 Y 轴
            //                 |
            //        0        1        2
            //     
            //        7        0        3 --> 视图空间的 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));
    
            // 寻找 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);
    
            // 计算正切(VerticalFOV * 0.5)
            float TanHalfDistortedVerticalFOV = TanHalfDistortedHorizontalFOV / DistortedAspectRatio;
    
            // 计算每个轴上所需的未变形视口比例。
            FVector2D ViewportScaleUpFactorPerViewAxis = 0.5 * FVector2D(
                TanHalfDistortedHorizontalFOV / FMath::Max(-MinInnerViewportRect.X, MaxInnerViewportRect.X),
                TanHalfDistortedVerticalFOV / FMath::Max(-MinInnerViewportRect.Y, MaxInnerViewportRect.Y));
    
            // 将视图空间中未变形视口尺寸调大 2%,
            // 即可解决奇数未变形位置在切向
            // 桶状光学变形中并不为最小的问题。
            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());
    
            // 编译摄像机模型,以了解过扫描比例因子。
            float TanHalfUndistortedHorizontalFOV = FMath::Tan(DistortedHorizontalFOV * 0.5f) * UndistortOverscanFactor;
            float TanHalfUndistortedVerticalFOV = TanHalfUndistortedHorizontalFOV / DistortedAspectRatio;
    
            // 输出。
            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 行将以下两行代码改为指向之前新创建的 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",
            // ... 在此处添加静态连接的其他公共依赖性 ...
        }
        );
  19. 然后在 Foo.Build.cs 文件中的 PrivateDependencyModuleNames.AddRange 部分移除 SlateSlateCore。操作完成后 Foo.Build.cs 应如下:

    // 版权所有 1998-2018 Epic Games, Inc. 保留所有权利。
    
    using UnrealBuildTool;
    
    public class Foo :ModuleRules
    {
        public Foo(ReadOnlyTargetRules Target) : base(Target)
        {
            PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
    
            PublicIncludePaths.AddRange(
                new string[] {
                    "Foo/Public"
                    // ... 在此处添加所需的公共包含路径 ...
                }
                );    
            PrivateIncludePaths.AddRange(
                new string[] {
                    "Foo/Private",
                    // ... 在此处添加所需的其他私有包含路径 ...
                }
                );
    
            PublicDependencyModuleNames.AddRange(
                new string[]
                {
                    "Core",
                    "RenderCore",
                    "ShaderCore",
                    "RHI",
                    // ... 在此处添加静态连接的其他公共依赖性 ...
                }
                );
            PrivateDependencyModuleNames.AddRange(
                new string[]
                {
                    "CoreUObject",
                    "Engine",
                    // ... 在此处添加静态连接的私有依赖性 ...    
                }
                );
    
            DynamicallyLoadedModuleNames.AddRange(
                new string[]
                {
                    // ... 在此处添加您的模式动态加载的模式 ...
                }
                );
        }
    }
  20. 重新启动项目的 Visual Studio 解决方案文件,并按下 CRTL + 5 重新编译项目。编译完成后按下 F5 启动 UE4 编辑器。

  21. UE4 编辑器加载完成后,前往 Edit > Plugins 访问 Plugins 管理器。

    HT_ShadersInPlugins_08.png

  22. 在 Plugins 管理器中向下滚动寻找 Project 部分,在此部分中找到插件。

    HT_ShadersInPlugins_09.png

    如插件未启用,则点击其命名旁边的勾选框将其启用,并重启 UE4 编辑器。

  23. 打开关卡蓝图,在事件图表中点击右键,在搜索框中输入 Foo 即可检查是否所有内容均齐备。完成后,您便可看到所有项目均被添加到 Foo Camera。

    点击查看全图。

最终结果

恭喜,您便已成功重建和编译了 UE4 Lense Distortion 插件的新版本。下一部分将讲述如何从蓝图调用这个新全局着色器,以及其如何对渲染目标进行变形。

欢迎来到全新虚幻引擎4文档站!

我们正在努力开发新功能,包括反馈系统,以便您能对我们的工作作出评价。但它目前还未正式上线。如果您对此页面有任何意见与在使用中遭遇任何问题,请前往文档反馈论坛告知我们。

新系统上线运行后,我们会及时通知您的。

发表反馈意见