UE4获取深度值

· ☕ 4 分钟 · 👻 Victor
🏷️
  • #C++
  • #UE4
  • #Game
  • 在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
    10
    
            // 添加引擎源码地址
            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));
      }
     }
    }
    
    分享

    redisread
    作者
    Victor
    Full Stack