Please enable Javascript to view the contents

UE4获取深度值

 ·  ☕ 4 分钟 · 👀... 阅读

在UE4中获取深度缓存,调用渲染命令读取。

获取深度缓存

深度像素格式

DepthPixel

键入命令vis scenedepthz uv0以查看实际使用的深度缓冲区。UE4对场景使用“反向”深度缓冲区。

Way1:直接使用ENQUEUE_RENDER_COMMAND命令获取(效率较低)

在任意tick函数或者其他函数添加以下的命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct DepthPixel	//定义深度像素结构体
	{
		float depth;
		char stencil;
		char unused1;
		char unused2;
		char unused3;
	};

	float* cpuDataPtr;	// Texture深度值数组首地址
	TArray<DepthPixel> mydata;	//最终获取色深度值数据
	FIntPoint buffsize;	//深度长宽大小X和Y

	ENQUEUE_RENDER_COMMAND(ReadSurfaceFloatCommand)(	// 将读取深度数据的命令推给渲染线程进行执行
		[&cpuDataPtr, &mydata, &buffsize](FRHICommandListImmediate& RHICmdList) //&cpuDataPtr, &mydata, &buffsize为传入的外部参数
	{
		FSceneRenderTargets::Get(RHICmdList).AdjustGBufferRefCount(RHICmdList, 1);
		FTexture2DRHIRef uTex2DRes = FSceneRenderTargets::Get(RHICmdList).GetSceneDepthSurface();	
		buffsize = uTex2DRes->GetSizeXY();
         uint32 sx = buffsize.X;
		uint32 sy = buffsize.Y;
         mydata.AddUninitialized(sx * sy);
         uint32 Lolstrid = 0;
		cpuDataPtr = (float*)RHILockTexture2D(uTex2DRes,0,RLM_ReadOnly,Lolstrid,true);	// 加锁 获取可读depth Texture深度值数组首地址
		memcpy(mydata.GetData(), cpuDataPtr, sx * sy * sizeof(DepthPixel));		//复制深度数据
		RHIUnlockTexture2D(uTex2DRes, 0, true);	//解锁
		FSceneRenderTargets::Get(RHICmdList).AdjustGBufferRefCount(RHICmdList, -1);	

	});
	FlushRenderingCommands();	//等待渲染线程执行

	mydata; 	//最终获取深度数据

最终返回的mydata数据就是最终的深度值数组,其中每个深度值的结构是DepthPixel,其中一个成员为depth,另外四个不不使用。其中使用上面的几个命令需要添加"RHI.h“头文件

Way2:写个请求类读取

UML图:
depth_UML
流程图:
depth-flowchat

1. 首先在项目的build.cs文件添加:

添加引擎源码地址

1
2
3
4
5
6
7
8
9
        // 添加引擎源码地址
        string EnginePath = "C:/Program Files (x86)/UE4+VS2017/UnrealEngine/";
        PrivateIncludePaths.AddRange(
            new string[] {
               EnginePath + "Source/Runtime/Renderer/Private",
               EnginePath + "Source/Runtime/Renderer/Private/CompositionLighting",
               EnginePath + "Source/Runtime/Renderer/Private/PostProcess"
                }
            );

添加引依赖项

依赖项

2. 类实现

将下面类代码复制到PostProcessing.h文件任意位置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*****************************************Get Depth Class*******************************************************/

/*	存储一个像素的缓存
	depth   深度缓存
	stencil (抠图缓存)*/
struct DepthPixel
{
	float depth;
	char stencil;
	char unused1;
	char unused2;
	char unused3;
};

/*	存储整个视窗的缓存
	data			像素缓存数组
	bufferSizeX		缓存大小X
	bufferSizeY		缓存大小Y
	pixelSizeBytes	像素缓存字节数*/
struct DepthResult
{
	TArray<DepthPixel> data;
	int bufferSizeX;
	int bufferSizeY;
	int pixelSizeBytes;
};

