BlueRose
文章97
标签28
分类7
(虚幻4Shader篇)向Shader传递数据

(虚幻4Shader篇)向Shader传递数据

基础类型数据

Shader中的基础类型数据,例如:int、float4等的传入方式如下:

  1. 在你声明的Shader类中声明FShaderParameter变量。
  2. 在构造函数中绑定Shader中的变量。
  3. 在序列化函数中添加声明的FShaderParameter变量。
  4. 实现自定义的设置Shader变量函数,并在最终绘制前调用。

在构造函数中绑定Shader中的变量,其格式如下:

//其中SimpleColorVal为Shader类中声明的FShaderParameter变量,TEXT("SimpleColor")为usf声明的变量。
SimpleColorVal.Bind(Initializer.ParameterMap,TEXT("SimpleColor"));

往序列化函数中添加声明的FShaderParameter变量

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

实现自定义的设置Shader变量函数

template<typename TShaderRHIParamRef>
void SetParameters(FRHICommandListImmediate& RHICmdList,const TShaderRHIParamRef ShaderRHI,const FLinearColor &MyColor)
{
    SetShaderValue(RHICmdList, ShaderRHI, SimpleColorVal,MyColor); 
}

在绘制前调用(详细代码请见github或者见上一篇文章)

static void DrawTestShaderRenderTarget_RenderThread(  
    FRHICommandListImmediate& RHICmdList,   
    FTextureRenderTargetResource* OutputRenderTargetResource,  
    ERHIFeatureLevel::Type FeatureLevel,  
    const FLinearColor MyColor,
    const FTextureRHIParamRef TextureRHI,
    const FSimpleUniformStruct UniformStruct
)  
{
    //上略
    VertexShader->SetParameters(RHICmdList, VertexShader->GetVertexShader(),MyColor,TextureRHI);
    PixelShader->SetParameters(RHICmdList, PixelShader->GetPixelShader(),MyColor,TextureRHI);
    //下略
}

如此一来,usf中的SimpleColor就被

顶点数据

我们可以参考Ue4中的代码,搜索VertexDeclaration就可以看到官方是如何声明顶点格式的。

声明顶点格式步骤:

  1. 定义定点格式结构体
  2. 继承FRenderResource定义新的顶点声明,并且实现InitRHI与ReleaseRHI接口
  3. 在渲染线程的函数中给显卡管线状态指定顶点声明,GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = VertexDeclaration.VertexDeclarationRHI;

首先我们需要声明自己的顶点格式:

struct FTextureVertex{
    FVector4 Position;
    FVector2D UV;
};

class FTextureVertexDeclaration : public FRenderResource
{
public:
    FVertexDeclarationRHIRef VertexDeclarationRHI;
    virtual void InitRHI() override
    {
        FVertexDeclarationElementList Elements;
        uint32 Stride = sizeof(FTextureVertex);
        Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, Position), VET_Float2, 0, Stride));
        Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, UV), VET_Float2, 1, Stride));
        VertexDeclarationRHI = RHICreateVertexDeclaration(Elements);
    }
    virtual void ReleaseRHI() override
    {
        VertexDeclarationRHI.SafeRelease();
    }
};

有需要还可以加入顶点法线或者顶点色之类的,接下来就是使用声明的顶点格式来声明顶点缓存与顶点索引:

class FRectangleVertexBuffer : public FVertexBuffer
{
public:
    /** Initialize the RHI for this rendering resource */
    void InitRHI() override 
    {
        TResourceArray<FTextureVertex, VERTEXBUFFER_ALIGNMENT> Vertices;
        Vertices.SetNumUninitialized(6);

        Vertices[0].Position = FVector4(1, 1, 0, 1);
        Vertices[0].UV = FVector2D(1, 1);

        Vertices[1].Position = FVector4(-1, 1, 0, 1);
        Vertices[1].UV = FVector2D(0, 1);

        Vertices[2].Position = FVector4(1, -1, 0, 1);
        Vertices[2].UV = FVector2D(1, 0);

        Vertices[3].Position = FVector4(-1, -1, 0, 1);
        Vertices[3].UV = FVector2D(0, 0);

        //The final two vertices are used for the triangle optimization (a single triangle spans the entire viewport )
        Vertices[4].Position = FVector4(-1, 1, 0, 1);
        Vertices[4].UV = FVector2D(-1, 1);

        Vertices[5].Position = FVector4(1, -1, 0, 1);
        Vertices[5].UV = FVector2D(1, -1);

        // Create vertex buffer. Fill buffer with initial data upon creation
        FRHIResourceCreateInfo CreateInfo(&Vertices);
        VertexBufferRHI = RHICreateVertexBuffer(Vertices.GetResourceDataSize(), BUF_Static, CreateInfo);
    }
};

