mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-11 21:00:35 +01:00
252 lines
19 KiB
HTML
252 lines
19 KiB
HTML
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
|
<head>
|
||
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||
|
<title>Irrlicht 3D Engine: Tutorial 26: OcclusionQuery</title>
|
||
|
|
||
|
<link href="tabs.css" rel="stylesheet" type="text/css"/>
|
||
|
<link href="doxygen.css" rel="stylesheet" type="text/css" />
|
||
|
<link href="navtree.css" rel="stylesheet" type="text/css"/>
|
||
|
<script type="text/javascript" src="jquery.js"></script>
|
||
|
<script type="text/javascript" src="resize.js"></script>
|
||
|
<script type="text/javascript" src="navtree.js"></script>
|
||
|
<script type="text/javascript">
|
||
|
$(document).ready(initResizable);
|
||
|
</script>
|
||
|
<link href="search/search.css" rel="stylesheet" type="text/css"/>
|
||
|
<script type="text/javascript" src="search/search.js"></script>
|
||
|
<script type="text/javascript">
|
||
|
$(document).ready(function() { searchBox.OnSelectItem(0); });
|
||
|
</script>
|
||
|
|
||
|
</head>
|
||
|
<body>
|
||
|
<div id="top"><!-- do not remove this div! -->
|
||
|
|
||
|
|
||
|
<div id="titlearea">
|
||
|
<table cellspacing="0" cellpadding="0">
|
||
|
<tbody>
|
||
|
<tr style="height: 56px;">
|
||
|
|
||
|
<td id="projectlogo"><img alt="Logo" src="irrlichtlogo.png"/></td>
|
||
|
|
||
|
|
||
|
<td style="padding-left: 0.5em;">
|
||
|
<div id="projectname">Irrlicht 3D Engine
|
||
|
|
||
|
</div>
|
||
|
|
||
|
</td>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
<td> <div id="MSearchBox" class="MSearchBoxInactive">
|
||
|
<span class="left">
|
||
|
<img id="MSearchSelect" src="search/mag_sel.png"
|
||
|
onmouseover="return searchBox.OnSearchSelectShow()"
|
||
|
onmouseout="return searchBox.OnSearchSelectHide()"
|
||
|
alt=""/>
|
||
|
<input type="text" id="MSearchField" value="Search" accesskey="S"
|
||
|
onfocus="searchBox.OnSearchFieldFocus(true)"
|
||
|
onblur="searchBox.OnSearchFieldFocus(false)"
|
||
|
onkeyup="searchBox.OnSearchFieldChange(event)"/>
|
||
|
</span><span class="right">
|
||
|
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
|
||
|
</span>
|
||
|
</div>
|
||
|
</td>
|
||
|
|
||
|
|
||
|
</tr>
|
||
|
</tbody>
|
||
|
</table>
|
||
|
</div>
|
||
|
|
||
|
<!-- Generated by Doxygen 1.7.5.1 -->
|
||
|
<script type="text/javascript">
|
||
|
var searchBox = new SearchBox("searchBox", "search",false,'Search');
|
||
|
</script>
|
||
|
<script type="text/javascript" src="dynsections.js"></script>
|
||
|
</div>
|
||
|
<div id="side-nav" class="ui-resizable side-nav-resizable">
|
||
|
<div id="nav-tree">
|
||
|
<div id="nav-tree-contents">
|
||
|
</div>
|
||
|
</div>
|
||
|
<div id="splitbar" style="-moz-user-select:none;"
|
||
|
class="ui-resizable-handle">
|
||
|
</div>
|
||
|
</div>
|
||
|
<script type="text/javascript">
|
||
|
initNavTree('example026.html','');
|
||
|
</script>
|
||
|
<div id="doc-content">
|
||
|
<div class="header">
|
||
|
<div class="headertitle">
|
||
|
<div class="title">Tutorial 26: OcclusionQuery </div> </div>
|
||
|
</div>
|
||
|
<div class="contents">
|
||
|
<div class="textblock"><div class="image">
|
||
|
<img src="026shot.jpg" alt="026shot.jpg"/>
|
||
|
</div>
|
||
|
<p>This Tutorial shows how to speed up rendering by use of the OcclusionQuery feature. The usual rendering tries to avoid rendering of scene nodes by culling those nodes which are outside the visible area, the view frustum. However, this technique does not cope with occluded objects which are still in the line of sight, but occluded by some larger object between the object and the eye (camera). Occlusion queries check exactly that. The queries basically measure the number of pixels that a previous render left on the screen. Since those pixels cannot be recognized at the end of a rendering anymore, the pixel count is measured directly when rendering. Thus, one needs to render the occluder (the object in front) first. This object needs to write to the z-buffer in order to become a real occluder. Then the node is rendered and in case a z-pass happens, i.e. the pixel is written to the framebuffer, the pixel is counted in the query. The result of a query is the number of pixels which got through. One can, based on this number, judge if the scene node is visible enough to be rendered, or if the node should be removed in the next round. Also note that the number of pixels is a safe over approximation in general. The pixels might be overdrawn later on, and the GPU tries to avoid inaccuracies which could lead to false negatives in the queries.</p>
|
||
|
<p>As you might have recognized already, we had to render the node to get the numbers. So where's the benefit, you might say. There are several ways where occlusion queries can help. It is often a good idea to just render the bbox of the node instead of the actual mesh. This is really fast and is a safe over approximation. If you need a more exact render with the actual geometry, it's a good idea to render with just basic solid material. Avoid complex shaders and state changes through textures. There's no need while just doing the occlusion query. At least if the render is not used for the actual scene. This is the third way to optimize occlusion queries. Just check the queries every 5th or 10th frame, or even less frequent. This depends on the movement speed of the objects and camera. </p>
|
||
|
<div class="fragment"><pre class="fragment"><span class="preprocessor">#ifdef _MSC_VER</span>
|
||
|
<span class="preprocessor"></span><span class="comment">// We'll also define this to stop MSVC complaining about sprintf().</span>
|
||
|
<span class="preprocessor">#define _CRT_SECURE_NO_WARNINGS</span>
|
||
|
<span class="preprocessor"></span><span class="preprocessor">#pragma comment(lib, "Irrlicht.lib")</span>
|
||
|
<span class="preprocessor"></span><span class="preprocessor">#endif</span>
|
||
|
<span class="preprocessor"></span>
|
||
|
<span class="preprocessor">#include <<a class="code" href="irrlicht_8h.html" title="Main header file of the irrlicht, the only file needed to include.">irrlicht.h</a>></span>
|
||
|
<span class="preprocessor">#include "<a class="code" href="driver_choice_8h.html">driverChoice.h</a>"</span>
|
||
|
|
||
|
<span class="keyword">using namespace </span>irr;
|
||
|
</pre></div><p>We need keyboard input events to switch some parameters </p>
|
||
|
<div class="fragment"><pre class="fragment"><span class="keyword">class </span>MyEventReceiver : <span class="keyword">public</span> IEventReceiver
|
||
|
{
|
||
|
<span class="keyword">public</span>:
|
||
|
<span class="comment">// This is the one method that we have to implement</span>
|
||
|
<span class="keyword">virtual</span> <span class="keywordtype">bool</span> OnEvent(<span class="keyword">const</span> SEvent& event)
|
||
|
{
|
||
|
<span class="comment">// Remember whether each key is down or up</span>
|
||
|
<span class="keywordflow">if</span> (event.EventType == <a class="code" href="namespaceirr.html#ac9eed96e06e85ce3c86fcbbbe9e48a0ca6f90390f3147a1693e5e2e3422d6ca09" title="A key input event.">irr::EET_KEY_INPUT_EVENT</a>)
|
||
|
KeyIsDown[<span class="keyword">event</span>.KeyInput.Key] = <span class="keyword">event</span>.KeyInput.PressedDown;
|
||
|
|
||
|
<span class="keywordflow">return</span> <span class="keyword">false</span>;
|
||
|
}
|
||
|
|
||
|
<span class="comment">// This is used to check whether a key is being held down</span>
|
||
|
<span class="keyword">virtual</span> <span class="keywordtype">bool</span> IsKeyDown(<a class="code" href="namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3">EKEY_CODE</a> keyCode)<span class="keyword"> const</span>
|
||
|
<span class="keyword"> </span>{
|
||
|
<span class="keywordflow">return</span> KeyIsDown[keyCode];
|
||
|
}
|
||
|
|
||
|
MyEventReceiver()
|
||
|
{
|
||
|
<span class="keywordflow">for</span> (<a class="code" href="namespaceirr.html#a0416a53257075833e7002efd0a18e804" title="32 bit unsigned variable.">u32</a> i=0; i<<a class="code" href="namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3a205b48c0c4ed7489ab8980657343283e">KEY_KEY_CODES_COUNT</a>; ++i)
|
||
|
KeyIsDown[i] = <span class="keyword">false</span>;
|
||
|
}
|
||
|
|
||
|
<span class="keyword">private</span>:
|
||
|
<span class="comment">// We use this array to store the current state of each key</span>
|
||
|
<span class="keywordtype">bool</span> KeyIsDown[<a class="code" href="namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3a205b48c0c4ed7489ab8980657343283e">KEY_KEY_CODES_COUNT</a>];
|
||
|
};
|
||
|
</pre></div><p>We create an <a class="el" href="classirr_1_1_irrlicht_device.html" title="The Irrlicht device. You can create it with createDevice() or createDeviceEx().">irr::IrrlichtDevice</a> and the scene nodes. One occluder, one occluded. The latter is a complex sphere, which has many triangles. </p>
|
||
|
<div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> main()
|
||
|
{
|
||
|
<span class="comment">// ask user for driver</span>
|
||
|
<a class="code" href="namespaceirr_1_1video.html#ae35a6de6d436c76107ad157fe42356d0" title="An enum for all types of drivers the Irrlicht Engine supports.">video::E_DRIVER_TYPE</a> driverType=driverChoiceConsole();
|
||
|
<span class="keywordflow">if</span> (driverType==<a class="code" href="namespaceirr_1_1video.html#ae35a6de6d436c76107ad157fe42356d0ae685cada50f8c100403134d932d0414c" title="No driver, just for counting the elements.">video::EDT_COUNT</a>)
|
||
|
<span class="keywordflow">return</span> 1;
|
||
|
|
||
|
<span class="comment">// create device</span>
|
||
|
MyEventReceiver receiver;
|
||
|
|
||
|
IrrlichtDevice* device = <a class="code" href="namespaceirr.html#abaf4d8719cc26b0d30813abf85e47c76" title="Creates an Irrlicht device. The Irrlicht device is the root object for using the engine.">createDevice</a>(driverType,
|
||
|
core::dimension2d<u32>(640, 480), 16, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">false</span>, &receiver);
|
||
|
|
||
|
<span class="keywordflow">if</span> (device == 0)
|
||
|
<span class="keywordflow">return</span> 1; <span class="comment">// could not create selected driver.</span>
|
||
|
|
||
|
video::IVideoDriver* driver = device->getVideoDriver();
|
||
|
scene::ISceneManager* smgr = device->getSceneManager();
|
||
|
|
||
|
smgr->getGUIEnvironment()->addStaticText(L<span class="stringliteral">"Press Space to hide occluder."</span>, <a class="code" href="namespaceirr_1_1core.html#a628365d56b9d3ca9c887cd7f651f7b45" title="Rectangle with int values.">core::recti</a>(10,10, 200,50));
|
||
|
</pre></div><p>Create the node to be occluded. We create a sphere node with high poly count. </p>
|
||
|
<div class="fragment"><pre class="fragment"> scene::ISceneNode * node = smgr->addSphereSceneNode(10, 64);
|
||
|
<span class="keywordflow">if</span> (node)
|
||
|
{
|
||
|
node->setPosition(<a class="code" href="namespaceirr_1_1core.html#a06f169d08b5c429f5575acb7edbad811" title="Typedef for a f32 3d vector.">core::vector3df</a>(0,0,60));
|
||
|
node->setMaterialTexture(0, driver->getTexture(<span class="stringliteral">"../../media/wall.bmp"</span>));
|
||
|
node->setMaterialFlag(<a class="code" href="namespaceirr_1_1video.html#a8a3bc00ae8137535b9fbc5f40add70d3acea597a2692b8415486a464a7f954d34" title="Will this material be lighted? Default: true.">video::EMF_LIGHTING</a>, <span class="keyword">false</span>);
|
||
|
}
|
||
|
</pre></div><p>Now we create another node, the occluder. It's a simple plane. </p>
|
||
|
<div class="fragment"><pre class="fragment"> scene::ISceneNode* plane = smgr->addMeshSceneNode(smgr->addHillPlaneMesh(
|
||
|
<span class="stringliteral">"plane"</span>, <a class="code" href="namespaceirr_1_1core.html#af6dc5c45ff13e7712758c827ff58676b" title="Typedef for an f32 dimension.">core::dimension2df</a>(10,10), <a class="code" href="namespaceirr_1_1core.html#ad2e562e3219072e2f7fc7c2bba0ef0cb" title="Typedef for an unsigned integer dimension.">core::dimension2du</a>(2,2)), 0, -1,
|
||
|
<a class="code" href="namespaceirr_1_1core.html#a06f169d08b5c429f5575acb7edbad811" title="Typedef for a f32 3d vector.">core::vector3df</a>(0,0,20), <a class="code" href="namespaceirr_1_1core.html#a06f169d08b5c429f5575acb7edbad811" title="Typedef for a f32 3d vector.">core::vector3df</a>(270,0,0));
|
||
|
|
||
|
<span class="keywordflow">if</span> (plane)
|
||
|
{
|
||
|
plane->setMaterialTexture(0, driver->getTexture(<span class="stringliteral">"../../media/t351sml.jpg"</span>));
|
||
|
plane->setMaterialFlag(<a class="code" href="namespaceirr_1_1video.html#a8a3bc00ae8137535b9fbc5f40add70d3acea597a2692b8415486a464a7f954d34" title="Will this material be lighted? Default: true.">video::EMF_LIGHTING</a>, <span class="keyword">false</span>);
|
||
|
plane->setMaterialFlag(<a class="code" href="namespaceirr_1_1video.html#a8a3bc00ae8137535b9fbc5f40add70d3ae1d176d0ce05ccc5df9e43ce854393bb" title="Is backface culling enabled? Default: true.">video::EMF_BACK_FACE_CULLING</a>, <span class="keyword">true</span>);
|
||
|
}
|
||
|
</pre></div><p>Here we create the occlusion query. Because we don't have a plain mesh scene node (ESNT_MESH or ESNT_ANIMATED_MESH), we pass the base geometry as well. Instead, we could also pass a simpler mesh or the bounding box. But we will use a time based method, where the occlusion query renders to the frame buffer and in case of success (occlusion), the mesh is not drawn for several frames. </p>
|
||
|
<div class="fragment"><pre class="fragment"> driver->addOcclusionQuery(node, ((scene::IMeshSceneNode*)node)->getMesh());
|
||
|
</pre></div><p>We have done everything, just a camera and draw it. We also write the current frames per second and the name of the driver to the caption of the window to examine the render speedup. We also store the time for measuring the time since the last occlusion query ran and store whether the node should be visible in the next frames. </p>
|
||
|
<div class="fragment"><pre class="fragment"> smgr->addCameraSceneNode();
|
||
|
<span class="keywordtype">int</span> lastFPS = -1;
|
||
|
<a class="code" href="namespaceirr.html#a0416a53257075833e7002efd0a18e804" title="32 bit unsigned variable.">u32</a> timeNow = device->getTimer()->getTime();
|
||
|
<span class="keywordtype">bool</span> nodeVisible=<span class="keyword">true</span>;
|
||
|
|
||
|
<span class="keywordflow">while</span>(device->run())
|
||
|
{
|
||
|
plane->setVisible(!receiver.IsKeyDown(<a class="code" href="namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3a52c3b1744ca8ae7b6da19fc00fbb8ee8">irr::KEY_SPACE</a>));
|
||
|
|
||
|
driver->beginScene(<span class="keyword">true</span>, <span class="keyword">true</span>, video::SColor(255,113,113,133));
|
||
|
</pre></div><p>First, we draw the scene, possibly without the occluded element. This is necessary because we need the occluder to be drawn first. You can also use several scene managers to collect a number of possible occluders in a separately rendered scene. </p>
|
||
|
<div class="fragment"><pre class="fragment"> node->setVisible(nodeVisible);
|
||
|
smgr->drawAll();
|
||
|
smgr->getGUIEnvironment()->drawAll();
|
||
|
</pre></div><p>Once in a while, here every 100 ms, we check the visibility. We run the queries, update the pixel value, and query the result. Since we already rendered the node we render the query invisible. The update is made blocking, as we need the result immediately. If you don't need the result immediately, e.g. because you have other things to render, you can call the update non-blocking. This gives the GPU more time to pass back the results without flushing the render pipeline. If the update was called non-blocking, the result from getOcclusionQueryResult is either the previous value, or 0xffffffff if no value has been generated at all, yet. The result is taken immediately as visibility flag for the node. </p>
|
||
|
<div class="fragment"><pre class="fragment"> <span class="keywordflow">if</span> (device->getTimer()->getTime()-timeNow>100)
|
||
|
{
|
||
|
driver->runAllOcclusionQueries(<span class="keyword">false</span>);
|
||
|
driver->updateAllOcclusionQueries();
|
||
|
nodeVisible=driver->getOcclusionQueryResult(node)>0;
|
||
|
timeNow=device->getTimer()->getTime();
|
||
|
}
|
||
|
|
||
|
driver->endScene();
|
||
|
|
||
|
<span class="keywordtype">int</span> fps = driver->getFPS();
|
||
|
|
||
|
<span class="keywordflow">if</span> (lastFPS != fps)
|
||
|
{
|
||
|
<a class="code" href="namespaceirr_1_1core.html#aef83fafbb1b36fcce44c07c9be23a7f2" title="Typedef for wide character strings.">core::stringw</a> tmp(L<span class="stringliteral">"OcclusionQuery Example ["</span>);
|
||
|
tmp += driver->getName();
|
||
|
tmp += L<span class="stringliteral">"] fps: "</span>;
|
||
|
tmp += fps;
|
||
|
|
||
|
device->setWindowCaption(tmp.c_str());
|
||
|
lastFPS = fps;
|
||
|
}
|
||
|
}
|
||
|
</pre></div><p>In the end, delete the Irrlicht device. </p>
|
||
|
<div class="fragment"><pre class="fragment"> device->drop();
|
||
|
|
||
|
<span class="keywordflow">return</span> 0;
|
||
|
}
|
||
|
</pre></div><p>That's it. Compile and play around with the program. </p>
|
||
|
</div></div>
|
||
|
</div>
|
||
|
<div id="nav-path" class="navpath">
|
||
|
<ul>
|
||
|
<!-- window showing the filter options -->
|
||
|
<div id="MSearchSelectWindow"
|
||
|
onmouseover="return searchBox.OnSearchSelectShow()"
|
||
|
onmouseout="return searchBox.OnSearchSelectHide()"
|
||
|
onkeydown="return searchBox.OnSearchSelectKey(event)">
|
||
|
<a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(0)"><span class="SelectionMark"> </span>All</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(1)"><span class="SelectionMark"> </span>Classes</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(2)"><span class="SelectionMark"> </span>Namespaces</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(3)"><span class="SelectionMark"> </span>Files</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(4)"><span class="SelectionMark"> </span>Functions</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(5)"><span class="SelectionMark"> </span>Variables</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(6)"><span class="SelectionMark"> </span>Typedefs</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(7)"><span class="SelectionMark"> </span>Enumerations</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(8)"><span class="SelectionMark"> </span>Enumerator</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(9)"><span class="SelectionMark"> </span>Friends</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(10)"><span class="SelectionMark"> </span>Defines</a></div>
|
||
|
|
||
|
<!-- iframe showing the search results (closed by default) -->
|
||
|
<div id="MSearchResultsWindow">
|
||
|
<iframe src="javascript:void(0)" frameborder="0"
|
||
|
name="MSearchResults" id="MSearchResults">
|
||
|
</iframe>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<li class="footer">
|
||
|
<a href="http://irrlicht.sourceforge.net" target="_blank">Irrlicht
|
||
|
Engine</a> Documentation © 2003-2012 by Nikolaus Gebhardt. Generated on Sat Jul 9 2016 18:18:27 for Irrlicht 3D Engine by
|
||
|
<a href="http://www.doxygen.org/index.html" target="_blank">Doxygen</a> 1.7.5.1 </li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
</body>
|
||
|
</html>
|