227 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			227 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								title: XR拍摄角色没有阴影的解决方案
							 | 
						|||
| 
								 | 
							
								date: 2022-12-05 14:01:39
							 | 
						|||
| 
								 | 
							
								excerpt: 
							 | 
						|||
| 
								 | 
							
								tags: XR
							 | 
						|||
| 
								 | 
							
								rating: ⭐
							 | 
						|||
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 解决方案
							 | 
						|||
| 
								 | 
							
								在不修改引擎的情况下,解决思路有:
							 | 
						|||
| 
								 | 
							
								1. [[#手动渲染阴影]]
							 | 
						|||
| 
								 | 
							
								2. [[#使用Composure进行合成]]
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								修改引擎的解决思路有:
							 | 
						|||
| 
								 | 
							
								1. 
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 手动渲染阴影
							 | 
						|||
| 
								 | 
							
								- 使用ShadowMapMaterialFunction插件渲染渲染阴影并且贴在一个面片上。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								缺点:
							 | 
						|||
| 
								 | 
							
								1. 如果快速旋转视角,阴影会有残影效果。**原因:因为这个阴影贴图不是在同一帧渲染出来的关系。**
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 使用Composure进行合成
							 | 
						|||
| 
								 | 
							
								- 使用Composure渲染 Floor(地面模型)、Shadow Layer(地面+角色模型),再通过Shader实现阴影+角色抠像方法。
							 | 
						|||
| 
								 | 
							
								- 使用Composure渲染 Floor(地面模型)、Reflection Layer(地面+角色模型),再通过Shader实现反射+角色抠像方法。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								经过测试捕捉反射可以使用绿色面片作为背景,最后使用Chroma进行抠像。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								注意:使用Composure捕捉的结果都是经过ToneMapping,如果把RT再次贴到场景中进行渲染会有2次Tonemaping只是结果“发白”,所以需要在几个Layer中添加Tonemap选项,进行反ToneMapping。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								缺点:
							 | 
						|||
| 
								 | 
							
								1. 需要使用多次SceneCapture,会严重降低帧数。即使只捕捉单个物体,渲染消耗也和原始的渲染消耗一样。**建议:把角色与Composure相关的东西,放在一个子关卡中,场景放在一个子关卡中,然后2台机器使用SVN同步完工程之后,启动对应的关卡进行推流。**
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## ~~UPlanarReflectionComponent~~
							 | 
						|||
| 
								 | 
							
								核心函数:
							 | 
						|||
| 
								 | 
							
								- UPlanarReflectionComponent(主要用于维护Scene的PlanarReflections数组以及数据更新标记bRegisteredReflectionCapturesHasChanged)
							 | 
						|||
| 
								 | 
							
									- CreateRenderState_Concurrent():创建FPlanarReflectionSceneProxy,并且调用Scene->AddPlanarReflection。
							 | 
						|||
| 
								 | 
							
									- SendRenderTransform_Concurrent():调用Scene->UpdatePlanarReflectionTransform。
							 | 
						|||
| 
								 | 
							
									- DestroyRenderState_Concurrent():调用Scene->RemovePlanarReflection(this),并且删除渲染线程的FPlanarReflectionSceneProxy。
							 | 
						|||
| 
								 | 
							
									- PostEditChangeProperty():更新可见性。
							 | 
						|||
| 
								 | 
							
								- FScene 
							 | 
						|||
| 
								 | 
							
									- UpdatePlanarReflectionContents():
							 | 
						|||
| 
								 | 
							
										- 清理RT并重新初始化RT
							 | 
						|||
| 
								 | 
							
										- 计算反射平面与FSceneCaptureViewInfo(主要是View与Projaction矩阵)
							 | 
						|||
| 
								 | 
							
										- 构建FSceneViewFamilyContext,可以理解为Capture的画布与相关View变量
							 | 
						|||
| 
								 | 
							
										- 调用FSceneRenderer::CreateSceneRenderer()创建渲染器
							 | 
						|||
| 
								 | 
							
										- 在渲染线程调用UpdatePlanarReflectionContents_RenderThread()渲染反射结果
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								    // Reflection view late update  
							 | 
						|||
| 
								 | 
							
								   if (SceneRenderer->Views.Num() > 1)  
							 | 
						|||
| 
								 | 
							
								   {      const FMirrorMatrix MirrorMatrix(MirrorPlane);  
							 | 
						|||
| 
								 | 
							
								      for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)  
							 | 
						|||
| 
								 | 
							
								      {         FViewInfo& ReflectionViewToUpdate = SceneRenderer->Views[ViewIndex];  
							 | 
						|||
| 
								 | 
							
								         const FViewInfo& UpdatedParentView = MainSceneRenderer->Views[ViewIndex];  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								         ReflectionViewToUpdate.UpdatePlanarReflectionViewMatrix(UpdatedParentView, MirrorMatrix);  
							 | 
						|||
| 
								 | 
							
								      }   }  
							 | 
						|||
| 
								 | 
							
								   // Render the scene normally  
							 | 
						|||
| 
								 | 
							
								   {  
							 | 
						|||
| 
								 | 
							
								      RDG_RHI_EVENT_SCOPE(GraphBuilder, RenderScene);  
							 | 
						|||
| 
								 | 
							
								      SceneRenderer->Render(GraphBuilder);  
							 | 
						|||
| 
								 | 
							
								   }  
							 | 
						|||
| 
								 | 
							
								   SceneProxy->RenderTarget = RenderTarget;  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   // Update the view rects into the planar reflection proxy.  
							 | 
						|||
| 
								 | 
							
								   for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)  
							 | 
						|||
| 
								 | 
							
								   {      // Make sure screen percentage has correctly been set on render thread.  
							 | 
						|||
| 
								 | 
							
								      check(SceneRenderer->Views[ViewIndex].ViewRect.Area() > 0);  
							 | 
						|||
| 
								 | 
							
								      SceneProxy->ViewRect[ViewIndex] = SceneRenderer->Views[ViewIndex].ViewRect;  
							 | 
						|||
| 
								 | 
							
								   }  
							 | 
						|||
| 
								 | 
							
								   FRDGTextureRef ReflectionOutputTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(RenderTarget->TextureRHI, TEXT("ReflectionOutputTexture")));  
							 | 
						|||
| 
								 | 
							
								   GraphBuilder.SetTextureAccessFinal(ReflectionOutputTexture, ERHIAccess::SRVGraphics);  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   FSceneTextureShaderParameters SceneTextureParameters = CreateSceneTextureShaderParameters(GraphBuilder, &SceneRenderer->GetActiveSceneTextures(), SceneRenderer->FeatureLevel, ESceneTextureSetupMode::SceneDepth);  
							 | 
						|||
| 
								 | 
							
								   const FMinimalSceneTextures& SceneTextures = SceneRenderer->GetActiveSceneTextures();  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)  
							 | 
						|||