/*	获取深度缓存的类	 */
class RENDERER_API DepthCapture
{
public:
	/*	静态成员,当用户发出一个获取深度缓存的请求后,waitForCapture长度加1,新增DepthResult内容为空
				当系统完成一个深度缓存的请求后,waitForCapture长度减一 */
	static TQueue<DepthResult *, EQueueMode::Mpsc> waitForCapture;
	/*	静态成员,当系统完成一个深度缓存的请求后,finishedCapture长度加1,
				新增DepthResult含有深度缓存信息	*/
	static TQueue<DepthResult *, EQueueMode::Mpsc> finishedCapture;

public:
	/*用户发出一个获取深度缓存的请求时调用*/
	static void AddCapture()
	{
		waitForCapture.Enqueue(new DepthResult());
	}
	/*系统完成一个深度缓存请求后调用*/
	static void FinishedCapture(DepthResult *result)
	{
		finishedCapture.Enqueue(result);
	}
	/*返回是否存在已经完成的请求*/
	static bool HasFinishedCapture()
	{
		return !finishedCapture.IsEmpty();
	}
	/*如果存在已完成的请求,返回一个深度结果*/
	static DepthResult* GetIfExistFinished()
	{
		DepthResult* result = NULL;
		if (!finishedCapture.IsEmpty())
		{
			finishedCapture.Dequeue(result);
		}
		return result;
	}
	/*返回是否存在等待系统执行的请求*/
	static bool HasCaptureRequest()
	{
		return !waitForCapture.IsEmpty();
	}
	/*如果存在待完成的请求,返回一个深度结果(为空)*/
	static DepthResult* GetIfExistRequest()
	{
		DepthResult* result = NULL;
		if (!waitForCapture.IsEmpty())
		{
			waitForCapture.Dequeue(result);
		}
		return result;
	}
	//friend void AddPostProcessingPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FPostProcessingInputs& Inputs);
};

/*****************************************end******************************************************/

将下面类中静态成员初始化和添加执行获取代码代码复制到PostProcessing.cpp文件任意位置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*类静态成员的定义*/
TQueue<DepthResult *, EQueueMode::Mpsc> DepthCapture::waitForCapture;
TQueue< DepthResult *, EQueueMode::Mpsc> DepthCapture::finishedCapture;

/*获取深度缓存*/
void AddDepthInspectorPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, DepthResult* result)
{

	RDG_EVENT_SCOPE(GraphBuilder, "DepthInspector");
	{
		// 获取渲染对象
		FSceneRenderTargets& renderTargets = FSceneRenderTargets::Get(GRHICommandList.GetImmediateCommandList());

		// 定义拷贝参数
		uint32 striped = 0;
		FIntPoint size = renderTargets.GetBufferSizeXY();
		result->bufferSizeX = size.X;
		result->bufferSizeY = size.Y;
		result->data.AddUninitialized(size.X * size.Y);

		// 获取视窗某一帧的深度缓存对象
		FRHITexture2D* depthTexture = (FRHITexture2D *)renderTargets.SceneDepthZ->GetRenderTargetItem().TargetableTexture.GetReference();

		// 执行拷贝深度缓存操作,将GPU显存中的缓存信息拷贝到CPU内存中,返回指向这块CPU内存的首地址
		void* buffer = RHILockTexture2D(depthTexture, 0, EResourceLockMode::RLM_ReadOnly, striped, true);

		// 将缓存结果拷贝到result,用于输出
		memcpy(result->data.GetData(), buffer, size.X * size.Y * 8);

		// 必须执行解锁语句,否则被锁住的GPU缓存信息将不能释放
		RHIUnlockTexture2D(depthTexture, 0, true);

		// 拷贝结果入队
		DepthCapture::FinishedCapture(result);
	}
}
////////////////////////////////////////

PostProcessing.cpp中该位置添加以下代码:

添加代码

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
	// Capture depth buffer,otherwise the buffer will be changed
	if (DepthCapture::HasCaptureRequest())
	{
		DepthResult *reuslt;
		reuslt = DepthCapture::GetIfExistRequest();
		if (reuslt)
		{
			AddDepthInspectorPass(GraphBuilder, View, reuslt);
		}
	}

3. 调用

使用以下的代码可以获取深度值,获取的结果为result

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int tickcount = 0;
// Called every frame
void ATestPawn::Tick(float DeltaTime)
{
 tickcount++;
 if (tickcount % 2 == 0)	// 设计几帧调用
  DepthCapture::AddCapture();  // 定时发出获取深度缓存的请求

 // 如果存在已完成的深度缓存请求
 if (DepthCapture::HasFinishedCapture())
 {
  DepthResult *result;
  // 获取已完成的深度缓存结果
  result = DepthCapture::GetIfExistFinished();
  if (result)
  {
   int n = result->data.Num();
   //this is test
   GEngine->AddOnScreenDebugMessage(-1, -1, FColor::Blue, FString::Printf(TEXT("Get Depth Size: %d "), n));
  }
 }
}

VictorHong
作者
VictorHong
📚Learner🤓Nerd🌐Developer 努力做有价值的事情