irrlicht/examples/28.CubeMapping/tutorial.html

214 lines
61 KiB
HTML
Raw Normal View History

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Tutorial 28: CubeMapping</title>
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- Wanted to avoid copying .css to each folder, so copied default .css from doxyen in here, kicked out most stuff we don't need for examples and modified some a little bit.
Target was having a single html in each example folder which is created from the main.cpp files and needs no files besides some images below media folder.
Feel free to improve :)
-->
<style>
body, table, div, p, dl {
font: 400 14px/22px;
}
body {
background-color: #F0F0F0;
color: black;
margin-left: 5%;
margin-right: 5%;
}
p.reference, p.definition {
font: 400 14px/22px;
}
.title {
font: 400 14px/28px;
font-size: 150%;
font-weight: bold;
margin: 10px 2px;
}
h1, h2, h3, h4, h5, h6 {
-webkit-transition: text-shadow 0.5s linear;
-moz-transition: text-shadow 0.5s linear;
-ms-transition: text-shadow 0.5s linear;
-o-transition: text-shadow 0.5s linear;
transition: text-shadow 0.5s linear;
margin-right: 15px;
}
caption {
font-weight: bold;
}
h3.version {
font-size: 90%;
text-align: center;
}
a {
color: #3D578C;
font-weight: normal;
text-decoration: none;
}
.contents a:visited {
color: #4665A2;
}
a:hover {
text-decoration: underline;
}
a.el {
font-weight: bold;
}
a.code, a.code:visited, a.line, a.line:visited {
color: #4665A2;
}
a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
color: #4665A2;
}
pre.fragment {
border: 1px solid #C4CFE5;
background-color: #FBFCFD;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
font-family: monospace, fixed;
font-size: 105%;
}
div.fragment {
padding: 0px;
margin: 4px 8px 4px 2px;
background-color: #FBFCFD;
border: 1px solid #C4CFE5;
}
div.line {
font-family: monospace, fixed;
font-size: 13px;
min-height: 13px;
line-height: 1.0;
text-wrap: unrestricted;
white-space: -moz-pre-wrap; /* Moz */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* CSS3 */
word-wrap: break-word; /* IE 5.5+ */
text-indent: -53px;
padding-left: 53px;
padding-bottom: 0px;
margin: 0px;
-webkit-transition-property: background-color, box-shadow;
-webkit-transition-duration: 0.5s;
-moz-transition-property: background-color, box-shadow;
-moz-transition-duration: 0.5s;
-ms-transition-property: background-color, box-shadow;
-ms-transition-duration: 0.5s;
-o-transition-property: background-color, box-shadow;
-o-transition-duration: 0.5s;
transition-property: background-color, box-shadow;
transition-duration: 0.5s;
}
div.contents {
margin-top: 10px;
margin-left: 12px;
margin-right: 8px;
}
div.center {
text-align: center;
margin-top: 0px;
margin-bottom: 0px;
padding: 0px;
}
div.center img {
border: 0px;
}
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
blockquote {
background-color: #F7F8FB;
border-left: 2px solid #9CAFD4;
margin: 0 24px 0 4px;
padding: 0 12px 0 16px;
}
hr {
height: 0px;
border: none;
border-top: 1px solid #4A6AAA;
}
address {
font-style: normal;
color: #2A3D61;
}
div.header {
background-image:url('nav_h.png');
background-repeat:repeat-x;
background-color: #F9FAFC;
margin: 0px;
border-bottom: 1px solid #C4CFE5;
}
div.headertitle {
padding: 5px 5px 5px 10px;
}
.image {
text-align: center;
}
.caption {
font-weight: bold;
}
div.zoom {
border: 1px solid #90A5CE;
}
tr.heading h2 {
margin-top: 12px;
margin-bottom: 4px;
}
</style>
</head>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--END TITLEAREA-->
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
</div><!-- top -->
<div class="header">
<div class="headertitle">
<div class="title">Tutorial 28: CubeMapping </div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><div class="image">
<img src="../../media/example_screenshots/028shot.jpg" alt="028shot.jpg"/>
</div>
<p>Shows usage of cubemap textures and how to do some simple environment mapping. Cubemap textures have images for all 6 directions of a cube in a single texture. Environment is used to reflect the environment around an object onto the object. Cubemaps only work with shader materials which are written to support cube mapping.</p>
<dl class="section author"><dt>Author</dt><dd>Michael Zeilfelder, based on EnvCubeMap example from irrSpintz engine.</dd></dl>
<p>Start with the usual includes. </p><div class="fragment"><div class="line"><span class="preprocessor">#ifdef _MSC_VER</span></div><div class="line"><span class="preprocessor">#pragma comment(lib, &quot;Irrlicht.lib&quot;)</span></div><div class="line"><span class="preprocessor">#endif</span></div><div class="line"></div><div class="line"><span class="preprocessor">#include &lt;irrlicht.h&gt;</span></div><div class="line"><span class="preprocessor">#include &quot;driverChoice.h&quot;</span></div><div class="line"><span class="preprocessor">#include &quot;exampleHelper.h&quot;</span></div><div class="line"></div><div class="line"><span class="keyword">using namespace </span>irr;</div></div><!-- fragment --><p> A callback class for our cubemap shader. We need a shader material which maps the cubemap texture to the polygon vertices of objects. </p><div class="fragment"><div class="line"><span class="keyword">class </span>CubeMapReflectionCallback : <span class="keyword">public</span> video::IShaderConstantSetCallBack</div><div class="line">{</div><div class="line"><span class="keyword">public</span>:</div><div class="line"> CubeMapReflectionCallback(scene::ISceneManager* smgr, <span class="keywordtype">int</span> styleUVW)</div><div class="line"> : SceneMgr(smgr)</div><div class="line"> , StyleUVW(styleUVW), Roughness(0.f)</div><div class="line"> , styleUvwID(-1) , worldViewProjID(-1), worldID(-1), cameraPosID(-1)</div><div class="line"> {}</div></div><!-- fragment --><p> Setting the style to map vertex UV-coordinates to the cubemap textures.</p><ul>
<li>Specular style is typically used for mirrors and highlight reflections.</li>
<li>Diffuse style is commonly used in image based lighting calculations and often in combination with a higher roughness. Think of it as the sum of all light which reaches a point on your object.</li>
<li>Using model vertices directly for UV's is just nice for testing sometimes. Maybe has more uses? Experiment around :-) <div class="fragment"><div class="line"><span class="keywordtype">void</span> SetStyleUVW(<span class="keywordtype">int</span> style)</div><div class="line">{</div><div class="line"> StyleUVW = style;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keywordtype">int</span> GetStyleUVW()<span class="keyword"> const</span></div><div class="line"><span class="keyword"></span>{</div><div class="line"> <span class="keywordflow">return</span> StyleUVW;</div><div class="line">}</div></div><!-- fragment --> We could also call this sharpness as the rougher a material the less sharp the reflections of a cubemap are (light for rough materials spreads out more while smooth materials reflect it more like a mirror). Roughness is calculated using the mipmaps of the cubemap texture. Note that rendertarget cubemap textures won't have mipmaps, so unfortunately it won't work for those. Also currently only OpenGL is able to interpolate seamless over cubemap borders. On Direct3D9 you will only smooth per side, but not over side-borders. <div class="fragment"><div class="line"><span class="keywordtype">void</span> SetRoughness(<span class="keywordtype">float</span> roughness)</div><div class="line">{</div><div class="line"> Roughness = roughness;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keywordtype">float</span> getRoughness()<span class="keyword"> const</span></div><div class="line"><span class="keyword"></span>{</div><div class="line"> <span class="keywordflow">return</span> Roughness;</div><div class="line">}</div></div><!-- fragment --> Typical code which passes a few values from c++ to shader. <div class="fragment"><div class="line"> <span class="keyword">virtual</span> <span class="keywordtype">void</span> OnSetMaterial(<span class="keyword">const</span> video::SMaterial&amp; material)</div><div class="line"> {}</div><div class="line"></div><div class="line"> <span class="keyword">virtual</span> <span class="keywordtype">void</span> OnSetConstants(video::IMaterialRendererServices* services, s32 userData)</div><div class="line"> {</div><div class="line"> video::IVideoDriver* driver = services-&gt;getVideoDriver();</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> ( worldViewProjID &lt; 0 ) <span class="comment">// first update</span></div><div class="line"> {</div><div class="line"> styleUvwID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;StyleUVW&quot;</span>);</div><div class="line"> <span class="keywordflow">if</span>( driver-&gt;getDriverType() == video::EDT_DIRECT3D9 )</div><div class="line"> {</div><div class="line"> worldViewProjID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;WorldViewProj&quot;</span>);</div><div class="line"> }</div><div class="line"> worldID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;World&quot;</span>);</div><div class="line"> cameraPosID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;CameraPos&quot;</span>);</div><div class="line"> roughnessID = services-&gt;getPixelShaderConstantID(<span class="stringliteral">&quot;Roughness&quot;</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> services-&gt;setVertexShaderConstant(styleUvwID, &amp;StyleUVW, 1 );</div><div class="line"></div><div class="line"> irr::core::matrix4 world = driver-&gt;getTransform(irr::video::ETS_WORLD);</div><div class="line"> services-&gt;setVertexShaderConstant(worldID, world.pointer(), 16);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span>( driver-&gt;getDriverType() == video::EDT_DIRECT3D9 )</div><div class="line"> {</div><div class="line">
</ul>
<p>So... the following 2 defines are two different workarounds I found. Both are ugly, which one is better in reality depends probably on the scene. Only use one of those: CUBEMAP_UPSIDE_DOWN_GL_PROJECTION is relatively fast as it just changes the project matrix. The problem is that changing the projection matrix means changing front/backside culling. So every node rendered has to flip the material flags for those.</p>
<p>CUBEMAP_USPIDE_DOWN_RTT will change the texture memory itself and flip the image upside-down. While easier to do, this involves texture-locking and is very slow. </p><div class="fragment"><div class="line"><span class="preprocessor">#define CUBEMAP_UPSIDE_DOWN_GL_PROJECTION</span></div><div class="line"><span class="comment">//#define CUBEMAP_USPIDE_DOWN_RTT</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">// Flip frontface/backface culling for all nodes</span></div><div class="line"><span class="preprocessor">#ifdef CUBEMAP_UPSIDE_DOWN_GL_PROJECTION</span></div><div class="line"><span class="keywordtype">void</span> flipCullingFlags(<span class="keyword">const</span> core::array&lt;scene::ISceneNode*&gt;&amp; nodes)</div><div class="line">{</div><div class="line"> <span class="keywordflow">for</span> ( irr::u32 n=0; n &lt; nodes.size(); ++n )</div><div class="line"> {</div><div class="line"> scene::ISceneNode* node = nodes[n];</div><div class="line"> <span class="keyword">const</span> irr::u32 matCount = node-&gt;getMaterialCount();</div><div class="line"> <span class="keywordflow">for</span> ( irr::u32 m=0; m &lt; matCount; ++m)</div><div class="line"> {</div><div class="line"> video::SMaterial&amp; mat = node-&gt;getMaterial(m);</div><div class="line"> mat.BackfaceCulling = !mat.BackfaceCulling;</div><div class="line"> mat.FrontfaceCulling = !mat.FrontfaceCulling;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="preprocessor">#endif</span></div></div><!-- fragment --><p> Render the environment around a node into a cubemap texture. </p><div class="fragment"><div class="line"><span class="keywordtype">void</span> renderEnvironmentCubeMap(irr::video::IVideoDriver* driver, irr::scene::ICameraSceneNode* cubeMapCamera, irr::scene::ISceneNode* cubeCenterNode, video::IRenderTarget* cubeMapRT, video::ITexture* dynamicCubeMapRTT, video::ITexture* depthStencilRTT)</div><div class="line">{</div><div class="line"> <span class="comment">// Change to the cubemap camera which has a few specific render-settings</span></div><div class="line"> scene::ISceneManager* smgr = cubeMapCamera-&gt;getSceneManager();</div><div class="line"> scene::ICameraSceneNode * oldCam = smgr-&gt;getActiveCamera();</div><div class="line"> smgr-&gt;setActiveCamera( cubeMapCamera );</div></div><!-- fragment --><p> We want to see everything around the center node, so hide the node itself, otherwise it would be in the way. Then set the camera to that node's position. </p><div class="fragment"><div class="line">cubeCenterNode-&gt;setVisible( <span class="keyword">false</span> );</div><div class="line"><span class="keyword">const</span> core::vector3df center( cubeCenterNode-&gt;getAbsolutePosition() );</div><div class="line">cubeMapCamera-&gt;setPosition( center );</div></div><!-- fragment --><p> Render all 6 directions. Which means simple setting the camera target/up vector to all 6 directions and then render the full scene each time. So yeah - updating an environment cube-map means 6 full renders for each object which needs an environment map. In other words - you generally only want to do that in pre-processing, not in realtime. </p><div class="fragment"><div class="line"> <span class="keyword">const</span> core::vector3df targetVecs[6] = {</div><div class="line"> core::vector3df(1.f, 0.f, 0.f),</div><div class="line"> core::vector3df(-1.f, 0.f, 0.f),</div><div class="line"> core::vector3df(0.f, 1.f, 0.f),</div><div class="line"> core::vector3df(0.f, -1.f, 0.f),</div><div class="line"> core::vector3df(0.f, 0.f, 1.f),</div><div class="line"> core::vector3df(0.f, 0.f, -1.f)</div><div class="line"> };</div><div class="line"></div><div class="line"> <span class="keyword">const</span> core::vector3df upVecs[6] = {</div><div class="line"> core::vector3df( 0,1,0 ),</div><div class="line"> core::vec
<!-- HTML footer for doxygen 1.8.13-->
<!-- start footer part -->
<p>&nbsp;</p>
</body>
</html>