遮挡剔除 2021-07-04

遮挡剔除

        遮挡剔除的运行将通过在场景中使用一个虚拟的摄像机来创建一个物体潜在可视性状态(set)的层级。 这些数据可以让每个运行时间内的摄像机来确定什么能看见什么看不见。通过这些数据, Unity 将确定只把可以看见的物体送去渲染。 这将降低 draw calls 的数量并提供游戏的运行效率。
  occlusion culling的数据由单元(cells)组成。 每个单元是整个场景范围数值的一部分。 更多特定的单元来自一个二叉树( binary tree)。 Occlusion Culling使用两个叉, 一个给 View Cells (静态物体) 另一个给 Target Cells (移动物体)。 View Cells map给出了一个定义了静态可视物体的索引列表 (精确剔除后的静态物体)。
  非常重要的一点是在创建你的物体时要随时注意,因为你需要在物体的尺寸和单元的尺寸间取得一个好的平衡。 理想情况下, 有时你可以通过将大的物体拆成几个部分来改进遮挡剔除效果。 无论如何你仍然能够将小的物体合并为一体来降低draw calls, 在它们都属于一些小的组件的时候, occlusion culling 将不起作用。确定组件中那个是可视的组件的选集和可视信息被认为是 PVS (潜在可视状态Potentially Visible Set)。

渲染流程

涉及到的剔除方法包括:
视椎体剔除
遮挡剔除
裁剪Clipping
背面剔除
深度剔除


如下对于这几种剔除方法分别进行分析:
视椎体剔除
发生在应用程序阶段(Application Stage),通常由游戏引擎内部实现或者本身编写对应的算法来实现,运行在CPU上。裁剪的依据主要是根据摄像机的视野(field of view)以及近裁减面和远裁剪面的距离,将可视范围外的物体排除出渲染,被剔除的物体将不会进入渲染的几何阶段(Geometry Stage)。视椎体剔除是减小渲染消耗的最有效手段之一,能够在不影响渲染效果的状况下大幅减小渲染涉及到的顶点数和面数。算法

根据不一样引擎的的实现,实际时候时可能有一些须要注意的地方。通过测试,Unity中默认ShadowMap的生成会大幅影响视椎体剔除的范围,对于移动平台不使用实时阴影的状况下,能够尝试关闭灯的阴影投射或者去掉简单sheder中的FallBack(一般会含有ShadowCaster),就会使同屏渲染面数大幅减少。缓存

遮挡剔除
发生在应用程序阶段(Application Stage),由游戏引擎实现,运行在CPU上。以Unity为例,须要根据场景中Static物体的位置预先生成场景OcclusionCulling数据,运行时就能够剔除对应静态物体以后的其余物体。遮挡剔除是减小渲染消耗的有效手段之一,能够和视椎体剔除同时生效,进一步减小渲染的消耗。性能

针对Unity中遮挡剔除中的使用和研究,https://blog.csdn.net/cartzhang/article/details/52684127 中总结的很好。测试

随着硬件技术的提高,目前GPU已经能够支持在没有应用程序阶段额外数据的支持下在硬件侧面实现遮挡剔除(在光栅化以后,像素着色器以前,优于渲染管线末尾才进行的深度剔除) 
https://blog.csdn.net/cartzhang/article/details/72420731 一文中提到优化

现代GPU中运用了Early-Z的技术,在Vertex阶段和Fragment阶段之间(光栅化以后,fragment以前)进行一次深度测试,若是深度测试失败,就没必要进行fragment阶段的计算了,所以在性能上会有很大的提高。可是最终的ZTest仍然须要进行,以保证最终的遮挡关系结果正确。.net

static void DoRenderScreenCamera(Camera* cam, RenderManager::PostPerCameraFunctor preImageEffectsPerCameraCall, RenderManager::PostPerCameraFunctor postImageEffectsPerCameraCall)
{
    DebugAssertMsg(cam != NULL, "Camera must not be null here");

    if (cam->GetStereoEnabled())
    {
        // Stereo rendering does culling internally
        Camera::DefaultPerformEyeRenderFunction eyeRenderFunction;
        if (GetIVRDevice() && GetIVRDevice()->GetStereoRenderingEnabled())
            cam->RenderStereo(Camera::kRenderFlagSetRenderTarget, kCullFlagNone, &eyeRenderFunction);
    }
    else
    {
        // Regular rendering path: cull, then render
        CullResults cullResults;
        if (cam->GetEnabled()) // might become disabled by pre-culling callbacks
        {
            cam->Cull(cullResults);
        }
        if (cam->GetEnabled()) // might become disabled by culling callbacks
        {
            cam->UpdateVelocity();
            cam->Render(cullResults, GetDefaultPassContext(), Camera::kRenderFlagSetRenderTarget, preImageEffectsPerCameraCall, postImageEffectsPerCameraCall);
        }
    }

    // Intermediate Renderers are created outside of camera rendering, but need to be cleaned up even if the camera doesn't render
    GetIntermediateRendererManager().ClearIntermediateRenderers(cam->GetInstanceID());
}

裁剪Clipping
发生在几何阶段(Geometry Stage)后期,投影变换以后屏幕映射以前,是渲染管线的必要一环。只有当图元彻底或部分存在于规范立方体内部(摄像机视野内)的时候,才将其返送到光栅化阶段。其中,对于彻底位于规范立方体内部的图元,则直接进行下一阶段;彻底处于规范立方外部的图元则彻底被舍弃;部分处于规范立方体内部图元,则会根据摄像机视野进行对应的裁剪,在这一过程当中可能会产生新的顶点。3d

经过裁剪Clipping能够将摄像机视野外的图元舍弃掉,减少光栅化阶段的消耗。htm