class FRectangleIndexBuffer : public FIndexBuffer
{
public:
    /** Initialize the RHI for this rendering resource */
    void InitRHI() override 
    {
        // Indices 0 - 5 are used for rendering a quad. Indices 6 - 8 are used for triangle optimization.
        const uint16 Indices[] = { 0, 1, 2, 2, 1, 3, 0, 4, 5 };

        TResourceArray<uint16, INDEXBUFFER_ALIGNMENT> IndexBuffer;
        uint32 NumIndices = ARRAY_COUNT(Indices);
        IndexBuffer.AddUninitialized(NumIndices);
        FMemory::Memcpy(IndexBuffer.GetData(), Indices, NumIndices * sizeof(uint16));

        // Create index buffer. Fill buffer with initial data upon creation
        FRHIResourceCreateInfo CreateInfo(&IndexBuffer);
        IndexBufferRHI = RHICreateIndexBuffer(sizeof(uint16), IndexBuffer.GetResourceDataSize(), BUF_Static, CreateInfo);
    }
};

这里我参考了

Engine\Source\Runtime\RenderCore\Private\CommonRenderResources.cpp
Engine\Source\Runtime\RenderCore\Private\PixelShaderUtils.cpp

其中PixelShaderUtils.cpp提供了绘制图元代码。
如果以前有学习过OpenGL或者Directx3D的,对这些就不会陌生。

使用以上声明

这些将会在XXXX_RenderThread渲染函数中使用:

声明的顶点格式会在设置显卡管线状态中使用

FTextureVertexDeclaration VertexDeclaration;
VertexDeclaration.InitRHI();

//设置显卡管线状态
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = VertexDeclaration.VertexDeclarationRHI;

声明的顶点缓存与顶点索引缓存将会在绘制时使用

RHICmdList.SetStreamSource(0, GRectangleVertexBuffer.VertexBufferRHI, 0);

RHICmdList.DrawIndexedPrimitive(
    GRectangleIndexBuffer.IndexBufferRHI,
    /*BaseVertexIndex=*/ 0,
    /*MinIndex=*/ 0,
    /*NumVertices=*/ 4,
    /*StartIndex=*/ 0,
    /*NumPrimitives=*/ 2,
    /*NumInstances=*/ 1);

贴图

贴图和基础类型数据相似。

  1. 在你声明的Shader类中添加2个FShaderResourceParameter成员变量,分别对应了贴图对象与采样器。
  2. 在序列化函数中添加这两个成员变量。
  3. 在构造函数中绑定这两个成员变量。

使用贴图

  1. 在自定义的Shader变量设置函数中添加const FTextureRHIParamRef&形参,并在函数调用SetTextureParameter函数。
  2. 在XXX_RenderThread渲染函数中添加const FTextureRHIParamRef TextureRHI形参。
  3. 编写蓝图库函数用于渲染测试。

步骤1

template<typename TShaderRHIParamRef>
void SetParameters(FRHICommandListImmediate& RHICmdList,const TShaderRHIParamRef ShaderRHI,    const FLinearColor &MyColor,const FTextureRHIParamRef& TextureRHI)
{
    SetShaderValue(RHICmdList, ShaderRHI, SimpleColorVal, MyColor); 
    SetTextureParameter(RHICmdList, ShaderRHI, TextureVal, TextureSampler,TStaticSamplerState<SF_Trilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),TextureRHI);
}

步骤2

