occlusion_culling.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. .. _doc_occlusion_culling:
  2. Occlusion culling
  3. =================
  4. In a 3D rendering engine, **occlusion culling** is the process of performing
  5. hidden geometry removal.
  6. On this page, you'll learn:
  7. - What are the advantages and pitfalls of occlusion culling.
  8. - How to set up occlusion culling in Godot.
  9. - Troubleshooting common issues with occlusion culling.
  10. .. seealso::
  11. You can see how occlusion culling works in action using the
  12. `Occlusion Culling and Mesh LOD demo project <https://github.com/godotengine/godot-demo-projects/tree/master/3d/occlusion_culling_mesh_lod>`__.
  13. Why use occlusion culling
  14. -------------------------
  15. In this example scene with hundreds of rooms stacked next to each other, a
  16. dynamic object (red sphere) is hidden behind the wall in the lit room (on the
  17. left of the door):
  18. .. figure:: img/occlusion_culling_scene_example.png
  19. :align: center
  20. :alt: Example scene with an occlusion culling-friendly layout
  21. Example scene with an occlusion culling-friendly layout
  22. With occlusion culling disabled, all the rooms behind the lit room have to be
  23. rendered. The dynamic object also has to be rendered:
  24. .. figure:: img/occlusion_culling_disabled.png
  25. :align: center
  26. :alt: Example scene with occlusion culling disabled (wireframe)
  27. Example scene with occlusion culling **disabled** (wireframe)
  28. With occlusion culling enabled, only the rooms that are actually visible have to
  29. be rendered. The dynamic object is also occluded by the wall, and therefore no
  30. longer has to be rendered:
  31. .. figure:: img/occlusion_culling_enabled.png
  32. :align: center
  33. :alt: Example scene with occlusion culling enabled (wireframe)
  34. Example scene with occlusion culling **enabled** (wireframe)
  35. Since the engine has less work to do (fewer vertices to render and fewer draw calls),
  36. performance will increase as long as there are enough occlusion culling opportunities
  37. in the scene. This means occlusion culling is most effective in indoor scenes,
  38. preferably with many smaller rooms instead of fewer larger rooms. Combine
  39. this with :ref:`doc_mesh_lod` and :ref:`doc_visibility_ranges` to further improve
  40. performance gains.
  41. .. note::
  42. When using the Forward+ renderer, the engine already
  43. performs a *depth prepass*. This consists in rendering a depth-only version
  44. of the scene before rendering the scene's actual materials. This is used to
  45. ensure each opaque pixel is only shaded once, reducing the cost of overdraw
  46. significantly.
  47. The greatest performance benefits can be observed when using the Mobile
  48. renderer, as it does not feature a depth prepass for performance reasons. As
  49. a result, occlusion culling will actively decrease shading overdraw with
  50. that renderer.
  51. Nonetheless, even when using a depth prepass, there is still a noticeable
  52. benefit to occlusion culling in complex 3D scenes. However, in scenes with
  53. few occlusion culling opportunities, occlusion culling may not be worth the
  54. added setup and CPU usage.
  55. How occlusion culling works in Godot
  56. ------------------------------------
  57. .. note::
  58. "occluder" refers to the shape blocking the view, while "occludee" refers to
  59. the object being hidden.
  60. In Godot, occlusion culling works by rasterizing the scene's occluder geometry
  61. to a low-resolution buffer on the CPU. This is done using
  62. the software raytracing library `Embree <https://github.com/embree/embree>`__.
  63. The engine then uses this low-resolution buffer to test the occludee's
  64. :abbr:`AABB (Axis-Aligned Bounding Box)` against the occluder shapes.
  65. The occludee's :abbr:`AABB (Axis-Aligned Bounding Box)` must be *fully occluded*
  66. by the occluder shape to be culled.
  67. As a result, smaller objects are more likely to be effectively culled than
  68. larger objects. Larger occluders (such as walls) also tend to be much more
  69. effective than smaller ones (such as decoration props).
  70. Setting up occlusion culling
  71. ----------------------------
  72. The first step to using occlusion culling is to enable the
  73. **Rendering > **Occlusion Culling > Use Occlusion Culling** project setting.
  74. (Make sure the **Advanced** toggle is enabled in the Project Settings dialog to
  75. be able to see it.)
  76. This project setting applies immediately, so you don't need to restart the editor.
  77. After enabling the project setting, you still need to create some occluders. For
  78. performance reasons, the engine doesn't automatically use all visible geometry
  79. as a basis for occlusion culling. Instead, the engine requires a simplified
  80. representation of the scene with only static objects to be baked.
  81. There are two ways to set up occluders in a scene:
  82. .. _doc_occlusion_culling_baking:
  83. Automatically baking occluders (recommended)
  84. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  85. .. note::
  86. Only MeshInstance3D nodes are currently taken into account in the *occluder*
  87. baking process. MultiMeshInstance3D, GPUParticles3D, CPUParticles3D and CSG
  88. nodes are **not** taken into account when baking occluders. If you wish
  89. those to be treated as occluders, you have to manually create occluder
  90. shapes that (roughly) match their geometry.
  91. Since Godot 4.4, CSG nodes can be taken into account in the baking process if they are
  92. :ref:`converted to a MeshInstance3D <doc_csg_tools_converting_to_mesh_instance_3d>`
  93. before baking occluders.
  94. This restriction does not apply to *occludees*. Any node type that inherits
  95. from GeometryInstance3D can be occluded.
  96. After enabling the occlusion culling project setting mentioned above, add an
  97. OccluderInstance3D node to the scene containing your 3D level.
  98. Select the OccluderInstance3D node, then click **Bake Occluders** at the top of
  99. the 3D editor viewport. After baking, the OccluderInstance3D node will contain
  100. an Occluder3D resource that stores a simplified version of your level's
  101. geometry. This occluder geometry appears as purple wireframe lines in the 3D view
  102. (as long as **View Gizmos** is enabled in the **Perspective** menu).
  103. This geometry is then used to provide occlusion culling for both static and
  104. dynamic occludees.
  105. After baking, you may notice that your dynamic objects (such as the player,
  106. enemies, etc…) are included in the baked mesh. To prevent this, set the
  107. **Bake > Cull Mask** property on the OccluderInstance3D to exclude certain visual
  108. layers from being baked.
  109. For example, you can disable layer 2 on the cull mask, then configure your
  110. dynamic objects' MeshInstance3D nodes to be located on the visual layer 2
  111. (instead of layer 1). To do so, select the MeshInstance3D node in question, then
  112. on the **VisualInstance3D > Layers** property, uncheck layer 1 then check layer
  113. 2. After configuring both cull mask and layers, bake occluders again by
  114. following the above process.
  115. Manually placing occluders
  116. ^^^^^^^^^^^^^^^^^^^^^^^^^^
  117. This approach is more suited for specialized use cases, such as creating occlusion
  118. for MultiMeshInstance3D setups or CSG nodes (due to the aforementioned limitation).
  119. After enabling the occlusion culling project setting mentioned above, add an
  120. OccluderInstance3D node to the scene containing your 3D level. Select the
  121. OccluderInstance3D node, then choose an occluder type to add in the **Occluder**
  122. property:
  123. - QuadOccluder3D (a single plane)
  124. - BoxOccluder3D (a cuboid)
  125. - SphereOccluder3D (a sphere-shaped occluder)
  126. - PolygonOccluder3D (a 2D polygon with as many points as you want)
  127. There is also ArrayOccluder3D, whose points can't be modified in the editor but
  128. can be useful for procedural generation from a script.
  129. .. _doc_occlusion_culling_preview:
  130. Previewing occlusion culling
  131. ----------------------------
  132. You can enable a debug draw mode to preview what the occlusion culling is
  133. actually "seeing". In the top-left corner of the 3D editor viewport, click the
  134. **Perspective** button (or **Orthogonal** depending on your current camera
  135. mode), then choose **Display Advanced… > Occlusion Culling Buffer**. This will
  136. display the low-resolution buffer that is used by the engine for occlusion
  137. culling.
  138. In the same menu, you can also enable **View Information** and **View Frame
  139. Time** to view the number of draw calls and rendered primitives (vertices +
  140. indices) in the bottom-right corner, along with the number of frames per second
  141. rendered in the top-right corner.
  142. If you toggle occlusion culling in the project settings while this information
  143. is displayed, you can see how much occlusion culling improves performance in
  144. your scene. Note that the performance benefit highly depends on the 3D editor
  145. camera's view angle, as occlusion culling is only effective if there are
  146. occluders in front of the camera.
  147. To toggle occlusion culling at runtime, set ``use_occlusion_culling`` on the
  148. root viewport as follows:
  149. .. tabs::
  150. .. code-tab:: gdscript
  151. get_tree().root.use_occlusion_culling = true
  152. .. code-tab:: csharp
  153. GetTree().Root.UseOcclusionCulling = true;
  154. Toggling occlusion culling at runtime is useful to compare performance on a
  155. running project.
  156. Performance considerations
  157. --------------------------
  158. Design your levels to take advantage of occlusion culling
  159. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  160. **This is the most important guideline.** A good level design is not just about
  161. what the gameplay demands; it should also be built with occlusion in mind.
  162. For indoor environments, add opaque walls to "break" the line of sight at
  163. regular intervals and ensure not too much of the scene can be seen at once.
  164. For large open scenes, use a pyramid-like structure for the terrain's elevation
  165. when possible. This provides the greatest culling opportunities compared to any
  166. other terrain shape.
  167. Avoid moving OccluderInstance3D nodes during gameplay
  168. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  169. This includes moving the parents of OccluderInstance3D nodes, as this will cause
  170. the nodes themselves to move in global space, therefore requiring the :abbr:`BVH
  171. (Bounding Volume Hierarchy)` to be rebuilt.
  172. Toggling an OccluderInstance3D's visibility (or one of its parents' visibility)
  173. is not as expensive, as the update only needs to happen once (rather than
  174. continuously).
  175. For example, if you have a sliding or rotating door, you can make the
  176. OccluderInstance3D node not be a child of the door itself (so that the occluder
  177. never moves), but you can hide the OccluderInstance3D visibility once the door
  178. starts opening. You can then reshow the OccluderInstance3D once the door is
  179. fully closed.
  180. If you absolutely have to move an OccluderInstance3D node during gameplay, use a
  181. primitive Occluder3D shape for it instead of a complex baked shape.
  182. Use the simplest possible occluder shapes
  183. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  184. If you notice low performance or stuttering in complex 3D scenes, it may mean
  185. that the CPU is overloaded as a result of rendering detailed occluders.
  186. Select the OccluderInstance3D node,
  187. increase the **Bake > Simplification** property then bake occluders again.
  188. Remember to keep the simplification value reasonable. Values that are too high
  189. for the level's geometry may cause incorrect occlusion culling to occur, as in
  190. :ref:`doc_occlusion_culling_troubleshooting_false_negative`.
  191. If this still doesn't lead to low enough CPU usage,
  192. you can try adjusting the **Rendering > Occlusion Culling > BVH Build Quality**
  193. project setting and/or decreasing
  194. **Rendering > Occlusion Culling > Occlusion Rays Per Thread**.
  195. You'll need to enable the **Advanced** toggle in the Project Settings dialog to
  196. see those settings.
  197. Troubleshooting
  198. ---------------
  199. My occludee isn't being culled when it should be
  200. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  201. **On the occluder side:**
  202. First, double-check that the **Bake > Cull Mask** property in the
  203. OccluderInstance3D is set to allow baking the meshes you'd like. The visibility
  204. layer of the MeshInstance3D nodes must be present within the cull mask for the
  205. mesh to be included in the bake.
  206. Also note that occluder baking only takes meshes with *opaque* materials into
  207. account. Surfaces will *transparent* materials will **not** be included in the
  208. bake, even if the texture applied on them is fully opaque.
  209. Lastly, remember that MultiMeshInstance3D, GPUParticles3D, CPUParticles3D and CSG
  210. nodes are **not** taken into account when baking occluders. As a workaround, you
  211. can add OccluderInstance3D nodes for those manually.
  212. **On the occludee side:**
  213. Make sure **Extra Cull Margin** is set as low as possible (it should usually be
  214. ``0.0``), and that **Ignore Occlusion Culling** is disabled in the object's
  215. GeometryInstance3D section.
  216. Also, check the AABB's size (which is represented by an orange box when
  217. selecting the node). This axis-aligned bounding box must be *fully* occluded by
  218. the occluder shapes for the occludee to be hidden.
  219. .. _doc_occlusion_culling_troubleshooting_false_negative:
  220. My occludee is being culled when it shouldn't be
  221. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  222. The most likely cause for this is that objects that were included in the
  223. occluder bake have been moved after baking occluders. For instance, this can
  224. occur when moving your level geometry around or rearranging its layout. To fix
  225. this, select the OccluderInstance3D node and bake occluders again.
  226. This can also happen because dynamic objects were included in the bake, even
  227. though they shouldn't be. Use the
  228. :ref:`occlusion culling debug draw mode <doc_occlusion_culling_preview>` to look
  229. for occluder shapes that shouldn't be present, then
  230. :ref:`adjust the bake cull mask accordingly <doc_occlusion_culling_baking>`.
  231. The last possible cause for this is overly aggressive mesh simplification during
  232. the occluder baking process. Select the OccluderInstance3D node,
  233. decrease the **Bake > Simplification** property then bake occluders again.
  234. As a last resort, you can enable the **Ignore Occlusion Culling** property on
  235. the occludee. This will negate the performance improvements of occlusion culling
  236. for that object, but it makes sense to do this for objects that will never be
  237. culled (such as a first-person view model).