| 
								 | 
							
								   {      FViewInfo& View = SceneRenderer->Views[ViewIndex];  
							 | 
						|||
| 
								 | 
							
								      RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);  
							 | 
						|||
| 
								 | 
							
								      if (MainSceneRenderer->Scene->GetShadingPath() == EShadingPath::Deferred)  
							 | 
						|||
| 
								 | 
							
								      {         
							 | 
						|||
| 
								 | 
							
									      PrefilterPlanarReflection<true>(GraphBuilder, View, SceneTextureParameters, SceneProxy, SceneTextures.Color.Resolve, ReflectionOutputTexture);  
							 | 
						|||
| 
								 | 
							
								      }else  
							 | 
						|||
| 
								 | 
							
								      {  
							 | 
						|||
| 
								 | 
							
								         PrefilterPlanarReflection<false>(GraphBuilder, View, SceneTextureParameters, SceneProxy, SceneTextures.Color.Resolve, ReflectionOutputTexture);  
							 | 
						|||
| 
								 | 
							
								      }   
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								可行的实现思路:
							 | 
						|||
| 
								 | 
							
								1. 将`class FPlanarReflectionRenderTarget* RenderTarget`的结果覆盖到对应的`UTextureRenderTarget2D* `中。
							 | 
						|||
| 
								 | 
							
								2. 使用`UTextureRenderTarget2D`数据来对`FPlanarReflectionRenderTarget`进行初始化。
							 | 
						|||
| 
								 | 
							
									1. 扩展FPlanarReflectionRenderTarget类,添加新的构造函数,将UTextureRenderTarget2D的数据来扩充内部的RT。进行手动的初始化。
							 | 
						|||
| 
								 | 
							
									2. 扩展UPlanarReflectionComponent类,增加UTextureRenderTarget2D选项。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								但因为RenderTarget是Private,所以不修改源码的情况无法实现。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 使用SceneCapture2D模拟UPlanarReflectionComponent
							 | 
						|||
