123456911 发表于 2020-12-28 10:45

虚幻4渲染编程(Shader篇)【第二卷:不用虚幻4Shader管线使用自己的shader】

MY BLOG DIRECTORY:
INTRODUCTION:
下面的球使用的是我用GlobalShader画出来的RT赋上的颜色
这个球的颜色是通过自己的shader实现的:
下面就来一步一步实现使用自己的shader吧。
MAIN CONTENT:
首先我们先创建一个插件。插件其实相当于一个独立的模块,我不想再项目里做,当然再游戏模块里做也是没问题的。我们先把整个流程搞清楚了再去改引擎。
首先创建个插件,名字取为ShadertestPlugin
然后打开工程里的这个插件,在插件目录下加一个Shaders文件夹




然后再在Shaders文件夹里加Private文件夹




然后再在private里面夹里新建一个文本文档,然后把后缀名改位.usf


在这个MyShader.usf里键入如下代码:
#include "/Engine/Public/Platform.ush"

float4 SimpleColor;
void MainVS(
in float4 InPosition : ATTRIBUTE0,
out float4 OutPosition : SV_POSITION
)
{
// screenspace position from vb
    OutPosition = InPosition;
}

void MainPS(
    out float4 OutColor : SV_Target0
    )
{
    OutColor = SimpleColor;
}我声明了一个MyColor的颜色,这个变量后面会和c++绑定,到时候就可以让它跟cpu交互了。然后声明了一个顶点着色器和像素着色i器。
完成了这个之后,再在插件的source文件夹下加两个文件




新建两个文本文档然后把后缀改位.h和.cpp就可以了。做完这些之后记得右键Uproject文件重写generate一下工程




然后重新加载vs项目就可以啦。
然后在把ShadertestPlugin.uplugin文件改一下,如下图。把加载顺序改成在引擎前加载。PostConfigInit。shader必须要在引擎加载完成前加载完才行。
做完这个后,再在MyShader.h中打入如下代码:
然后再在MyShader.cpp中打入如下代码:
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "MyShaderTest.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"
#include "Public/Logging/MessageLog.h"
#include "Public/Internationalization/Internationalization.h"
#include "Public/StaticBoundShaderState.h"

#define LOCTEXT_NAMESPACE "TestShader"

