虚幻4渲染编程(Shader篇)【第四卷:虚幻4C++层和Shader层的简单数据通讯】

本文详细介绍如何在虚幻4引擎中实现自定义Shader,包括贴图映射、顶点着色器及像素着色器的编写等。通过具体实例展示了如何将纹理数据传递给Shader,并介绍了必要的代码实现。

本章将会在前几张的基础上进行讲解,如果你没看过前几章的话建议先看下虚幻4渲染编程(shader篇)的前几篇的内容

先上效果图如下。我们在虚幻里实现了自己的shader的贴图映射。

v2-6b11ab22f9a04ac80234b0e8fc635da9_b.jpg

为啥我们要这么麻烦敲代码做这些事情,而不是直接用虚幻的材质编辑器呢。因为这是为我们自己定制我们自己的shader管线做准备!

经过前几卷的探索,我们现在已经可以通过自己的shader和Draw调用,在一个RenderTarget上绘制东西了。虽然最后还是通过材质编辑器才传到场景的render target上,不过随着“旅行”的进行,我们揭开的谜团越来越多了。下一步我们将一起来向shader传入更多数据来完成一些更复杂的绘制。

我们现在只有一个颜色,我们来尝试一下把一张贴图传入shader吧。如果一个shader需要采一张贴图,需要一个Texture资源,需要一个sampler,还需要UV信息。我们先在HLSL代码里做如下工作:

#include "/Engine/Public/Platform.ush"  
Texture2D MyTexture;  
SamplerState MyTextureSampler;  
  
float4 SimpleColor;  
  
void MainVS(  
    in float4 InPosition : ATTRIBUTE0,  
    in float2 InUV : ATTRIBUTE1,  
    out float2 OutUV : TEXCOORD0,  
    out float4 OutPosition : SV_POSITION  
    )  
{  
    // screenspace position from vb  
    OutPosition = InPosition;  
    OutUV = InUV;  
  
}  
  
void MainPS(  
    in float2 UV : TEXCOORD0,  
    out float4 OutColor : SV_Target0  
    )  
{  
    OutColor = float4(MyTexture.Sample(MyTextureSampler, UV.xy).rgb, 1.0f);  
    OutColor *= SimpleColor;  
}  

我们给顶点着色器增加一个UV的输入输出,然后给像素着色器增加一个UV输入和一点简单的计算。因为加入了更多的顶点输入信息,所以我们要开始管理顶点输入布局了。

v2-12acd5d29be85e88f6d25f28bf6376aa_b.jpg

让我们定义一个顶点数据的结构体FMyTextureVertex,然后声明一个顶点输入布局类,在InitRHI种进行顶点输入布局的初始化。

v2-7f7d1b56bd946a2b574013f8299631ba_b.jpg

虚幻自己实现了一个ATRIBUTE语义(HLSL并没有这个语义估计是虚幻自己实现的)。这个插槽和顶点输入布局的对应关系如上图所示。

完成了顶点输入布局后,下面我们需要把顶点输入布局给渲染管线。

v2-a461ed124fc463cb28e723ca730d41d0_b.jpg

完成指认后,我们需要把顶点UV数据传进draw函数,接下来

v2-d9e322b9053c6e36639f460b607ec1af_b.jpg

我们在原来的代码基础上增加了UV数据。至此UV数据算是完全完成了。下面我们来传贴图数据和sampler数据

首先在FMyShaderText类中加入新的成员变量

v2-3390eeb1d5c6639ee12b74546713f45e_b.jpg

然后在构造函数把他们和shader里的变量绑定:

v2-76ca180f46a017b5f711ab675289cb14_b.jpg

完成这个之后就是需要CPU端传渲染资源到GPU端了:

v2-a474024dd5e28298cd09432101b49130_b.jpg

这里截图没截图完,不过不用担心,后面我会把所有代码再贴一遍,现在只需要关心的是再原有代码基础上的改动。主要是从UTexture中拿到渲染资源。

然后在渲染线程的函数中set一下即可:

v2-2af8ae440aea8b8f2cf0c344fae3001e_b.jpg
v2-6e128565bf150a0fca7b519f70d623ab_b.jpg

至此整个代码就完成啦。再在蓝图脚本中完成资源指认即可。

v2-fa5d5b8f38e6820088e8b72392bc5acf_b.jpg