背面剔除
背面剔除便是将背向视点的图元剔除,由于它们对最终渲染的图像没有贡献。这是一种简单直观的操做,一次对一个多边形进行操做。剔除的基本原理是先断定多边形的朝向,并和当前的观察方向进行比较blog

更为详细的说明能够在《Real-Time Rendering》第14章加速算法找到,也可参照浅墨大大的总结文章 
https://blog.csdn.net/poem_qianmo/article/details/78884513

Unity中的背面剔除在光栅化阶段进行,执行在Vertex Shader 以后,在Fragment Shader片元着色器以前,经过Shader中的Cull指令来控制背面剔除的开启和关闭, 
Unity手册上的图示显示的比较清晰: 
 
https://docs.unity3d.com/Manual/SL-CullAndDepth.html

深度剔除
在Fragment Shader以后,光栅化阶段末期的融合阶段执行,又叫深度检测(或Z缓存检测)。每次将一个图元回执为相应的像素时,都会计算像素位置处图元的深度值,和深度缓存中对应像素的值进行比较,若是新计算出的深度小于缓存中的深度,则更新深度缓存中的值;若是深度值大于深度缓冲中的值,则计算结果被舍弃,深度缓冲的值也无需更新。

在执行深度剔除时,已经位于渲染管线末尾,全部的颜色计算都已经完成,所以意义只在处理图元的可见性问题,对于渲染的消耗基本没有影响,不是优化时须要重点考察的问题。

 	Unity.exe!OcclusionCullingTask::CreateUmbraTask() 行 438	C++
 	Unity.exe!OcclusionCullingTask::GenerateTomeInBackground() 行 828	C++
>	Unity.exe!StaticOcclusionCulling_CUSTOM_GenerateInBackground() 行 40352	C++
 	[外部代码]	
 	mono-2.0-bdwgc.dll!mono_jit_runtime_invoke(_MonoMethod * method, void * obj, void * * params, _MonoObject * * exc, _MonoError * error) 行 2809	C
 	mono-2.0-bdwgc.dll!do_runtime_invoke(_MonoMethod * method, void * obj, void * * params, _MonoObject * * exc, _MonoError * error) 行 2921	C
 	mono-2.0-bdwgc.dll!mono_runtime_invoke_checked(_MonoMethod * method, void * obj, void * * params, _MonoError * error) 行 3073	C
 	mono-2.0-bdwgc.dll!mono_runtime_try_invoke_array(_MonoMethod * method, void * obj, _MonoArray * params, _MonoObject * * exc, _MonoError * error) 行 5264	C
 	mono-2.0-bdwgc.dll!mono_runtime_invoke_array_checked(_MonoMethod * method, void * obj, _MonoArray * params, _MonoError * error) 行 5142	C
 	mono-2.0-bdwgc.dll!ves_icall_InternalInvoke(_MonoReflectionMethod * method, _MonoObject * this_arg, _MonoArray * params, _MonoException * * exc) 行 3358	C
 	[外部代码]	
 	mono-2.0-bdwgc.dll!mono_jit_runtime_invoke(_MonoMethod * method, void * obj, void * * params, _MonoObject * * exc, _MonoError * error) 行 2809	C
 	mono-2.0-bdwgc.dll!do_runtime_invoke(_MonoMethod * method, void * obj, void * * params, _MonoObject * * exc, _MonoError * error) 行 2921	C
 	[内联框架] mono-2.0-bdwgc.dll!mono_runtime_try_invoke(_MonoMethod *) 行 3026	C
 	mono-2.0-bdwgc.dll!mono_runtime_invoke(_MonoMethod * method, void * obj, void * * params, _MonoObject * * exc) 行 2968	C
 	Unity.exe!scripting_method_invoke(ScriptingMethodPtr method, ScriptingObjectPtr object, ScriptingArguments & arguments, ScriptingExceptionPtr * exception, bool convertArgs) 行 633	C++
 	Unity.exe!ScriptingInvocation::Invoke(ScriptingExceptionPtr * exception, bool convertArguments) 行 273	C++
 	Unity.exe!ScriptingInvocation::InvokeHandlingReturnValue<bool>(ScriptingExceptionPtr * exception, bool convertArgs) 行 185	C++
 	Unity.exe!ScriptingInvocation::Invoke<bool>(ScriptingExceptionPtr * exception, bool convertArgs) 行 203	C++
 	Unity.exe!Scripting::UnityEngine::GUIUtilityProxy::ProcessEvent(int instanceID, void * nativeEventPtr, ScriptingExceptionPtr * outException) 行 164	C++
 	Unity.exe!GUIView::ProcessRetainedMode(InputEvent & event, GUIState & state, int instanceID) 行 267	C++
 	Unity.exe!GUIView::OnInputEvent(InputEvent & event) 行 2548	C++
 	Unity.exe!GUIView::ProcessInputEventInternal(InputEvent & event) 行 97	C++
 	Unity.exe!GUIView::ProcessInputEvent(InputEvent & event) 行 106	C++
 	Unity.exe!GUIView::ProcessEventMessages(unsigned int message, unsigned __int64 wParam, __int64 lParam) 行 1180	C++
 	Unity.exe!GUIView::GUIViewWndProc(HWND__ * hWnd, unsigned int message, unsigned __int64 wParam, __int64 lParam) 行 1448	C++
 	[外部代码]	
 	Unity.exe!MainMessageLoop() 行 399	C++
 	Unity.exe!WinMain(HINSTANCE__ * hInst, HINSTANCE__ * hPrev, char * szCmdLine, int nCmdShow) 行 1014	C++
 	[外部代码]	
CreateUmbraTask

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值