(UE4 4.27)插件添加GlobalShder

前言

在上一节我们大致介绍了UE4 GlobalShader的情况, 并且在引擎代码做了一些修改来阐述GlobalShader的声明-定义-使用。实际上在UE4开发中, GlobalShader不一定使用在修改引擎代码,也可能是用在插件, 比如LensDistortion插件就是使用GlobalShader定制一个RenderToTexture的功能。当然在插件中,GlobalShader的使用场景不仅仅局限于RenderToTexture, 可以ComputeShader等等来加速某些并行计算(如基于GPU的植被插件等)。(UE4 4.27)自定义GlobalShadericon-default.png?t=L9C2https://blog.csdn.net/qq_29523119/article/details/120573174?spm=1001.2014.3001.5501

GlobalShader插件声明步骤

功能

下面做一个对图片进行调色并渲染到RenderTexture的功能

(UE4 4.27)插件添加GlobalShder_第1张图片

声明定义GlobalShader

#pragma once
#include "CoreMinimal.h"
#include "GlobalShader.h"
#include "ShaderParameters.h"
#include "Shader.h"

/**
 * 
 */
class FDrawVS : public FGlobalShader
{
	DECLARE_SHADER_TYPE(FDrawVS, Global);

public:
	FDrawVS() {};
	FDrawVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
		:FGlobalShader(Initializer)
	{

	}

	static bool ShouldCompilePermutation(const FShaderPermutationParameters& Parameters)
	{
		return true;
	}
};

class FDrawPS : public FGlobalShader
{
	DECLARE_SHADER_TYPE(FDrawPS, Global);

public:
	FDrawPS() {};
	FDrawPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
		:FGlobalShader(Initializer)
	{
		Color.Bind(Initializer.ParameterMap, TEXT("MyColor"));
		ColorScale.Bind(Initializer.ParameterMap, TEXT("MyColorScale"));
		Texture.Bind(Initializer.ParameterMap, TEXT("InTexture"));
		TextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
	}

	/** Should the shader be cached? Always. */
	static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
	{
		return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
	}

	void SetParam(FRHICommandList& RHICmdList, const FTexture* TextureValue, const FLinearColor& InColor, float InColorScale)
	{
		FRHIPixelShader* PS = RHICmdList.GetBoundPixelShader();
		SetTextureParameter(RHICmdList, PS, Texture, TextureSampler, TextureValue);
		SetShaderValue(RHICmdList, PS, Color, InColor);
		SetShaderValue(RHICmdList, PS, ColorScale, InColorScale);
	}

private:
	LAYOUT_FIELD(FShaderParameter, Color);
	LAYOUT_FIELD(FShaderParameter, ColorScale);
	LAYOUT_FIELD(FShaderResourceParameter, Texture);
	LAYOUT_FIELD(FShaderResourceParameter, TextureSampler);
};

重定向Shader路径和Shader代码

void FTestShader::StartupModule()
{
	FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("TestShader"))->GetBaseDir(), TEXT("Shaders"));
	AddShaderSourceDirectoryMapping(TEXT("/Plugin/TestShader"), PluginShaderDir);
}
IMPLEMENT_SHADER_TYPE(, FDrawVS, TEXT("/Plugin/TestShader/DrawShader.usf"), TEXT("MainVS"), SF_Vertex);
IMPLEMENT_SHADER_TYPE(, FDrawPS, TEXT("/Plugin/TestShader/DrawShader.usf"), TEXT("MainPS"), SF_Pixel);

DrawShader.usf


#include "/Engine/Public/Platform.ush"

void MainVS(
	in float3 InPostion : ATTRIBUTE0, 
	in float2 UV : ATTRIBUTE1,
	out float2 OutUV : TEXCOORD0,
	out float4 Output : SV_POSITION)
{
	OutUV = UV;
    Output = float4(InPostion, 1.0f);
}

Texture2D InTexture;
SamplerState InTextureSampler;

float4 MyColor;
float MyColorScale;
void MainPS(
	in float2 UV : TEXCOORD0,
	out float4 OutColor : SV_Target0)
{
	float4 TextureColor = InTexture.Sample(InTextureSampler, UV);
    OutColor = MyColor * TextureColor * MyColorScale;
	//return float4(1.0f, 1.0f, 1.0f, 1.0f);
}

蓝图库静态RT函数

void UMyShaderBPFunctionLibrary::DrawTestShaderToRenderTexture(
	const UObject* WorldContextObject,
	const UTexture2D* InTexture,
	class UTextureRenderTarget2D* OutputRenderTarget,
	const FLinearColor& Color,
	float ColorScale)
{
	UWorld* World = WorldContextObject->GetWorld();
	ERHIFeatureLevel::Type FeatureLevel = World->Scene->GetFeatureLevel();

	if (FeatureLevel < ERHIFeatureLevel::SM5)
	{
		UE_LOG(LogTemp, Warning, TEXT("FeatureLevel < ERHIFeatureLevel::SM5"));
		return;
	}

	if (nullptr == InTexture)
	{
		UE_LOG(LogTemp, Warning, TEXT("InTexture Is NULL"));
		return;
	}

	FTextureRenderTargetResource* TextureRenderTargetSource = OutputRenderTarget->GameThread_GetRenderTargetResource();
	FTexture* TextureSource = InTexture->Resource;

	ENQUEUE_RENDER_COMMAND(TestShaderCommand)(
		[TextureSource, TextureRenderTargetSource, FeatureLevel, Color, ColorScale](FRHICommandListImmediate& RHICmdList)
		{
			DrawTestShaderToRenderTexture_RenderThread(RHICmdList, TextureSource, TextureRenderTargetSource, FeatureLevel, Color, ColorScale);
		}
	);
}