static void DrawTestShaderRenderTarget_RenderThread(  
    FRHICommandListImmediate& RHICmdList,   
    FTextureRenderTargetResource* OutputRenderTargetResource,  
    ERHIFeatureLevel::Type FeatureLevel,  
//    const FName TextureRenderTargetName,  
    const FLinearColor MyColor,
    const FTextureRHIParamRef TextureRHI,
    const FSimpleUniformStruct UniformStruct
)  
{
    //上略
    VertexShader->SetParameters(RHICmdList, VertexShader->GetVertexShader(),MyColor,TextureRHI);
    PixelShader->SetParameters(RHICmdList, PixelShader->GetPixelShader(),MyColor,TextureRHI);
    //下略
}
步骤3
void USimplePixelShaderBlueprintLibrary::DrawTestShaderRenderTarget(const UObject* WorldContextObject, UTextureRenderTarget2D* OutputRenderTarget, FLinearColor MyColor, UTexture* MyTexture, FSimpleUniformStruct UniformStruct)
{  
    check(IsInGameThread());  

    if (!OutputRenderTarget)  
    {  
        return;  
    }  

    FTextureRenderTargetResource* TextureRenderTargetResource = OutputRenderTarget->GameThread_GetRenderTargetResource();  
    FTextureRHIParamRef TextureRHI = MyTexture->TextureReference.TextureReferenceRHI;
    const UWorld* World = WorldContextObject->GetWorld();
    ERHIFeatureLevel::Type FeatureLevel = World->Scene->GetFeatureLevel();  
  /*  FName TextureRenderTargetName = OutputRenderTarget->GetFName(); */ 
    ENQUEUE_RENDER_COMMAND(CaptureCommand)(  
        [TextureRenderTargetResource, FeatureLevel, MyColor,TextureRHI, UniformStruct](FRHICommandListImmediate& RHICmdList)
        {  
            DrawTestShaderRenderTarget_RenderThread(RHICmdList,TextureRenderTargetResource, FeatureLevel, MyColor,TextureRHI, UniformStruct);
        }  
    );  
}

Struct

结构体除了基础类型还可以含有贴图等类型,它是通过宏来声明的,具体的可以通过搜索宏来查看使用方式。这些宏都位于ShaderParameterMacros.h中。

//开始声明,第一种较为常用的,第二种我没研究过
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT_WITH_CONSTRUCTOR

SHADER_PARAMETER            //普通类型变量
SHADER_PARAMETER_TEXTURE    //贴图对象
SHADER_PARAMETER_SAMPLER    //贴图采样器
SHADER_PARAMETER_EX         //矩阵
SHADER_PARAMETER_SRV        //没研究过
SHADER_PARAMETER_ARRAY_EX   //没研究过

//结束声明
END_GLOBAL_SHADER_PARAMETER_STRUCT()

//向Ue4与usf注册
//这里我再cpp中声明了FSimpleUniformStructParameters类型;在usf中声明了SimpleUniformStruct
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FSimpleUniformStructParameters, "SimpleUniformStruct");

声明并且注册完后,结构体会生成到Common.usf中,所以我们需要在自己的usf文件中包含Common.ush。

例如我这么声明:

BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FSimpleUniformStructParameters, )
SHADER_PARAMETER(FVector4, Color1)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FSimpleUniformStructParameters, "SimpleUniformStruct");

那么在usf中,我可以通过SimpleUniformStruct.Color1来取得变量。

在渲染函数中设置Struct的值

这里的FSimpleUniformStruct是我定义的一个结构体,用于在蓝图传参给渲染函数使用。这样我们就可以通过蓝图来控制usf的结构体值了。

static void DrawTestShaderRenderTarget_RenderThread(  
    FRHICommandListImmediate& RHICmdList,   
    FTextureRenderTargetResource* OutputRenderTargetResource, 
    ERHIFeatureLevel::Type FeatureLevel,  
    const FLinearColor MyColor,
    const FTextureRHIParamRef TextureRHI,
    const FSimpleUniformStruct UniformStruct
)  
{
    //上略
    FSimpleUniformStructParameters Parameters;
    Parameters.Color1 = UniformStruct.Color1;
    Parameters.Color2 = UniformStruct.Color2;
    Parameters.Color3 = UniformStruct.Color3;
    Parameters.Color4 = UniformStruct.Color4;
    Parameters.ColorIndex= UniformStruct.ColorIndex;

    SetUniformBufferParameterImmediate(RHICmdList, PixelShader->GetPixelShader(), PixelShader->GetUniformBufferParameter<FSimpleUniformStructParameters>(), Parameters);
    //下略

设置Struct也有两个函数SetUniformBufferParameter与SetUniformBufferParameterImmediate,但是后者我没有仔细看过。