249 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			249 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								## 前言
							 | 
						|||
| 
								 | 
							
								UE4 RDG(RenderDependencyGraph)渲染框架本质上是在原有渲染框架上基础上进行再次封装,它主要的设计目的就为了更好的管理每个资源的生命周期。同时Ue4的渲染管线已经都替换成了RDG框架了(但依然有很多非重要模块以及第三方插件没有替换),所以掌握以下RDG框架还是十分有必要的。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								上一篇文章已经大致介绍了RDG框架的使用方法。看了前文的资料与官方的ppt,再看一下渲染管线的的代码基本就可以上手了写Shader。但作为一个工作与Ue4一点关系的业余爱好者,用的2014年的电脑通过修改渲染管线的方式来写Shader不太现实,编译一次3小时真心伤不起。同时google与Epic论坛也没有在插件中使用RDG的资料,所以我就花了些时间探索了一下用法,最后写了本文。**因为非全职开发UE4,时间精力有限,不可避免得会有些错误,还请见谅。** 代码写在我的插件里,如果感觉有用麻烦Star一下。位于在Rendering下的SimpleRDG.cpp与SimpleRenderingExample.h中。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								[https://github.com/blueroseslol/BRPlugins](https://github.com/blueroseslol/BRPlugins)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								首先还是从ComputeShader开始,因为比较简单。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 参考文件
							 | 
						|||
| 
								 | 
							
								下面推荐几个参考文件,强烈推荐看GenerateMips,包含RDG Compute与GlobalShader两个案例。
							 | 
						|||
| 
								 | 
							
								- GenerateMips.cpp
							 | 
						|||
| 
								 | 
							
								- ShaderPrint.cpp
							 | 
						|||
| 
								 | 
							
								- PostProcessCombineLUTs.cpp
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								搜索ERDGPassFlags::Compute就可以找到RDG调用ComputeShader的代码。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## RDG的数据导入与导出
							 | 
						|||
| 
								 | 
							
								RDG需要使用RegisterExternalBuffer/Texture导入数据;GraphBuilder.QueueExternalBuffer/Texture取出渲染结果,这需要使用一个TRefCountPtr<IPooledRenderTarget>对象。直接使用RHIImmCmdList.CopyTexture尝试将FRDGTextureRef的数据拷贝出来时会触发禁止访问的断言。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## UAV
							 | 
						|||
| 
								 | 
							
								UAV用于保存ComputeShader的计算结果,它的创建步骤如下:
							 | 
						|||
| 
								 | 
							
								### 实现使用宏声明Shader变量
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								SHADER_PARAMETER_UAV(Texture2D, MyUAV)
							 | 
						|||
| 
								 | 
							
								SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<float4>, MyUAV)
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### 使用对应的函数创建并且绑定到对应Shader变量上
							 | 
						|||
| 
								 | 
							
								SHADER_PARAMETER_UAV对应CreateTexture(),SHADER_PARAMETER_RDG_BUFFER_UAV对应CreateUAV()。(前者没试过)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								可以使用FRDGTextureUAVDesc与Buff两种方式进行创建。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## SRV创建与使用
							 | 
						|||
| 
								 | 
							
								UAV是不能直接在Shader里读取,所以需要通过创建SRV的方式来读取。因为我并没有测试SRV,所以这里贴一下FGenerateMips中的部分代码:
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								TSharedPtr<FGenerateMipsStruct> FGenerateMips::SetupTexture(FRHITexture* InTexture, const FGenerateMipsParams& InParams)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								check(InTexture->GetTexture2D());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TSharedPtr<FGenerateMipsStruct> GenMipsStruct = MakeShareable(new FGenerateMipsStruct());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										FPooledRenderTargetDesc Desc;
							 | 
						|||
| 
								 | 
							
										Desc.Extent.X = InTexture->GetSizeXYZ().X;
							 | 
						|||
| 
								 | 
							
										Desc.Extent.Y = InTexture->GetSizeXYZ().Y;
							 | 
						|||
| 
								 | 
							
										Desc.TargetableFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV;
							 | 
						|||
| 
								 | 
							
										Desc.Format = InTexture->GetFormat();
							 | 
						|||
| 
								 | 
							
										Desc.NumMips = InTexture->GetNumMips();;
							 | 
						|||
