main.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /** Example 026 OcclusionQuery
  2. This tutorial shows how to speed up rendering by use of the
  3. OcclusionQuery feature. The usual rendering tries to avoid rendering of
  4. scene nodes by culling those nodes which are outside the visible area, the
  5. view frustum. However, this technique does not cope with occluded objects
  6. which are still in the line of sight, but occluded by some larger object
  7. between the object and the eye (camera). Occlusion queries check exactly that.
  8. The queries basically measure the number of pixels that a previous render
  9. left on the screen.
  10. Since those pixels cannot be recognized at the end of a rendering anymore,
  11. the pixel count is measured directly when rendering. Thus, one needs to render
  12. the occluder (the object in front) first. This object needs to write to the
  13. z-buffer in order to become a real occluder. Then the node is rendered and in
  14. case a z-pass happens, i.e. the pixel is written to the framebuffer, the pixel
  15. is counted in the query.
  16. The result of a query is the number of pixels which got through. One can, based
  17. on this number, judge if the scene node is visible enough to be rendered, or if
  18. the node should be removed in the next round. Also note that the number of
  19. pixels is a safe over approximation in general. The pixels might be overdrawn
  20. later on, and the GPU tries to avoid inaccuracies which could lead to false
  21. negatives in the queries.
  22. As you might have recognized already, we had to render the node to get the
  23. numbers. So where's the benefit, you might say. There are several ways where
  24. occlusion queries can help. It is often a good idea to just render the bbox
  25. of the node instead of the actual mesh. This is really fast and is a safe over
  26. approximation. If you need a more exact render with the actual geometry, it's
  27. a good idea to render with just basic solid material. Avoid complex shaders
  28. and state changes through textures. There's no need while just doing the
  29. occlusion query. At least if the render is not used for the actual scene. This
  30. is the third way to optimize occlusion queries. Just check the queries every
  31. 5th or 10th frame, or even less frequent. This depends on the movement speed
  32. of the objects and camera.
  33. */
  34. #ifdef _MSC_VER
  35. // We'll also define this to stop MSVC complaining about sprintf().
  36. #define _CRT_SECURE_NO_WARNINGS
  37. #pragma comment(lib, "Irrlicht.lib")
  38. #endif
  39. #include <irrlicht.h>
  40. #include "driverChoice.h"
  41. #include "exampleHelper.h"
  42. using namespace irr;
  43. /*
  44. We need keyboard input events to switch some parameters
  45. */
  46. class MyEventReceiver : public IEventReceiver
  47. {
  48. public:
  49. // This is the one method that we have to implement
  50. virtual bool OnEvent(const SEvent& event)
  51. {
  52. // Remember whether each key is down or up
  53. if (event.EventType == irr::EET_KEY_INPUT_EVENT)
  54. KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
  55. return false;
  56. }
  57. // This is used to check whether a key is being held down
  58. virtual bool IsKeyDown(EKEY_CODE keyCode) const
  59. {
  60. return KeyIsDown[keyCode];
  61. }
  62. MyEventReceiver()
  63. {
  64. for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
  65. KeyIsDown[i] = false;
  66. }
  67. private:
  68. // We use this array to store the current state of each key
  69. bool KeyIsDown[KEY_KEY_CODES_COUNT];
  70. };
  71. /*
  72. We create an irr::IrrlichtDevice and the scene nodes. One occluder, one
  73. occluded. The latter is a complex sphere, which has many triangles.
  74. */
  75. int main()
  76. {
  77. // ask user for driver
  78. video::E_DRIVER_TYPE driverType=driverChoiceConsole();
  79. if (driverType==video::EDT_COUNT)
  80. return 1;
  81. // create device
  82. MyEventReceiver receiver;
  83. IrrlichtDevice* device = createDevice(driverType,
  84. core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
  85. if (device == 0)
  86. return 1; // could not create selected driver.
  87. video::IVideoDriver* driver = device->getVideoDriver();
  88. scene::ISceneManager* smgr = device->getSceneManager();
  89. const io::path mediaPath = getExampleMediaPath();
  90. smgr->getGUIEnvironment()->addStaticText(L"Press Space to hide occluder.", core::recti(10,10, 200,50));
  91. /*
  92. Create the node to be occluded. We create a sphere node with high poly count.
  93. */
  94. scene::ISceneNode * node = smgr->addSphereSceneNode(10, 64);
  95. if (node)
  96. {
  97. node->setPosition(core::vector3df(0,0,60));
  98. node->setMaterialTexture(0, driver->getTexture(mediaPath + "wall.bmp"));
  99. node->setMaterialFlag(video::EMF_LIGHTING, false);
  100. }
  101. /*
  102. Now we create another node, the occluder. It's a simple plane.
  103. */
  104. scene::ISceneNode* plane = smgr->addMeshSceneNode(smgr->addHillPlaneMesh(
  105. "plane", core::dimension2df(10,10), core::dimension2du(2,2)), 0, -1,
  106. core::vector3df(0,0,20), core::vector3df(270,0,0));
  107. if (plane)
  108. {
  109. plane->setMaterialTexture(0, driver->getTexture(mediaPath + "t351sml.jpg"));
  110. plane->setMaterialFlag(video::EMF_LIGHTING, false);
  111. plane->setMaterialFlag(video::EMF_BACK_FACE_CULLING, true);
  112. }
  113. /*
  114. Here we create the occlusion query. Because we don't have a plain mesh scene node
  115. (ESNT_MESH or ESNT_ANIMATED_MESH), we pass the base geometry as well. Instead,
  116. we could also pass a simpler mesh or the bounding box. But we will use a time
  117. based method, where the occlusion query renders to the frame buffer and in case
  118. of success (occlusion), the mesh is not drawn for several frames.
  119. */
  120. driver->addOcclusionQuery(node, ((scene::IMeshSceneNode*)node)->getMesh());
  121. /*
  122. We have done everything, just a camera and draw it. We also write the
  123. current frames per second and the name of the driver to the caption of the
  124. window to examine the render speedup.
  125. We also store the time for measuring the time since the last occlusion query ran
  126. and store whether the node should be visible in the next frames.
  127. */
  128. smgr->addCameraSceneNode();
  129. int lastFPS = -1;
  130. u32 timeNow = device->getTimer()->getTime();
  131. bool nodeVisible=true;
  132. while(device->run())
  133. {
  134. plane->setVisible(!receiver.IsKeyDown(irr::KEY_SPACE));
  135. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(255,113,113,133));
  136. /*
  137. First, we draw the scene, possibly without the occluded element. This is necessary
  138. because we need the occluder to be drawn first. You can also use several scene
  139. managers to collect a number of possible occluders in a separately rendered
  140. scene.
  141. */
  142. node->setVisible(nodeVisible);
  143. smgr->drawAll();
  144. smgr->getGUIEnvironment()->drawAll();
  145. /*
  146. Once in a while, here every 100 ms, we check the visibility. We run the queries,
  147. update the pixel value, and query the result. Since we already rendered the node
  148. we render the query invisible. The update is made blocking, as we need the result
  149. immediately. If you don't need the result immediately, e.g. because you have other
  150. things to render, you can call the update non-blocking. This gives the GPU more
  151. time to pass back the results without flushing the render pipeline.
  152. If the update was called non-blocking, the result from getOcclusionQueryResult is
  153. either the previous value, or 0xffffffff if no value has been generated at all, yet.
  154. The result is taken immediately as visibility flag for the node.
  155. */
  156. if (device->getTimer()->getTime()-timeNow>100)
  157. {
  158. driver->runAllOcclusionQueries(false);
  159. driver->updateAllOcclusionQueries();
  160. nodeVisible=driver->getOcclusionQueryResult(node)>0;
  161. timeNow=device->getTimer()->getTime();
  162. }
  163. driver->endScene();
  164. int fps = driver->getFPS();
  165. if (lastFPS != fps)
  166. {
  167. core::stringw tmp(L"OcclusionQuery Example [");
  168. tmp += driver->getName();
  169. tmp += L"] fps: ";
  170. tmp += fps;
  171. device->setWindowCaption(tmp.c_str());
  172. lastFPS = fps;
  173. }
  174. }
  175. /*
  176. In the end, delete the Irrlicht device.
  177. */
  178. device->drop();
  179. return 0;
  180. }
  181. /*
  182. That's it. Compile and play around with the program.
  183. **/