UTestShaderBlueprintLibrary::UTestShaderBlueprintLibrary(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{

}

class FMyShaderTest : public FGlobalShader
{
public:

    FMyShaderTest(){}

    FMyShaderTest(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
      : FGlobalShader(Initializer)
    {
      SimpleColorVal.Bind(Initializer.ParameterMap, TEXT("SimpleColor"));
    }

    static bool ShouldCache(EShaderPlatform Platform)
    {
      return true;
    }

    static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
    {
      //return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
      return true;
    }

    static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
    {
      FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
      OutEnvironment.SetDefine(TEXT("TEST_MICRO"), 1);
    }

    void SetParameters(
      FRHICommandListImmediate& RHICmdList,
      const FLinearColor &MyColor
      )
    {
      SetShaderValue(RHICmdList, GetPixelShader(), SimpleColorVal, MyColor);
    }

    virtual bool Serialize(FArchive& Ar) override
    {
      bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
      Ar << SimpleColorVal;
      return bShaderHasOutdatedParameters;
    }

private:

    FShaderParameter SimpleColorVal;

};

class FShaderTestVS : public FMyShaderTest
{
    DECLARE_SHADER_TYPE(FShaderTestVS, Global);

public:
    FShaderTestVS(){}

    FShaderTestVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
      : FMyShaderTest(Initializer)
    {

    }
};


class FShaderTestPS : public FMyShaderTest
{
    DECLARE_SHADER_TYPE(FShaderTestPS, Global);

public:
    FShaderTestPS() {}

    FShaderTestPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
      : FMyShaderTest(Initializer)
    {

    }
};

IMPLEMENT_SHADER_TYPE(, FShaderTestVS, TEXT("/Plugin/ShadertestPlugin/Private/MyShader.usf"), TEXT("MainVS"), SF_Vertex)
IMPLEMENT_SHADER_TYPE(, FShaderTestPS, TEXT("/Plugin/ShadertestPlugin/Private/MyShader.usf"), TEXT("MainPS"), SF_Pixel)

static void DrawTestShaderRenderTarget_RenderThread(
    FRHICommandListImmediate& RHICmdList,   
    FTextureRenderTargetResource* OutputRenderTargetResource,
    ERHIFeatureLevel::Type FeatureLevel,
    FName TextureRenderTargetName,
    FLinearColor MyColor
)
{
    check(IsInRenderingThread());

#if WANTS_DRAW_MESH_EVENTS
    FString EventName;
    TextureRenderTargetName.ToString(EventName);
    SCOPED_DRAW_EVENTF(RHICmdList, SceneCapture, TEXT("ShaderTest %s"), *EventName);
#else
    SCOPED_DRAW_EVENT(RHICmdList, DrawUVDisplacementToRenderTarget_RenderThread);
#endif

    //设置渲染目标
    SetRenderTarget(
      RHICmdList,
      OutputRenderTargetResource->GetRenderTargetTexture(),
      FTextureRHIRef(),
      ESimpleRenderTargetMode::EUninitializedColorAndDepth,
      FExclusiveDepthStencil::DepthNop_StencilNop
    );

    //设置视口
    //FIntPoint DrawTargetResolution(OutputRenderTargetResource->GetSizeX(), OutputRenderTargetResource->GetSizeY());
    //RHICmdList.SetViewport(0, 0, 0.0f, DrawTargetResolution.X, DrawTargetResolution.Y, 1.0f);

    TShaderMap<FGlobalShaderType>* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
    TShaderMapRef<FShaderTestVS> VertexShader(GlobalShaderMap);
    TShaderMapRef<FShaderTestPS> 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);

    //RHICmdList.SetViewport(0, 0, 0.0f, DrawTargetResolution.X, DrawTargetResolution.Y, 1.0f);
    PixelShader->SetParameters(RHICmdList, MyColor);

    // Draw grid.
    //uint32 PrimitiveCount = 2;
    //RHICmdList.DrawPrimitive(PT_TriangleList, 0, PrimitiveCount, 1);
    FVector4 Vertices;
    Vertices.Set(-1.0f, 1.0f, 0, 1.0f);
    Vertices.Set(1.0f, 1.0f, 0, 1.0f);
    Vertices.Set(-1.0f, -1.0f, 0, 1.0f);
    Vertices.Set(1.0f, -1.0f, 0, 1.0f);
    static const uint16 Indices =
    {
      0, 1, 2,
      2, 1, 3
    };
    //DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, Vertices, sizeof(Vertices));
    DrawIndexedPrimitiveUP(
      RHICmdList,
      PT_TriangleList,
      0,
      ARRAY_COUNT(Vertices),
      2,
      Indices,
      sizeof(Indices),
      Vertices,
      sizeof(Vertices)
    );

    // Resolve render target.
    RHICmdList.CopyToResolveTarget(
      OutputRenderTargetResource->GetRenderTargetTexture(),
      OutputRenderTargetResource->TextureRHI,
      false, FResolveParams());
}

void UTestShaderBlueprintLibrary::DrawTestShaderRenderTarget(
    UTextureRenderTarget2D* OutputRenderTarget,   
    AActor* Ac,
    FLinearColor MyColor
)
{
    check(IsInGameThread());

    if (!OutputRenderTarget)
    {
      return;
    }

    FTextureRenderTargetResource* TextureRenderTargetResource = OutputRenderTarget->GameThread_GetRenderTargetResource();
    UWorld* World = Ac->GetWorld();
    ERHIFeatureLevel::Type FeatureLevel = World->Scene->GetFeatureLevel();
    FName TextureRenderTargetName = OutputRenderTarget->GetFName();
    ENQUEUE_RENDER_COMMAND(CaptureCommand)(
      (FRHICommandListImmediate& RHICmdList)
      {
            DrawTestShaderRenderTarget_RenderThread(RHICmdList,TextureRenderTargetResource, FeatureLevel, TextureRenderTargetName, MyColor);
      }
    );

}

#undef LOCTEXT_NAMESPACE
稍后我会一行行解释这些代码的意思。然后我们要修改下c#文件
完成这些后再generate一下工程,然后就可以打开项目了。我们新建如下资源:


打开BP_Shader_Test蓝图做如下设置
新建一个颜色变量,然后再beginplay函数先清空一下我们的那张ShaderTestRT渲染目标,然后把我们这个shadertestrt渲染目标给我们的DrawTestShaderRenderTarget函数。还需要注意设置input,这样actor才能接收到我们的input事件。
做完这些之后再建一个材质,把RT拖到材质里(这里只是想让我们的shader效果被看到,我们的shader已经可以运作了)
这个操作其实已经是多pass了!!!然后把BP_Shader_Test拖到场景里,你按下1 2 3 就能看到如下效果了:
SUMMARY AND OUTLOOK:
虽然是一个简单的例子,但是终于可以在Unreal中使用自己的着色器了。
Enjoy it
NEXT:
页: [1]
查看完整版本: 虚幻4渲染编程(Shader篇)【第二卷:不用虚幻4Shader管线使用自己的shader】