至此完成了一个shader基本的需求。至于要传一些比较复杂的数据结构,如结构体buffer什么的,以后再作介绍。后面是完整的代码:

MyShader.usf

#include "/Engine/Public/Platform.ush"  
  
Texture2D MyTexture;  
SamplerState MyTextureSampler;  
  
float4 SimpleColor;  
  
void MainVS(  
    in float4 InPosition : ATTRIBUTE0,  
    in float2 InUV : ATTRIBUTE1,  
    out float2 OutUV : TEXCOORD0,  
    out float4 OutPosition : SV_POSITION  
    )  
{  
    // screenspace position from vb  
    OutPosition = InPosition;  
    OutUV = InUV;  
  
}  
  
void MainPS(  
    in float2 UV : TEXCOORD0,  
    out float4 OutColor : SV_Target0  
    )  
{  
    OutColor = float4(MyTexture.Sample(MyTextureSampler, UV.xy).rgb, 1.0f);  
    OutColor *= SimpleColor;  
}  

MyShaderTest.h(这里面的Struct是暂时没用的,以后介绍传复杂的buffer的时候会用到)

#pragma once  
  
#include "CoreMinimal.h"  
#include "UObject/ObjectMacros.h"  
#include "Classes/Kismet/BlueprintFunctionLibrary.h"  
#include "MyShaderTest.generated.h"  
  
USTRUCT(BlueprintType)  
struct FMyShaderStructData  
{  
    GENERATED_USTRUCT_BODY()  
  
    UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = ShaderData)  
    FLinearColor ColorOne;  
    UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = ShaderData)  
    FLinearColor ColorTwo;  
    UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = ShaderData)  
    FLinearColor Colorthree;  
    UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = ShaderData)  
    FLinearColor ColorFour;  
};  
  
UCLASS(MinimalAPI,meta = (ScriptName = "TestShaderLibary"))  
class UTestShaderBlueprintLibrary : public UBlueprintFunctionLibrary  
{  
    GENERATED_UCLASS_BODY()  
  
    UFUNCTION(BlueprintCallable, Category = "ShaderTestPlugin", meta = (WorldContext = "WorldContextObject"))  
    static void DrawTestShaderRenderTarget(class UTextureRenderTarget2D* OutputRenderTarget, AActor* Ac, FLinearColor MyColor, UTexture* MyTexture);  
};  

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"  
  
#include "RHICommandList.h"  
#include "UniformBuffer.h"  
  
#define LOCTEXT_NAMESPACE "TestShader"  
  
//BEGIN_UNIFORM_BUFFER_STRUCT(MyStructData, )  
//DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FVector4, ColorOne)  
//DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FVector4, ColorTwo)  
//DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FVector4, ColorThree)  
//DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FVector4, ColorFour)  
//END_UNIFORM_BUFFER_STRUCT(MyStructData)  
//typedef TUniformBufferRef<MyStructData> MyStructDataRef;  
  
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"));  
        TestTextureVal.Bind(Initializer.ParameterMap, TEXT("MyTexture"));  
        TestTextureSampler.Bind(Initializer.ParameterMap, TEXT("MyTextureSampler"));  
    }  
  
    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,  
        FTextureRHIParamRef &MyTexture  
        )  
    {  
        SetShaderValue(RHICmdList, GetPixelShader(), SimpleColorVal, MyColor);  
        SetTextureParameter(  
            RHICmdList,  
            GetPixelShader(),  
            TestTextureVal,  
            TestTextureSampler,  
            TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),  
            MyTexture);  
    }  
  
    virtual bool Serialize(FArchive& Ar) override  
    {  
        bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);  
        Ar << SimpleColorVal << TestTextureVal;  
        return bShaderHasOutdatedParameters;  
    }  
  
private:  
  
    FShaderParameter SimpleColorVal;  
  
    FShaderResourceParameter TestTextureVal;  
    FShaderResourceParameter TestTextureSampler;  
  
};  
  
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)  
  
struct FMyTextureVertex  
{  
    FVector4    Position;  
    FVector2D   UV;  
};  
  
class FMyTextureVertexDeclaration : public FRenderResource  
{  
public:  
    FVertexDeclarationRHIRef VertexDeclarationRHI;  
  