| 
								 | 
							
								位于PlanarReflectionRendering.cpp的UpdatePlanarReflectionContents_RenderThread()
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								计算视锥与反射平面的代码:
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								const FMatrix ComponentTransform = CaptureComponent->GetComponentTransform().ToMatrixWithScale();  
							 | 
						|||
| 
								 | 
							
								FPlane MirrorPlane = FPlane(ComponentTransform.TransformPosition(FVector::ZeroVector), ComponentTransform.TransformVector(FVector(0, 0, 1)));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// Normalize the plane to remove component scaling  
							 | 
						|||
| 
								 | 
							
								bool bNormalized = MirrorPlane.Normalize();  
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								if (!bNormalized)  
							 | 
						|||
| 
								 | 
							
								{  
							 | 
						|||
| 
								 | 
							
								   MirrorPlane = FPlane(FVector(0, 0, 1), 0);  
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								for (int32 ViewIndex = 0; ViewIndex < MainSceneRenderer.Views.Num() && ViewIndex < GMaxPlanarReflectionViews; ++ViewIndex)  
							 | 
						|||
| 
								 | 
							
								{  
							 | 
						|||
| 
								 | 
							
								   const FViewInfo& View = MainSceneRenderer.Views[ViewIndex];  
							 | 
						|||
| 
								 | 
							
								   FSceneCaptureViewInfo NewView;  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   FVector2D ViewRectMin = FVector2D(View.UnscaledViewRect.Min.X, View.UnscaledViewRect.Min.Y);  
							 | 
						|||
| 
								 | 
							
								   FVector2D ViewRectMax = FVector2D(View.UnscaledViewRect.Max.X, View.UnscaledViewRect.Max.Y);  
							 | 
						|||
| 
								 | 
							
								   ViewRectMin *= FMath::Clamp(CaptureComponent->ScreenPercentage / 100.f, 0.25f, 1.f);  
							 | 
						|||
| 
								 | 
							
								   ViewRectMax *= FMath::Clamp(CaptureComponent->ScreenPercentage / 100.f, 0.25f, 1.f);  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   NewView.ViewRect.Min.X = FMath::TruncToInt(ViewRectMin.X);  
							 | 
						|||
| 
								 | 
							
								   NewView.ViewRect.Min.Y = FMath::TruncToInt(ViewRectMin.Y);  
							 | 
						|||
| 
								 | 
							
								   NewView.ViewRect.Max.X = FMath::CeilToInt(ViewRectMax.X);  
							 | 
						|||
| 
								 | 
							
								   NewView.ViewRect.Max.Y = FMath::CeilToInt(ViewRectMax.Y);  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   // Create a mirror matrix and premultiply the view transform by it  
							 | 
						|||
| 
								 | 
							
								   const FMirrorMatrix MirrorMatrix(MirrorPlane);  
							 | 
						|||
| 
								 | 
							
								   const FMatrix ViewMatrix(MirrorMatrix * View.ViewMatrices.GetViewMatrix());  
							 | 
						|||
| 
								 | 
							
								   const FVector ViewLocation = ViewMatrix.InverseTransformPosition(FVector::ZeroVector);  
							 | 
						|||
| 
								 | 
							
								   const FMatrix ViewRotationMatrix = ViewMatrix.RemoveTranslation();  
							 | 
						|||
| 
								 | 
							
								   const float HalfFOV = FMath::Atan(1.0f / View.ViewMatrices.GetProjectionMatrix().M[0][0]);  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   FMatrix ProjectionMatrix;  
							 | 
						|||
| 
								 | 
							
								   BuildProjectionMatrix(View.UnscaledViewRect.Size(), HalfFOV + FMath::DegreesToRadians(CaptureComponent->ExtraFOV), GNearClippingPlane, ProjectionMatrix);  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   NewView.ViewLocation = ViewLocation;  
							 | 
						|||
| 
								 | 
							
								   NewView.ViewRotationMatrix = ViewRotationMatrix;  
							 | 
						|||
| 
								 | 
							
								   NewView.ProjectionMatrix = ProjectionMatrix;  
							 | 
						|||
| 
								 | 
							
								   NewView.StereoPass = View.StereoPass;  
							 | 
						|||
| 
								 | 
							
								   NewView.StereoViewIndex = View.StereoViewIndex;  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   SceneCaptureViewInfo.Add(NewView);  
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								void BuildProjectionMatrix(FIntPoint InRenderTargetSize, float InFOV, float InNearClippingPlane, FMatrix& OutProjectionMatrix)  
							 | 
						|||
| 
								 | 
							
								{  
							 | 
						|||
| 
								 | 
							
								   float const XAxisMultiplier = 1.0f;  
							 | 
						|||
| 
								 | 
							
								   float const YAxisMultiplier = InRenderTargetSize.X / float(InRenderTargetSize.Y);  
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								   if ((int32)ERHIZBuffer::IsInverted)  
							 | 
						|||
| 
								 | 
							
								   {      OutProjectionMatrix = FReversedZPerspectiveMatrix(  
							 | 
						|||
| 
								 | 
							
								         InFOV,  
							 | 
						|||
| 
								 | 
							
								         InFOV,  
							 | 
						|||
| 
								 | 
							
								         XAxisMultiplier,  
							 | 
						|||
| 
								 | 
							
								         YAxisMultiplier,  
							 | 
						|||
| 
								 | 
							
								         InNearClippingPlane,  
							 | 
						|||
| 
								 | 
							
								         InNearClippingPlane  
							 | 
						|||
| 
								 | 
							
								         );  
							 | 
						|||
| 
								 | 
							
								   }   else  
							 | 
						|||
| 
								 | 
							
								   {  
							 | 
						|||
| 
								 | 
							
								      OutProjectionMatrix = FPerspectiveMatrix(         InFOV,         InFOV,         XAxisMultiplier,         YAxisMultiplier,         InNearClippingPlane,         InNearClippingPlane         );   }}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								其他计算方式:
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								bool IsInFrustum( AActor* Actor)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
							 | 
						|||
| 
								 | 
							
									if (LocalPlayer != nullptr && LocalPlayer->ViewportClient != nullptr && LocalPlayer->ViewportClient->Viewport)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
							 | 
						|||
| 
								 | 
							
											LocalPlayer->ViewportClient->Viewport,
							 | 
						|||
| 
								 | 
							
											GetWorld()->Scene,
							 | 
						|||
| 
								 | 
							
											LocalPlayer->ViewportClient->EngineShowFlags)
							 | 
						|||
| 
								 | 
							
											.SetRealtimeUpdate(true));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										FVector ViewLocation;
							 | 
						|||