这里 ENQUEUE_RENDER_COMMAND 表示从游戏线程向渲染线程发送渲染指令, 而FRHICommandListImmediate和上一节的FRHICommandList也有所不同,FRHICommandListImmediate代表的立即模式,代表发送到渲染线程后马上进行渲染,就能得到渲染结果。而FRHICommandList存在渲染指令的队列,会进行指令收集在进行延后的渲染有所不同。

顶点数据结构和VertexFormat声明

struct FCustomVertex
{
	FVector Pos;
	FVector2D UV;

	FCustomVertex()
	{
	}

	FCustomVertex(const FVector& VertexPos, const FVector2D& VertexUV)
		:Pos(VertexPos), UV(VertexUV)
	{
	}
};

//declare custom vertex format
class FMyTestVertexDeclaration : public FRenderResource
{
public:
	FVertexDeclarationRHIRef VertexDeclarationRHI;

	virtual void InitRHI() override
	{
		FVertexDeclarationElementList Elements;
		uint16 Stride = sizeof(FCustomVertex);

		//Pos-float3
		Elements.Add(FVertexElement(0, STRUCT_OFFSET(FCustomVertex, Pos), VET_Float3, 0, Stride));

		//UV-float2
		Elements.Add(FVertexElement(0, STRUCT_OFFSET(FCustomVertex, UV), VET_Float2, 1, Stride));
		VertexDeclarationRHI = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
	}

	virtual void ReleaseRHI() override
	{
		VertexDeclarationRHI.SafeRelease();
	}
};

TGlobalResource GCustomVertexDeclaration;

渲染数据准备, Draw

static void DrawTestShaderToRenderTexture_RenderThread(
	FRHICommandListImmediate& RHICmdList,
	FTexture* InTextureResource,
	FTextureRenderTargetResource* OutTextureRenderTargetResource,
	ERHIFeatureLevel::Type FeatureLevel,
	const FLinearColor& Color,
	float ColorScale)
{
	check(IsInRenderingThread());

	FRHITexture2D* RenderTargetTexture = OutTextureRenderTargetResource->GetRenderTargetTexture();
	RHICmdList.Transition(FRHITransitionInfo(RenderTargetTexture, ERHIAccess::SRVMask, ERHIAccess::RTV));

	FRHIRenderPassInfo RPInfo(RenderTargetTexture, ERenderTargetActions::DontLoad_Store);
	RHICmdList.BeginRenderPass(RPInfo, TEXT("Render Test Shader To Texture"));
	{
		FIntPoint ViewportSize(OutTextureRenderTargetResource->GetSizeX(), OutTextureRenderTargetResource->GetSizeY());
	
		// Upate viewport
		RHICmdList.SetViewport(0, 0, 0.0f, ViewportSize.X, ViewportSize.Y, 1.0f);

		// Get Shaders
		FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
		TShaderMapRef VertexShader(GlobalShaderMap);
		TShaderMapRef PixelShader(GlobalShaderMap);

		// Setup Pipeline state
		FGraphicsPipelineStateInitializer GraphicsPSOInit;
		RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
		GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI();
		GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
		GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
		GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
		GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GCustomVertexDeclaration.VertexDeclarationRHI;
		GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
		GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
		SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);

		// update shader uniform parameters
		PixelShader->SetParam(RHICmdList, InTextureResource, Color, ColorScale);

		// Create VertexBuffer and setup
		static const uint32 VERTEX_SIZE = sizeof(FCustomVertex) * 4;
		FRHIResourceCreateInfo CreateInfo;
		FVertexBufferRHIRef VertexBufferRHI = RHICreateVertexBuffer(VERTEX_SIZE, BUF_Static, CreateInfo);
		void* VoidPtr = RHILockVertexBuffer(VertexBufferRHI, 0, VERTEX_SIZE, RLM_WriteOnly);

		FCustomVertex Vertices[4];
		Vertices[0] = FCustomVertex(FVector(-1.0f, 1.0f, 0.0f), FVector2D(0.0f, 0.0f));
		Vertices[1] = FCustomVertex(FVector(1.0f, 1.0f, 0), FVector2D(1.0f, 0.0f));
		Vertices[2] = FCustomVertex(FVector(-1.0f, -1.0f, 0), FVector2D(0.0f, 1.0f));
		Vertices[3] = FCustomVertex(FVector(1.0f, -1.0f, 0), FVector2D(1.0f, 1.0f));
		FMemory::Memcpy(VoidPtr, (void*)Vertices, VERTEX_SIZE);
		RHIUnlockVertexBuffer(VertexBufferRHI);

		RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
		RHICmdList.DrawPrimitive(0, 2, 1);
	}
	RHICmdList.EndRenderPass();

	RHICmdList.Transition(FRHITransitionInfo(RenderTargetTexture, ERHIAccess::RTV, ERHIAccess::SRVMask));
}

 结果显示

(UE4 4.27)插件添加GlobalShder_第2张图片

(UE4 4.27)插件添加GlobalShder_第3张图片

总结 

总体而言各种渲染数据的声明定义和Directx11非常类似.

插件代码链接

https://download.csdn.net/download/qq_29523119/33147630

资料参考

【1】https://docs.unrealengine.com/4.27/en-US/ProgrammingAndScripting/Rendering/ShaderInPlugin/Overview/

【2】UE4的LensDistortion插件和Texture2DPreview.cpp

你可能感兴趣的:(C++,UE4,Rendering入门,ue4)