Advertisement

UE4 添加computeshader

阅读量:

参考这篇文章。笔者在开发时,犯了个错误:在UseComputeShader_RenderThread函数中,得到ComputeShader之后,要把这个computeshader设置给RHICmdList,否则下面DispathComputeSHader执行的时候会找不到computeShader(该指针为空)。

复制代码
    static void UseComputeShader_RenderThread(
    	FRHICommandListImmediate& RHICmdList,
    	FTextureRenderTargetResource* OutputRenderTargetResource,
    	ERHIFeatureLevel::Type FeatureLevel
    )
    {
    	check(IsInRenderingThread());
    	TShaderMap<FGlobalShaderType>* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
    	TShaderMapRef<FShaderTestCS> ComputeShader(GlobalShaderMap);
    	RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
    
    	int32 SizeX = OutputRenderTargetResource->GetSizeX();
    	int32 SizeY = OutputRenderTargetResource->GetSizeY();
    	FRHIResourceCreateInfo CreateInfo;
    	FTexture2DRHIRef Texture = RHICreateTexture2D(SizeX, SizeY, PF_R32_UINT, 1, 1, TexCreate_ShaderResource | TexCreate_UAV, CreateInfo);
    	//OutputRenderTargetResource->UA  
    	FUnorderedAccessViewRHIRef TextureUAV = RHICreateUnorderedAccessView(Texture);
    	ComputeShader->SetSurfaces(RHICmdList, TextureUAV);
    
    	DispatchComputeShader(RHICmdList, *ComputeShader, SizeX / 32, SizeY / 32, 1);
    
    	ComputeShader->UnbindBuffers(RHICmdList);
    	//create a bitmap  
    	TArray<FColor> Bitmap;
    
    	//To access our resource we do a custom read using lockrect  
    	uint32 LolStride = 0;
    	char* TextureDataPtr = (char*)RHICmdList.LockTexture2D(Texture, 0, EResourceLockMode::RLM_ReadOnly, LolStride, false);
    
    	for (uint32 Row = 0; Row < Texture->GetSizeY(); ++Row)
    	{
    		uint32* PixelPtr = (uint32*)TextureDataPtr;
    
    		//Since we are using our custom UINT format, we need to unpack it here to access the actual colors  
    		for (uint32 Col = 0; Col < Texture->GetSizeX(); ++Col)
    		{
    			uint32 EncodedPixel = *PixelPtr;
    			uint8 r = (EncodedPixel & 0x000000FF);
    			uint8 g = (EncodedPixel & 0x0000FF00) >> 8;
    			uint8 b = (EncodedPixel & 0x00FF0000) >> 16;
    			uint8 a = (EncodedPixel & 0xFF000000) >> 24;
    			Bitmap.Add(FColor(r, g, b, a));
    
    			PixelPtr++;
    		}
    
    		// move to next row:  
    		TextureDataPtr += LolStride;
    	}
    
    	RHICmdList.UnlockTexture2D(Texture, 0, false);
    
    	// if the format and texture type is supported  
    	if (Bitmap.Num())
    	{
    		// Create screenshot folder if not already present. 
    		const TCHAR* path = *FPaths::ScreenShotDir();
    		IFileManager::Get().MakeDirectory(path, true);
    
    		const FString ScreenFileName(FPaths::ScreenShotDir() / TEXT("VisualizeTexture"));
    
    		uint32 ExtendXWithMSAA = Bitmap.Num() / Texture->GetSizeY();
    
    		// Save the contents of the array to a bitmap file. (24bit only so alpha channel is dropped)  
    		FFileHelper::CreateBitmap(*ScreenFileName, ExtendXWithMSAA, Texture->GetSizeY(), Bitmap.GetData());
    
    		UE_LOG(LogConsoleResponse, Display, TEXT("Content was saved to \"%s\""), *FPaths::ScreenShotDir());
    	}
    	else
    	{
    		UE_LOG(LogConsoleResponse, Error, TEXT("Failed to save BMP, format or texture type is not supported"));
    	}
    }

得到的贴图存储到“const TCHAR* path = *FPaths::ScreenShotDir();
IFileManager::Get().MakeDirectory(path, true);
const FString ScreenFileName(FPaths::ScreenShotDir() / TEXT(“VisualizeTexture”));”路径下,在工程目录下的Saved/ScreenShots/windows/下。
可以参考PostprocessBloomSetup.cpp里的FPostProcessBloomSetupCS类。
computeShader代码如下:

复制代码
    class FShaderTestCS : public FGlobalShader
    {
    	DECLARE_SHADER_TYPE(FShaderTestCS, Global);
    
    	static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
    	{
    		return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
    	}
    
    	static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Platform, FShaderCompilerEnvironment& OutEnvironment)
    	{
    		FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
    	}
    
    	FShaderTestCS() {}
    
    public:
    	FShaderResourceParameter OutputSurface;
    
    	FShaderTestCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
    		: FGlobalShader(Initializer)
    	{
    		OutputSurface.Bind(Initializer.ParameterMap, TEXT("OutputSurface"));
    	}
    
    	virtual bool Serialize(FArchive& Ar) override
    	{
    		bool bShaderHasOutdatedParams = FGlobalShader::Serialize(Ar);
    
    		Ar << OutputSurface;
    
    		return bShaderHasOutdatedParams;
    	}
    
    	void SetSurfaces(FRHICommandList& RHICmdList, FUnorderedAccessViewRHIRef OutputSurfaceUAV)
    	{
    		FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader();
    		RHICmdList.SetUAVParameter(ComputeShaderRHI, OutputSurface.GetBaseIndex(), OutputSurfaceUAV);
    	}
    	void UnbindBuffers(FRHICommandList& RHICmdList)
    	{
    		const FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader();
    
    		RHICmdList.SetUAVParameter(ComputeShaderRHI, OutputSurface.GetBaseIndex(), NULL);
    	}
    
    
    };
    
    IMPLEMENT_SHADER_TYPE(, FShaderTestVS, TEXT("/Plugin/test/Private/MyShader.usf"), TEXT("MainVS"), SF_Vertex)
    IMPLEMENT_SHADER_TYPE(, FShaderTestPS, TEXT("/Plugin/test/Private/MyShader.usf"), TEXT("MainPS"), SF_Pixel)
    IMPLEMENT_SHADER_TYPE(, FShaderTestCS, TEXT("/Plugin/test/Private/MyShader.usf"), TEXT("MainCS"), SF_Compute)

shader代码如下:

复制代码
    RWTexture2D<uint> OutputSurface;
    
    [numthreads(32, 32, 1)]
    void MainCS(uint3 ThreadId : SV_DispatchThreadID)
    {
    	float sizeX, sizeY;
    	OutputSurface.GetDimensions(sizeX, sizeY);
    
    	float2 iResolution = float2(sizeX, sizeY);  
    float2 uv = (ThreadId.xy / iResolution.xy) - 0.5;  
    float iGlobalTime = 1.0f;
    
    	float t = iGlobalTime * 0.1 + ((0.25 + 0.05 * sin(iGlobalTime * 0.1)) / (length(uv.xy) + 0.07)) * 2.2;  
    float si = sin(t);  
    float co = cos(t);  
    float2x2 ma = { co, si, -si, co };  
      
    float v1, v2, v3;  
    v1 = v2 = v3 = 0.0;  
      
    float s = 0.0;  
    for (int i = 0; i < 90; i++)  
    {  
        float3 p = s * float3(uv, 0.0);  
        p.xy = mul(p.xy, ma);  
        p += float3(0.22, 0.3, s - 1.5 - sin(iGlobalTime * 0.13) * 0.1);  
          
        for (int i = 0; i < 8; i++)    
            p = abs(p) / dot(p, p) - 0.659;  
      
        v1 += dot(p, p) * 0.0015 * (1.8 + sin(length(uv.xy * 13.0) + 0.5 - iGlobalTime * 0.2));  
        v2 += dot(p, p) * 0.0013 * (1.5 + sin(length(uv.xy * 14.5) + 1.2 - iGlobalTime * 0.3));  
        v3 += length(p.xy * 10.0) * 0.0003;  
        s += 0.035;  
    }  
      
    float len = length(uv);  
    v1 *= lerp(0.7, 0.0, len);  
    v2 *= lerp(0.5, 0.0, len);  
    v3 *= lerp(0.9, 0.0, len);  
      
    float3 col = float3(v3 * (1.5 + sin(iGlobalTime * 0.2) * 0.4), (v1 + v3) * 0.3, v2)  
                    + lerp(0.2, 0.0, len) * 0.85  
                    + lerp(0.0, 0.6, v3) * 0.3;  
      
    float3 powered = pow(abs(col), float3(1.2, 1.2, 1.2));  
    float3 minimized = min(powered, 1.0);  
    float4 outputColor = float4(minimized, 1.0);  
      
    //Since there are limitations on operations that can be done on certain formats when using compute shaders  
    //I elected to go with the most flexible one (UINT 32bit) and do my packing manually to simulate an R8G8B8A8_UINT format.  
    //There might be better ways to do this :)  
    uint r = outputColor.r * 255.0;  
    uint g = ((uint) (outputColor.g * 255.0)) << 8;  
    uint b = ((uint) (outputColor.b * 255.0)) << 16;  
    uint a = ((uint) (outputColor.a * 255.0)) << 24;  
    OutputSurface[ThreadId.xy] = r | g | b | a;  
    
    }

全部评论 (0)

还没有任何评论哟~