在图形渲染各种算法上,特别是对2D纹理的处理上,经常出现一种情况,我们需要将一张纹理的一部分拷贝到另外一张纹理上。如下所示:
UE4提供了CopyTexture和CopyToResolveTarget来实现拷贝一张纹理一部分到另外一张纹理上的类似功能。
struct FRHICopyTextureInfo
{
// Number of texels to copy. By default it will copy the whole resource if no size is specified.
FIntVector Size = FIntVector::ZeroValue;
// Position of the copy from the source texture/to destination texture
FIntVector SourcePosition = FIntVector::ZeroValue;
FIntVector DestPosition = FIntVector::ZeroValue;
uint32 SourceSliceIndex = 0;
uint32 DestSliceIndex = 0;
uint32 NumSlices = 1;
// Mips to copy and destination mips
uint32 SourceMipIndex = 0;
uint32 DestMipIndex = 0;
uint32 NumMips = 1;
};
CopyTexture的SourceTextureRHI和DestTextureRHI适用于UE4的UTexture2D, UTextureRenderTarget2D, UTexture2DArray等几种纹理类型进行相关拷贝。
FRHICopyTextureInfo包含了关于CopySrcTexture和CopyDestTexture各种设定参数
Size: 拷贝纹理部分的大小
SourcePosition:SrcTexture拷贝部分的起始位置
DestPosition:DestTexture拷贝部分的起始位置
SourceSliceIndex:SrcTexture拷贝的部分所在SrcTexture纹理数组的数组index(普通纹理就是数组大小为1的纹理数组,默认SliceIndex等于0)
DestSliceIndex:DestTexture拷贝的部分所在DestTexture纹理数组的数组index(普通纹理就是数组大小为1的纹理数组,默认SliceIndex 等于0)
SourceMipIndex: SrcTexture拷贝的部分是当前SrcTexture纹理的哪个Mip级
DestMipIndex: DestMipIndex拷贝的部分是当前DestMipIndex纹理的哪个Mip级
NumMips: 这次进行进行N级Mip的拷贝(通常情况下,这个是1,一次一般只拷贝一级Mip)
总结:将SrcTexture数组的第SourceSliceIndex个纹理的第SourceMipIndexMip级的[SourcePosition, SourcePosition+Size]范围拷贝到DestTexture数组的第DestSliceIndex个纹理的第DestMipIndex级的[DestPosition, DestPosition+Size]范围
将一张Texture的每级Mip左上角四分之一拷贝到一张RenderTexture对应Mip级的左上角部分
UFUNCTION(BlueprintCallable, Category = "Test Shader", meta = (WorldContext = "WorldContextObject"))
static void CopyTextureToRenderTextureAllMips(
const UObject* WorldContextObject,
const UTexture2D* InTexture,
class UTextureRenderTarget2D* OutputRenderTarget);
void UTestCopyRenderFunctionLibrary::CopyTextureToRenderTextureAllMips(
const UObject* WorldContextObject,
const UTexture2D* InTexture,
class UTextureRenderTarget2D* OutputRenderTargetA)
{
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 = OutputRenderTargetA->GameThread_GetRenderTargetResource();
FTexture* TextureSource = InTexture->Resource;
int32 CommonMip = FMath::Min(TextureRenderTargetSource->TextureRHI->GetNumMips(), TextureSource->TextureRHI->GetNumMips());
ENQUEUE_RENDER_COMMAND(TestShaderCommand)(
[TextureSource, TextureRenderTargetSource, FeatureLevel, CommonMip](FRHICommandListImmediate& RHICmdList)
{
if (CommonMip >= 2)
{
//Test Copy Subregion
for (int32 MipIndex = 0; MipIndex < CommonMip - 1; ++MipIndex)
{
FRHICopyTextureInfo CopyInfo;
CopyInfo.SourcePosition = FIntVector(0, 0, 0);
CopyInfo.DestPosition = FIntVector(0, 0, 0);
CopyInfo.SourceMipIndex = MipIndex;
CopyInfo.DestMipIndex = MipIndex;
CopyInfo.Size = FIntVector(TextureSource->GetSizeX() / FMath::Pow(2, MipIndex + 1), TextureSource->GetSizeY() / FMath::Pow(2, MipIndex + 1), TextureSource->GetSizeZ() / FMath::Pow(2, MipIndex + 1));
RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::Unknown, ERHIAccess::CopyDest));
RHICmdList.Transition(FRHITransitionInfo(TextureSource->TextureRHI, ERHIAccess::SRVMask, ERHIAccess::CopySrc));
FRHITexture* Test = TextureSource->TextureRHI.GetReference();
RHICmdList.CopyTexture(TextureSource->TextureRHI, TextureRenderTargetSource->TextureRHI, CopyInfo);
RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopySrc, ERHIAccess::SRVMask));
}
}
else
{
FRHICopyTextureInfo CopyInfo;
CopyInfo.SourcePosition = FIntVector(0, 0, 0);
CopyInfo.DestPosition = FIntVector(0, 0, 0);
CopyInfo.Size = FIntVector(TextureSource->GetSizeX() / 2, TextureSource->GetSizeY() /2, TextureSource->GetSizeZ() / 2);
RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::Unknown, ERHIAccess::CopyDest));
RHICmdList.Transition(FRHITransitionInfo(TextureSource->TextureRHI, ERHIAccess::SRVMask, ERHIAccess::CopySrc));
RHICmdList.CopyTexture(TextureSource->TextureRHI, TextureRenderTargetSource->TextureRHI, CopyInfo);
RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopySrc, ERHIAccess::SRVMask));
}
}
);
FlushRenderingCommands();
}
SrcTexture: 512 * 512的存在10级Mip的Texture
DestTexture: 512 * 512的存在10级Mip的RenderTexture
[1]SrcTexture和DestTexture数组格式匹配,比如都说RGBA8或者RGBA16,格式不匹配可能导致程序崩溃
[2]像MipIndex,SliceIndex, Szie等参数注意不要越界(比如纹理数组Size为5, 在程序指定来SliceIndex = 5), 也可能导致程序崩溃.
[3]当CopySize为0时,会自动拷贝各级Mip, 进行整体拷贝,参考下面
UE4 CopyTexture使用RHI层的东西,在不同的平台下有不同的实现,在DX11下的实现:
CopySubresourceRegion(CopySize不为0时) 和 CopyResource(CopySize等于0时)
CopyToResolveTarget在UE4渲染线程经常使用,作用也说用来拷贝FTextureRHI,作用和CopyTexture很类似。
使用案例代码
void UTestCopyRenderFunctionLibrary::CopyToResolveTarget(
const UObject* WorldContextObject,
class UTexture2D* InTexture,
const UTexture2D* OutTexture,
FIntVector CopySize)
{
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 == OutTexture)
{
UE_LOG(LogTemp, Warning, TEXT("InTexture Is NULL"));
return;
}
FTexture* SourceTexture = InTexture->Resource;
FTexture* TargetTexture = OutTexture->Resource;
ENQUEUE_RENDER_COMMAND(TestShaderCommand)(
[SourceTexture, TargetTexture, FeatureLevel, CopySize](FRHICommandListImmediate& RHICmdList)
{
FResolveParams ResolveParams;
ResolveParams.Rect = FResolveRect(0, 0, SourceTexture->GetSizeX(), SourceTexture->GetSizeY());
ResolveParams.DestRect = FResolveRect(0, 0, SourceTexture->GetSizeX(), SourceTexture->GetSizeY());
RHICmdList.Transition(FRHITransitionInfo(TargetTexture->TextureRHI, ERHIAccess::Unknown, ERHIAccess::CopyDest));
RHICmdList.Transition(FRHITransitionInfo(SourceTexture->TextureRHI, ERHIAccess::SRVMask, ERHIAccess::CopySrc));
RHICmdList.CopyToResolveTarget(SourceTexture->TextureRHI, TargetTexture->TextureRHI, ResolveParams);
RHICmdList.Transition(FRHITransitionInfo(TargetTexture->TextureRHI, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
RHICmdList.Transition(FRHITransitionInfo(SourceTexture->TextureRHI, ERHIAccess::CopySrc, ERHIAccess::SRVMask));
});
}
连参数的作用也很类似,不过严格来说CopyToResolveTarget使用 范围比CopyTexture要广,比如说普通纹理(UTexture2D, UTexture2DArray, UTextureRenderTarget2D)使用CopyTexture不存在什么问题,但是SceneDepthTexture这种Texture使用CopyTexture直接野蛮拷贝效果是问题的。
看看CopyToResolveTarget在DX11的实现
可以看到CopyToResolveTarget在针对DepthStencil类纹理,说采用定制的FResolveDepthPS来进行Copy的,而针对普通纹理,则和CopyTexture一样。 总体上CopyTexture只说CopyToResolveTarget功能上的一个子集。
https://download.csdn.net/download/qq_29523119/86242859
https://docs.microsoft.com/zh-cn/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-copysubresourceregion