| 
								 | 
							
										FRotator ViewRotation;
							 | 
						|||
| 
								 | 
							
										FSceneView* SceneView = LocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, LocalPlayer->ViewportClient->Viewport);
							 | 
						|||
| 
								 | 
							
										if (SceneView != nullptr)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return SceneView->ViewFrustum.IntersectSphere(
							 | 
						|||
| 
								 | 
							
														Actor->GetActorLocation(), Actor->GetSimpleCollisionRadius());
							 | 
						|||
| 
								 | 
							
										}			 
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    return false
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### SceneCapture2D
							 | 
						|||
| 
								 | 
							
								位于SceneCaptureRendering.cpp
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 其他
							 | 
						|||
| 
								 | 
							
								## Disguise
							 | 
						|||
| 
								 | 
							
								支持的输入方式:
							 | 
						|||
| 
								 | 
							
								 - sdi传输(采集卡走这个协议)
							 | 
						|||
| 
								 | 
							
								 - ndi网络传输 (通过网卡网线)
							 | 
						|||
| 
								 | 
							
								 - smpte2110协议传输(显卡直接插大屏幕)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Composure相关问题
							 | 
						|||
| 
								 | 
							
								- Transforms
							 | 
						|||
| 
								 | 
							
									- Custom Material Pass
							 | 
						|||
| 
								 | 
							
									- Post Process Pass Set
							 | 
						|||
| 
								 | 
							
									- Tonemap
							 | 
						|||
| 
								 | 
							
									- Multi Pass Chroma Keyer
							 | 
						|||
| 
								 | 
							
									- Multi Pass Despill
							 | 
						|||
| 
								 | 
							
								- Outputs
							 | 
						|||
| 
								 | 
							
									- Media Capture:走采集卡推流模式。
							 | 
						|||
| 
								 | 
							
									- Image Sequence
							 | 
						|||
| 
								 | 
							
									- Player Viewport:可以通过在UE中实现虚拟摄像机,再通过OBS推流。
							 | 
						|||
| 
								 | 
							
									- Render Target Asset
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								值得注意的是Transforms的Tonemap,这是一个反ToneMapping效果。
							 |