    virtual void InitRHI() override  
    {  
        FVertexDeclarationElementList Elements;  
        uint32 Stride = sizeof(FMyTextureVertex);  
        Elements.Add(FVertexElement(0, STRUCT_OFFSET(FMyTextureVertex, Position), VET_Float4, 0, Stride));  
        Elements.Add(FVertexElement(0, STRUCT_OFFSET(FMyTextureVertex, UV), VET_Float2, 1, Stride));  
        VertexDeclarationRHI = RHICreateVertexDeclaration(Elements);  
    }  
  
    virtual void ReleaseRHI() override  
    {  
        VertexDeclarationRHI->Release();  
    }  
};  
  
static void DrawTestShaderRenderTarget_RenderThread(  
    FRHICommandListImmediate& RHICmdList,   
    FTextureRenderTargetResource* OutputRenderTargetResource,  
    ERHIFeatureLevel::Type FeatureLevel,  
    FName TextureRenderTargetName,  
    FLinearColor MyColor,  
    FTextureRHIParamRef MyTexture  
)  
{  
    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);  
  
    FMyTextureVertexDeclaration VertexDec;  
    VertexDec.InitRHI();  
  
    // 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 = VertexDec.VertexDeclarationRHI;  
    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, MyTexture);  
  
    // Draw grid.  
    //uint32 PrimitiveCount = 2;  
    //RHICmdList.DrawPrimitive(PT_TriangleList, 0, PrimitiveCount, 1);  
    FMyTextureVertex Vertices[4];  
    Vertices[0].Position.Set(-1.0f, 1.0f, 0, 1.0f);  
    Vertices[1].Position.Set(1.0f, 1.0f, 0, 1.0f);  
    Vertices[2].Position.Set(-1.0f, -1.0f, 0, 1.0f);  
    Vertices[3].Position.Set(1.0f, -1.0f, 0, 1.0f);  
    Vertices[0].UV = FVector2D(0.0f, 1.0f);  
    Vertices[1].UV = FVector2D(1.0f, 1.0f);  
    Vertices[2].UV = FVector2D(0.0f, 0.0f);  
    Vertices[3].UV = FVector2D(1.0f, 0.0f);  
  
    static const uint16 Indices[6] =  
    {  
        0, 1, 2,  
        2, 1, 3  
    };  
    //DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, Vertices, sizeof(Vertices[0]));  
    DrawIndexedPrimitiveUP(  
        RHICmdList,  
        PT_TriangleList,  
        0,  
        ARRAY_COUNT(Vertices),  
        2,  
        Indices,  
        sizeof(Indices[0]),  
        Vertices,  
        sizeof(Vertices[0])  
    );  
  
    // Resolve render target.  
    RHICmdList.CopyToResolveTarget(  
        OutputRenderTargetResource->GetRenderTargetTexture(),  
        OutputRenderTargetResource->TextureRHI,  
        false, FResolveParams());  
}  
  
void UTestShaderBlueprintLibrary::DrawTestShaderRenderTarget(  
    UTextureRenderTarget2D* OutputRenderTarget,   
    AActor* Ac,  
    FLinearColor MyColor,  
    UTexture* MyTexture  
)  
{  
    check(IsInGameThread());  
  
    if (!OutputRenderTarget)  
    {  
        return;  
    }  
      
    FTextureRenderTargetResource* TextureRenderTargetResource = OutputRenderTarget->GameThread_GetRenderTargetResource();  
    FTextureRHIParamRef MyTextureRHI = MyTexture->TextureReference.TextureReferenceRHI;  
    UWorld* World = Ac->GetWorld();  
    ERHIFeatureLevel::Type FeatureLevel = World->Scene->GetFeatureLevel();  
    FName TextureRenderTargetName = OutputRenderTarget->GetFName();  
    ENQUEUE_RENDER_COMMAND(CaptureCommand)(  
        [TextureRenderTargetResource, FeatureLevel, MyColor, TextureRenderTargetName, MyTextureRHI](FRHICommandListImmediate& RHICmdList)  
        {  
            DrawTestShaderRenderTarget_RenderThread(RHICmdList,TextureRenderTargetResource, FeatureLevel, TextureRenderTargetName, MyColor, MyTextureRHI);  
        }  
    );  
  
}  
  
#undef LOCTEXT_NAMESPACE  

至此我们完成了逻辑层到shader的数据传输

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值