| 
								 | 
							
										Desc.DebugName = TEXT("GenerateMipPooledRTTexture");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										//Create the Pooled Render Target Resource from the input texture
							 | 
						|||
| 
								 | 
							
										FRHIResourceCreateInfo CreateInfo(Desc.DebugName);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										//Initialise a new render target texture for creating an RDG Texture
							 | 
						|||
| 
								 | 
							
										FSceneRenderTargetItem RenderTexture;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										//Update all the RenderTexture info
							 | 
						|||
| 
								 | 
							
										RenderTexture.TargetableTexture = InTexture;
							 | 
						|||
| 
								 | 
							
										RenderTexture.ShaderResourceTexture = InTexture;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										RenderTexture.SRVs.Empty(Desc.NumMips);
							 | 
						|||
| 
								 | 
							
										RenderTexture.MipUAVs.Empty(Desc.NumMips);
							 | 
						|||
| 
								 | 
							
										for (uint8 MipLevel = 0; MipLevel < Desc.NumMips; MipLevel++)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											FRHITextureSRVCreateInfo SRVDesc;
							 | 
						|||
| 
								 | 
							
											SRVDesc.MipLevel = MipLevel;
							 | 
						|||
| 
								 | 
							
											RenderTexture.SRVs.Emplace(SRVDesc, RHICreateShaderResourceView((FTexture2DRHIRef&)InTexture, SRVDesc));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											RenderTexture.MipUAVs.Add(RHICreateUnorderedAccessView(InTexture, MipLevel));
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										RHIBindDebugLabelName(RenderTexture.TargetableTexture, Desc.DebugName);
							 | 
						|||
| 
								 | 
							
										RenderTexture.UAV = RenderTexture.MipUAVs[0];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										//Create the RenderTarget from the PooledRenderTarget Desc and the new RenderTexture object.
							 | 
						|||
| 
								 | 
							
									GRenderTargetPool.CreateUntrackedElement(Desc, GenMipsStruct->RenderTarget, RenderTexture);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										//Specify the Sampler details based on the input.
							 | 
						|||
| 
								 | 
							
									GenMipsStruct->Sampler.Filter = InParams.Filter;
							 | 
						|||
| 
								 | 
							
									GenMipsStruct->Sampler.AddressU = InParams.AddressU;
							 | 
						|||
| 
								 | 
							
									GenMipsStruct->Sampler.AddressV = InParams.AddressV;
							 | 
						|||
| 
								 | 
							
									GenMipsStruct->Sampler.AddressW = InParams.AddressW;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return GenMipsStruct;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FGenerateMips::Compute(FRHICommandListImmediate& RHIImmCmdList, FRHITexture* InTexture, TSharedPtr<FGenerateMipsStruct> GenMipsStruct)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    check(IsInRenderingThread());	
							 | 
						|||
| 
								 | 
							
									//Currently only 2D textures supported
							 | 
						|||
| 
								 | 
							
									check(InTexture->GetTexture2D());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//Ensure the generate mips structure has been initialised correctly.
							 | 
						|||
| 
								 | 
							
									check(GenMipsStruct);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//Begin rendergraph for executing the compute shader
							 | 
						|||
| 
								 | 
							
									FRDGBuilder GraphBuilder(RHIImmCmdList);
							 | 
						|||
| 
								 | 
							
									FRDGTextureRef GraphTexture = GraphBuilder.RegisterExternalTexture(GenMipsStruct->RenderTarget, TEXT("GenerateMipsGraphTexture"));
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									···
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									FRDGTextureSRVDesc SRVDesc = FRDGTextureSRVDesc::CreateForMipLevel(GraphTexture, MipLevel - 1);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FGenerateMipsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FGenerateMipsCS::FParameters>();
							 | 
						|||
| 
								 | 
							
									PassParameters->MipInSRV = GraphBuilder.CreateSRV(SRVDesc);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								可以看出是先通过CreateUntrackedElement()创建IPooledRenderTarget,之后再调用RegisterExternalTexture进行注册,最后再调用CreateSRV创建SRV。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								另外IPooledRenderTarget除了有CreateUntrackedElement(),还有FindFreeElement()。这个函数就适合在多Pass RDG中使用了。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								FRDGTextureRef GraphTexture = GraphBuilder.RegisterExternalTexture(GenMipsStruct->RenderTarget, TEXT("GenerateMipsGraphTexture"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								FRDGTextureSRVDesc SRVDesc = FRDGTextureSRVDesc::CreateForMipLevel(GraphTexture, MipLevel - 1);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								FGenerateMipsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FGenerateMipsCS::FParameters>();
							 | 
						|||
| 
								 | 
							
								PassParameters->MipInSRV = GraphBuilder.CreateSRV(SRVDesc);
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								### 在Shader中读取SRV
							 | 
						|||
| 
								 | 
							
								读取SRV与读取Texture相同,首先需要创建采样器:
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								SHADER_PARAMETER_SAMPLER(SamplerState, MipSampler)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								PassParameters->MipSampler = RHIImmCmdList.CreateSamplerState(GenMipsStruct->Sampler);
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								之后就可以想Texture2D那样进行取值了:
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								#pragma once
							 | 
						|||
| 
								 | 
							
								#include "Common.ush"
							 | 
						|||
| 
								 | 
							
								#include "GammaCorrectionCommon.ush"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								float2 TexelSize;
							 | 
						|||
| 
								 | 
							
								Texture2D MipInSRV;
							 | 
						|||
| 
								 | 
							
								#if GENMIPS_SRGB
							 | 
						|||
| 
								 | 
							
								RWTexture2D<half4> MipOutUAV;
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
								RWTexture2D<float4> MipOutUAV;
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								SamplerState MipSampler;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								[numthreads(8, 8, 1)]
							 | 
						|||
| 
								 | 
							
								void MainCS(uint3 DT_ID : SV_DispatchThreadID)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    float2 UV = TexelSize * (DT_ID.xy + 0.5f);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if GENMIPS_SRGB
							 | 
						|||
| 
								 | 
							
								    half4 outColor = MipInSRV.SampleLevel(MipSampler, UV, 0);
							 | 
						|||
| 
								 | 
							
								    outColor = half4(LinearToSrgb(outColor.xyz), outColor.w);
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
								    float4 outColor = MipInSRV.SampleLevel(MipSampler, UV, 0);
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if GENMIPS_SWIZZLE
							 | 
						|||
| 
								 | 
							
								    MipOutUAV[DT_ID.xy] = outColor.zyxw;
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
								    MipOutUAV[DT_ID.xy] = outColor;
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## RDGCompute完整代码
							 | 
						|||
| 
								 | 
							
								以下就是我写的例子,因为比较简单而且有注释,所以就不详细解释了。
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								void RDGCompute(FRHICommandListImmediate &RHIImmCmdList, FTexture2DRHIRef RenderTargetRHI, FSimpleShaderParameter InParameter)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									check(IsInRenderingThread());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//Create PooledRenderTarget
							 | 
						|||
| 
								 | 
							
									FPooledRenderTargetDesc RenderTargetDesc = FPooledRenderTargetDesc::Create2DDesc(RenderTargetRHI->GetSizeXY(),RenderTargetRHI->GetFormat(), FClearValueBinding::Black, TexCreate_None, TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV, false);
							 | 
						|||
| 
								 | 
							
									TRefCountPtr<IPooledRenderTarget> PooledRenderTarget;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//RDG Begin
							 | 
						|||
| 
								 | 
							
									FRDGBuilder GraphBuilder(RHIImmCmdList);
							 | 
						|||
| 
								 | 
							
									FRDGTextureRef RDGRenderTarget = GraphBuilder.CreateTexture(RenderTargetDesc, TEXT("RDGRenderTarget"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//Setup Parameters
							 | 
						|||
| 
								 | 
							
									FSimpleUniformStructParameters StructParameters;
							 | 
						|||
| 
								 | 
							
									StructParameters.Color1 = InParameter.Color1;
							 | 
						|||
| 
								 | 
							
									StructParameters.Color2 = InParameter.Color2;
							 | 
						|||
| 
								 | 
							
									StructParameters.Color3 = InParameter.Color3;
							 | 
						|||
| 
								 | 
							
									StructParameters.Color4 = InParameter.Color4;
							 | 
						|||
| 
								 | 
							
									StructParameters.ColorIndex = InParameter.ColorIndex;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FSimpleRDGComputeShader::FParameters *Parameters = GraphBuilder.AllocParameters<FSimpleRDGComputeShader::FParameters>();
							 | 
						|||
| 
								 | 
							
									FRDGTextureUAVDesc UAVDesc(RDGRenderTarget);
							 | 
						|||
| 
								 | 
							
									Parameters->SimpleUniformStruct = TUniformBufferRef<FSimpleUniformStructParameters>::CreateUniformBufferImmediate(StructParameters, UniformBuffer_SingleFrame);
							 | 
						|||
| 
								 | 
							
									Parameters->OutTexture = GraphBuilder.CreateUAV(UAVDesc);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//Get ComputeShader From GlobalShaderMap
							 | 
						|||
| 
								 | 
							
									const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; //ERHIFeatureLevel::SM5
							 | 
						|||
| 
								 | 
							
									FGlobalShaderMap *GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
							 | 
						|||
| 
								 | 
							
									TShaderMapRef<FSimpleRDGComputeShader> ComputeShader(GlobalShaderMap);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//Compute Thread Group Count
							 | 
						|||
| 
								 | 
							
									FIntVector ThreadGroupCount(
							 | 
						|||
| 
								 | 
							
										RenderTargetRHI->GetSizeX() / 32,
							 | 
						|||
| 
								 | 
							
										RenderTargetRHI->GetSizeY() / 32,
							 | 
						|||
| 
								 | 
							
										1);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//ValidateShaderParameters(PixelShader, Parameters);
							 | 
						|||
| 
								 | 
							
									//ClearUnusedGraphResources(PixelShader, Parameters);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									GraphBuilder.AddPass(
							 | 
						|||
| 
								 | 
							
										RDG_EVENT_NAME("RDGCompute"),
							 | 
						|||
| 
								 | 
							
										Parameters,
							 | 
						|||
| 
								 | 
							
										ERDGPassFlags::Compute,
							 | 
						|||
| 
								 | 
							
										[Parameters, ComputeShader, ThreadGroupCount](FRHICommandList &RHICmdList) {
							 | 
						|||
| 
								 | 
							
											FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *Parameters, ThreadGroupCount);
							 | 
						|||
| 
								 | 
							
										});
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									GraphBuilder.QueueTextureExtraction(RDGRenderTarget, &PooledRenderTarget);
							 | 
						|||
| 
								 | 
							
									GraphBuilder.Execute();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									//Copy Result To RenderTarget Asset
							 | 
						|||
| 
								 | 
							
									RHIImmCmdList.CopyTexture(PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture, RenderTargetRHI->GetTexture2D(), FRHICopyTextureInfo());
							 | 
						|||
| 
								 | 
							
									//RHIImmCmdList.CopyToResolveTarget(PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture, RenderTargetRHI->GetTexture2D(), FResolveParams());
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								## 调用绘制函数
							 | 
						|||
| 
								 | 
							
								与传统方法类似,调用上述渲染函数时需要使用ENQUEUE_RENDER_COMMAND(CaptureCommand)[]。下面是我写在蓝图函数库的代码。
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								void USimpleRenderingExampleBlueprintLibrary::UseRDGComput(const UObject *WorldContextObject, UTextureRenderTarget2D *OutputRenderTarget, FSimpleShaderParameter Parameter)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									check(IsInGameThread());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FTexture2DRHIRef RenderTargetRHI = OutputRenderTarget->GameThread_GetRenderTargetResource()->GetRenderTargetTexture();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									ENQUEUE_RENDER_COMMAND(CaptureCommand)
							 | 
						|||
| 
								 | 
							
									(
							 | 
						|||
| 
								 | 
							
										[RenderTargetRHI, Parameter](FRHICommandListImmediate &RHICmdList) {
							 | 
						|||
| 
								 | 
							
											SimpleRenderingExample::RDGCompute(RHICmdList, RenderTargetRHI, Parameter);
							 | 
						|||
| 
								 | 
							
										});
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 如何使用
							 | 
						|||
| 
								 | 
							
								直接在蓝图中调用即可
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								注意RenderTarget的格式需要与UAV的格式一一对应。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								结果:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 |