<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta name="generator" content="pdf2htmlEX">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="https://static.pudn.com/base/css/base.min.css">
<link rel="stylesheet" href="https://static.pudn.com/base/css/fancy.min.css">
<link rel="stylesheet" href="https://static.pudn.com/prod/directory_preview_static/6251b9056caf596192437719/raw.css">
<script src="https://static.pudn.com/base/js/compatibility.min.js"></script>
<script src="https://static.pudn.com/base/js/pdf2htmlEX.min.js"></script>
<script>
try{
pdf2htmlEX.defaultViewer = new pdf2htmlEX.Viewer({});
}catch(e){}
</script>
<title></title>
</head>
<body>
<div id="sidebar" style="display: none">
<div id="outline">
</div>
</div>
<div id="pf1" class="pf w0 h0" data-page-no="1"><div class="pc pc1 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/6251b9056caf596192437719/bg1.jpg"><div class="t m0 x1 h2 y1 ff1 fs0 fc0 sc0 ls0 ws0">最长的一帧<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x2 h3 y2 ff3 fs1 fc0 sc1 ls2 ws0">王锐(array) </div><div class="t m0 x3 h4 y3 ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h4 y4 ff1 fs2 fc0 sc1 ls1 ws0">这是一篇有关<span class="_ _0"> </span><span class="ff4 ls3">OpenSceneGraph<span class="_"> </span></span>源代码的拙劣教程,没有任何能赏心悦目的小例子,也</div><div class="t m0 x3 h4 y5 ff1 fs2 fc0 sc1 ls1 ws0">不会贡献出什么企业级的绝密的商业代码,标题也只是个噱头(坏了,没人看了<span class="ff4 ls3">^_^</span>)<span class="_ _1"></span>。<span class="ff4"> </span></div><div class="t m0 x4 h4 y6 ff1 fs2 fc0 sc1 ls1 ws0">本文写作的目的说来很简单,无非就是想要深入地了解一<span class="_ _2"></span>下,<span class="ff4 ls4">OSG<span class="_ _3"> </span></span>在一帧时间,也就</div><div class="t m0 x3 h4 y7 ff1 fs2 fc0 sc1 ls1 ws0">是仿真循环的一个画面当中都做了什么。<span class="ff4"> </span></div><div class="t m0 x4 h4 y8 ff1 fs2 fc0 sc1 ls1 ws0">对<span class="_ _0"> </span><span class="ff4 ls4">OSG<span class="_"> </span></span>有所了解之后,我们也许可以很快地回答这个问题,正如下面的代码所示:<span class="ff4"> </span></div><div class="t m0 x4 h4 y9 ff4 fs2 fc0 sc1 ls5 ws1">while (!viewer<span class="_ _4"></span>.done()) </div><div class="t m0 x4 h4 ya ff4 fs2 fc0 sc1 ls1 ws2"> viewer<span class="_ _4"></span>.frame(); </div><div class="t m0 x4 h4 yb ff1 fs2 fc0 sc1 ls1 ws0">就这样,用一个循环结构来反复地执行<span class="_ _5"> </span><span class="ff4 ls6">frame()</span>函数,直到<span class="_ _5"> </span><span class="ff4 ls7">done()</span>函数的返回值为<span class="_ _5"> </span><span class="ff4 ls8">true</span></div><div class="t m0 x3 h4 yc ff1 fs2 fc0 sc1 ls1 ws0">为止。<span class="_ _6"></span>每一次执行<span class="_ _0"> </span><span class="ff4 ls6">frame()</span>函数就相当于完成了<span class="_ _0"> </span><span class="ff4 ls4">OSG<span class="_"> </span></span>场景渲染的一帧,<span class="_ _6"></span>配置较好的计算机可</div><div class="t m0 x3 h4 yd ff1 fs2 fc0 sc1 ls1 ws0">以达到每秒钟一二百帧的速率,而通常仿真程序顺利运行的最低帧速在<span class="_ _0"> </span><span class="ff4 ls9">15~25<span class="_"> </span></span>帧<span class="ff4">/</span>秒即可。<span class="ff4"> </span></div><div class="t m0 x4 h4 ye ff1 fs2 fc0 sc1 ls1 ws0">很好,<span class="_ _7"></span>看来笔者的机器运行<span class="_ _0"> </span><span class="ff4 ls6">frame()</span>函数通常只需要<span class="_ _0"> </span><span class="ff4 lsa">8~10ms<span class="_"> </span></span>左右,<span class="_ _7"></span>比一眨眼的工夫都要</div><div class="t m0 x3 h4 yf ff1 fs2 fc0 sc1 ls1 ws0">短。那么本文就到此结束吗?<span class="ff4"> </span></div><div class="t m0 x4 h5 y10 ff1 fs2 fc0 sc1 lsb ws0">答案当然是否定的,恰恰相反,<span class="_ _8"></span>这篇繁琐且可能错误百出<span class="_ _8"></span>的文字,其目的正是要深入</div><div class="t m0 x3 h4 y11 ff4 fs2 fc0 sc1 ls6 ws0">frame()<span class="ff1 ls1">函数,<span class="_ _9"></span>再深入函数中调用的函数……一直挖掘下去,<span class="_ _9"></span>直到我们期待的瑰宝出现;<span class="_ _a"></span>当然</span></div><div class="t m0 x3 h4 y12 ff1 fs2 fc0 sc1 ls1 ws0">也可能是一无所获,只是乐在其中。<span class="ff4"> </span></div><div class="t m0 x4 h4 y13 ff1 fs2 fc0 sc1 ls1 ws0">这样的探索要到什么时候结束呢?从这短短的<span class="_ _0"> </span><span class="ff4 lsc">10<span class="_"> </span></span>毫秒中引申出来的,<span class="_ _b"></span>无比冗长的一帧,</div><div class="t m0 x3 h5 y14 ff1 fs2 fc0 sc1 ls1 ws0">又是多么丰富抑或无聊的内容呢?现在笔者也不知道,<span class="_ _7"></span>也许直到最后也不会明了,<span class="_ _c"></span>不过相信</div><div class="t m0 x3 h4 y15 ff1 fs2 fc0 sc1 ls1 ws0">深入源代码的过程就是一种享受,希望读者您也可以同我一起享受这份辛苦与快乐。<span class="ff4"> </span></div><div class="t m0 x4 h4 y16 ff1 fs2 fc0 sc1 ls1 ws0">源代码版本:<span class="ff4 lsd ws3">OpenSceneGraph 2.6.0</span>;<span class="_ _8"></span>操作系统环境假设为<span class="_ _0"> </span><span class="ff4 lse">Wi<span class="_ _2"></span>n<span class="_ _d"></span>3<span class="_ _2"></span>2<span class="_ _e"> </span></span>平台。为了保证教程</div><div class="t m0 x3 h5 y17 ff1 fs2 fc0 sc1 ls1 ws0">的篇幅不致被过多程序代码所占据,<span class="_ _c"></span>文中会适当地改写和缩编所列出的代码,<span class="_ _7"></span>仅保证其执行</div><div class="t m0 x3 h4 y18 ff1 fs2 fc0 sc1 ls1 ws0">效果不变,因此可能与实际源文件的内容有所区别。<span class="ff4"> </span></div><div class="t m0 x4 h4 y19 ff1 fs2 fc0 sc1 ls1 ws0">由于作者水平和精力所限,本文暂时仅对单视景器(即使用<span class="_ _f"> </span><span class="ff4 lsf">osgV<span class="_ _4"></span>iewer::V<span class="_ _6"></span>iewer<span class="_ _f"> </span><span class="ff1 ls10">类)的</span></span></div><div class="t m0 x3 h4 y1a ff1 fs2 fc0 sc1 ls1 ws0">情形作出介绍。<span class="ff4"> </span></div><div class="t m0 x4 h4 y1b ff1 fs2 fc0 sc1 ls1 ws0">转载请注明作者和<span class="_ _0"> </span><span class="ff4 ls11">www<span class="_ _4"></span>.osgchina.org </span></div><div class="t m0 x4 h5 y1c ff1 fs2 fc0 sc1 ls1 ws0">本文在写作过程中将会用到一些专有名词,<span class="_ _b"></span>它们可能与读者阅读的其它文章中所述有所</div><div class="t m0 x3 h4 y1d ff1 fs2 fc0 sc1 ls1 ws0">差异,现列举如下:<span class="ff4"> </span></div><div class="t m0 x4 h4 y1e ff1 fs2 fc0 sc1 ls1 ws0">场景图形<span class="ff4 ls12">-SceneGraph</span>;<span class="_ _b"></span>场景子树<span class="ff4 lsd">-Subgraph</span>;<span class="_ _b"></span>节点<span class="ff4 ls13">-Node</span>;<span class="_ _b"></span>摄像机<span class="ff4 ls14">-Camera</span>;<span class="_ _b"></span>渲染器<span class="ff4 ls15">-Renderer</span>;</div><div class="t m0 x3 h4 y1f ff1 fs2 fc0 sc1 ls1 ws0">窗口<span class="ff4 lsf">-W<span class="_ _4"></span>indow<span class="ff1 ls1">;<span class="_ _10"></span>视口<span class="ff4 ls16">-V<span class="_ _4"></span>iewport<span class="ff1 ls1">;<span class="_ _10"></span>场景<span class="ff4 ls14">-Scene</span>;<span class="_ _11"></span>视图<span class="ff4 ls5">-V<span class="_ _4"></span>iew<span class="ff1 ls1">;<span class="_ _11"></span>视景器<span class="ff4 ls17">-V<span class="_ _4"></span>iewer<span class="ff1 ls1">;</span></span></span></span></span></span></span></span></div><div class="t m0 x5 h4 y20 ff1 fs2 fc0 sc1 ls1 ws0">漫游器<span class="ff4 ls18">-Manipulator</span>;</div><div class="t m0 x3 h4 y21 ff1 fs2 fc0 sc1 ls1 ws0">访问器<span class="ff4 ls19">-V<span class="_ _4"></span>isitor<span class="ff1 ls1">;回调</span><span class="ls1a">-Callback<span class="ff1 ls1">;事件</span><span class="ls1b">-Event<span class="ff1 ls1">;更新</span><span class="ls17">-Update<span class="ff1 ls1">;筛选</span><span class="ls12">-Cull<span class="ff1 ls1">;绘制</span></span>-Draw<span class="ff1 ls1">。<span class="ff4"> </span></span></span></span></span></span></div><div class="t m0 x4 h4 y22 ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x3 h6 y23 ff1 fs3 fc0 sc0 ls1c ws0">第一日<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x4 h4 y24 ff1 fs2 fc0 sc1 ls1d ws0">好了,在开始第一天的行程之前,请先打开您最惯用的编<span class="_ _8"></span>程工具吧:<span class="ff4 ls1e">Vi<span class="_ _d"></span>s<span class="_ _d"></span>u<span class="_ _12"></span>a<span class="_ _d"></span>l<span class="_ _12"></span>S<span class="_ _d"></span>t<span class="_ _d"></span>u<span class="_ _d"></span>d<span class="_ _12"></span>i<span class="_ _d"></span>o<span class="_ _13"></span></span><span class="ls1">?</span></div><div class="t m0 x3 h4 y25 ff4 fs2 fc0 sc1 ls1f ws0">CodeBlocks<span class="ff1 ls1">?</span><span class="ls3">UltraEdit<span class="ff1 ls1">?<span class="_ _8"></span><span class="ff4 lsd">SourceInsight<span class="ff1 ls1">?</span><span class="ls20">Em<span class="_ _4"></span>acs<span class="ff1 ls1">?</span><span class="ls1e">Vi<span class="_ _12"></span>m<span class="_ _d"></span><span class="ff1 ls1">?或者只是附件里那个制作低劣的记事</span></span></span></span></span></span></div><div class="t m0 x3 h4 y26 ff1 fs2 fc0 sc1 ls21 ws0">本……总之请打开它们,打开<span class="_ _3"> </span><span class="ff4 ls15">OpenSceneGraph-2.6.0<span class="_ _14"> </span></span>的源代码文件夹,打开</div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
</body>
</html>
<div id="pf2" class="pf w0 h0" data-page-no="2"><div class="pc pc2 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/6251b9056caf596192437719/bg2.jpg"><div class="t m0 x3 h4 y27 ff4 fs2 fc0 sc1 ls18 ws0">osgV<span class="_ _4"></span>iewer/V<span class="_ _6"></span>iewerBase.cpp<span class="_ _5"> </span><span class="ff1 ls1">这个文件……同样的话就不再重复了。但是如果您没有这样做,</span></div><div class="t m0 x3 h4 y28 ff1 fs2 fc0 sc1 ls1 ws0">而仅仅是一边聊着<span class="_ _0"> </span><span class="ff4 ls22">QQ</span>,<span class="_ _15"></span>一边在电话里和女朋友发着牢骚,<span class="_ _15"></span>一边还要应付突然冲进来的老板,</div><div class="t m0 x3 h4 y29 ff1 fs2 fc0 sc1 ls1 ws0">一边打开这篇教程的话——对不起,<span class="_ _c"></span>我想您会在<span class="_ _0"> </span><span class="ff4">1<span class="_ _16"> </span></span>分钟以内就对其感到厌烦,<span class="_ _c"></span>因为它就像天</div><div class="t m0 x3 h5 y2a ff1 fs2 fc0 sc1 ls1 ws0">书一样,<span class="_ _c"></span>或者就像一坨乱七八糟的毛线团,<span class="_ _7"></span>只有家里那只打着哈欠的小猫可能会过来捅上一</div><div class="t m0 x3 h4 y2b ff1 fs2 fc0 sc1 ls1 ws0">下。<span class="ff4"> </span></div><div class="t m0 x4 h4 y2c ff1 fs2 fc0 sc1 ls1 ws0">不过不用担心,<span class="_ _c"></span>如果您已经耐着性子读到了这里,<span class="_ _c"></span>并且已经看到了<span class="_ _0"> </span><span class="ff4 ls3">V<span class="_ _4"></span>iewerBase.cpp<span class="_"> </span><span class="ff1 ls1">中总</span></span></div><div class="t m0 x3 h4 y2d ff1 fs2 fc0 sc1 ls1 ws0">共<span class="_ _0"> </span><span class="ff4 lsa">752<span class="_"> </span></span>行规规矩矩的代码,<span class="_ _c"></span>那么我会尽全力协助您继续走完漫长的一帧的旅程,<span class="_ _7"></span>并在其中把</div><div class="t m0 x3 h4 y2e ff1 fs2 fc0 sc1 ls1 ws0">自己所知所得(不仅仅局限于那个该死的<span class="_ _e"> </span><span class="ff4 ls23">frame<span class="_ _e"> </span></span>函数,而是遍及<span class="_ _e"> </span><span class="ff4 ls22">OSG<span class="_ _e"> </span></span>的方方面面)全数灌</div><div class="t m0 x3 h5 y2f ff1 fs2 fc0 sc1 ls1 ws0">输给您。<span class="_ _a"></span>当然也希望您尽全力给我以打压、<span class="_ _9"></span>批评和指正,<span class="_ _a"></span>我只是一个普普通通的梦想着先找</div><div class="t m0 x3 h5 y30 ff1 fs2 fc0 sc1 ls1 ws0">个女朋友成家立业的毛头小子而已,<span class="_ _c"></span>如果我的话全都是对的,<span class="_ _7"></span>那么那些真正的图形学权威们</div><div class="t m0 x3 h4 y31 ff1 fs2 fc0 sc1 ls1 ws0">一定都住在寒风刺骨的珠穆朗玛峰顶上。<span class="ff4"> </span></div><div class="t m0 x4 h4 y32 ff1 fs2 fc0 sc1 ls1 ws0">好了,废话不再多说。我们这就开始。<span class="ff4"> </span></div><div class="t m0 x4 h4 y33 ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h7 y34 ff1 fs2 fc0 sc0 ls24 ws0">当前位置:<span class="ff2 sc1 ls19">osgV<span class="_ _4"></span>iewer/V<span class="_ _8"></span>iewerBase.cpp<span class="_"> </span><span class="ff1 sc0 ls1">第<span class="_ _0"> </span></span><span class="lsa">571<span class="_"> </span><span class="ff1 sc0 ls1">行,</span><span class="ls5">osgV<span class="_ _4"></span>iewer::V<span class="_ _8"></span>iewerBase::frame() </span></span></span></div><div class="t m0 x4 h4 y35 ff4 fs2 fc0 sc1 ls6 ws0">frame<span class="_"> </span><span class="ff1 ls1">函数的内容本身几乎一眼就可以看完。<span class="_ _4"></span>不过要注意的是,<span class="_ _8"></span>这个函数是<span class="_ _0"> </span><span class="ff4 ls8">V<span class="_ _4"></span>iewerBase</span></span></div><div class="t m0 x3 h4 y36 ff1 fs2 fc0 sc1 ls1 ws0">类的成员函数,而非<span class="_ _f"> </span><span class="ff4 ls25">V<span class="_ _4"></span>iewer<span class="_ _5"> </span><span class="ff1 ls1">类。因此,无论对于单视景器的<span class="_ _f"> </span></span>V<span class="_ _4"></span>iewer<span class="_ _5"> </span><span class="ff1 ls1">类,还是多视景器的</span></span></div><div class="t m0 x3 h4 y37 ff4 fs2 fc0 sc1 ls26 ws0">CompositeV<span class="_ _6"></span>iewer<span class="_"> </span><span class="ff1 ls1">类,<span class="_ _1"></span><span class="ff4 ls6">frame<span class="_"> </span><span class="ff1 ls1">函数的内容都是相同的<span class="_ _1"></span>(因为它们都没有再重写这个函数的内容)<span class="_ _1"></span>。<span class="_ _1"></span><span class="ff4"> </span></span></span></span></div><div class="t m0 x4 h4 y38 ff1 fs2 fc0 sc1 ls1 ws0">该函数所执行的主要工作如下:<span class="ff4"> </span></div><div class="t m0 x4 h4 y39 ff4 fs2 fc0 sc1 ls1 ws0">1<span class="ff1">、<span class="_ _1"></span>如果这是仿真系统启动后的第一帧,<span class="_ _1"></span>则执行<span class="_ _17"> </span><span class="ff4 ls25">viewerInit()</span>;<span class="_ _1"></span>此时如果还没有执行<span class="_ _17"> </span><span class="ff4 ls3">realize()</span></span></div><div class="t m0 x3 h4 y3a ff1 fs2 fc0 sc1 ls1 ws0">函数,则执行它。<span class="ff4"> </span></div><div class="t m0 x4 h4 y3b ff4 fs2 fc0 sc1 ls1 ws0">2<span class="ff1">、执行<span class="_ _0"> </span></span><span class="ls27">advance<span class="_"> </span></span><span class="ff1">函数。</span> </div><div class="t m0 x4 h4 y3c ff4 fs2 fc0 sc1 ls1 ws0">3<span class="ff1 ls28">、执<span class="_ _12"></span>行<span class="_ _18"> </span></span><span class="ls29">eventT<span class="_ _8"></span>raversal<span class="_"> </span><span class="ff1 ls1">函数,<span class="_ _6"></span>顾名思义,<span class="_ _6"></span>这个函数将负责处理系统产生的各种事件,<span class="_ _4"></span>诸</span></span></div><div class="t m0 x3 h5 y3d ff1 fs2 fc0 sc1 lsb ws0">如鼠标的移动,点击,键盘的<span class="_ _8"></span>响应,窗口的关闭等等,以<span class="_ _8"></span>及摄像机与场景图形的事件回<span class="_ _8"></span>调</div><div class="t m0 x3 h4 y3e ff1 fs2 fc0 sc1 ls1 ws0">(<span class="ff4 ls2a">EventCallback</span>)<span class="_ _1"></span>。<span class="ff4"> </span></div><div class="t m0 x4 h4 y3f ff4 fs2 fc0 sc1 ls1 ws0">4<span class="ff1">、执行<span class="_ _18"> </span></span><span class="ls2b">up<span class="_ _2"></span>dateT<span class="_ _8"></span>raversal<span class="_ _19"> </span><span class="ff1 ls1">函数,这个函数负责遍历所有的更新回调(</span><span class="ls2c">UpdateCa<span class="_ _2"></span>llback<span class="ff1 ls1">)<span class="_ _1"></span>;</span></span></span></div><div class="t m0 x3 h4 y40 ff1 fs2 fc0 sc1 ls1 ws0">除此之外,<span class="_ _a"></span>它的另一个重要任务就是负责更新<span class="_ _0"> </span><span class="ff4 ls25">DatabasePager<span class="_"> </span></span>与<span class="_ _17"> </span><span class="ff4 ls2d">ImagePager<span class="_"> </span></span>这两个重要的分</div><div class="t m0 x3 h4 y41 ff1 fs2 fc0 sc1 ls1 ws0">页数据处理组件。<span class="ff4"> </span></div><div class="t m0 x4 h4 y42 ff4 fs2 fc0 sc1 ls1 ws0">5<span class="ff1 ls2e">、执<span class="_ _13"> </span>行<span class="_ _f"> </span></span><span class="ls12">renderingT<span class="_ _4"></span>raversals<span class="_"> </span><span class="ff1 ls1">函数,<span class="_ _9"></span>这里将使用较为复杂的线程处理方法,<span class="_ _9"></span>完成场景的筛</span></span></div><div class="t m0 x3 h4 y43 ff1 fs2 fc0 sc1 ls1 ws0">选(<span class="ff4 ls9">cull</span>)和绘制(<span class="ff4 ls2a">draw</span>)工作。<span class="ff4"> </span></div><div class="t m0 x4 h4 y44 ff1 fs2 fc0 sc1 ls1 ws0">下面我们就按照<span class="_ _0"> </span><span class="ff4 lsa">1~5</span></div><div class="t m0 x6 h4 y45 ff1 fs2 fc0 sc1 ls1 ws0">的顺序,开始我们的源代码解读之旅。<span class="ff4"> </span></div><div class="t m0 x4 h4 y46 ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h7 y47 ff1 fs2 fc0 sc0 ls24 ws0">当前位置:<span class="ff2 sc1 ls23">osgV<span class="_ _4"></span>i<span class="_ _2"></span>ewer/V<span class="_ _8"></span>iew<span class="_ _4"></span>.cpp<span class="_"> </span><span class="ff1 sc0 ls1">第<span class="_ _0"> </span></span><span class="lsa">227<span class="_"> </span><span class="ff1 sc0 ls24">行,</span><span class="ls14">osgV<span class="_ _4"></span>iewer::V<span class="_ _8"></span>iew::init()<span class="ff4 ls1"> </span></span></span></span></div><div class="t m0 x4 h4 y48 ff4 fs2 fc0 sc1 ls19 ws0">V<span class="_ _4"></span>iewer::viewerInit<span class="_"> </span><span class="ff1 ls1">函数只做了一件事,就是调用<span class="_ _17"> </span></span><span class="ls2a">V<span class="_ _4"></span>iew::init()<span class="ff1 ls1">函数,而这个<span class="_ _0"> </span></span><span class="ls9">init<span class="_"> </span><span class="ff1 ls1">函数的工</span></span></span></div><div class="t m0 x3 h4 y49 ff1 fs2 fc0 sc1 ls1 ws0">作似乎也是一目了然的:无非就是完成视景器的初始化工作而已。<span class="ff4"> </span></div><div class="t m0 x4 h5 y4a ff1 fs2 fc0 sc1 ls1 ws0">不过在我们离开这个函数,<span class="_ _a"></span>继续我们的旅程之前,<span class="_ _9"></span>还是仔细探究一下,<span class="_ _a"></span>这个初始化工作</div><div class="t m0 x3 h4 y4b ff1 fs2 fc0 sc1 ls1 ws0">到底包含了什么?<span class="ff4"> </span></div><div class="t m0 x4 h5 y4c ff1 fs2 fc0 sc1 ls1 ws0">阅读某个函数的源代码过程中,<span class="_ _c"></span>如果能够大致知道这个函数的主要工作,<span class="_ _7"></span>并了解其中用</div><div class="t m0 x3 h5 y4d ff1 fs2 fc0 sc1 ls1 ws0">到的变量的功能,<span class="_ _a"></span>那么即使只有很少的注释内容,<span class="_ _9"></span>应该也可以顺利地读完所有代码。<span class="_ _a"></span>如果对</div><div class="t m0 x3 h5 y4e ff1 fs2 fc0 sc1 ls1 ws0">一些命名晦涩的变量不甚理解,<span class="_ _c"></span>或者根本不知道这个函数于运行流程中有何用途,<span class="_ _7"></span>那么理解</div><div class="t m0 x3 h4 y4f ff1 fs2 fc0 sc1 ls1 ws0">源代码的过程就会麻烦很多。<span class="ff4"> </span></div><div class="t m0 x4 h4 y50 ff4 fs2 fc0 sc1 lsd ws0">V<span class="_ _4"></span>iew::init<span class="_ _5"> </span><span class="ff1 ls1">函数中出现了两个重要的类成员变量:</span><span class="ls22">_eventQueue<span class="_ _3"> </span><span class="ff1 ls1">和</span><span class="ls2a">_cam<span class="_ _8"></span>eraManipulator<span class="ff1 ls1">,</span></span></span></div><div class="t m0 x3 h4 y51 ff1 fs2 fc0 sc1 ls1 ws0">并且还将一个<span class="_ _0"> </span><span class="ff4 ls25">osgGA::GUIEventAdapter<span class="_ _17"> </span></span>的实例传入后者的初始化函数。<span class="ff4"> </span></div><div class="t m0 x4 h4 y52 ff1 fs2 fc0 sc1 ls1 ws0">代码如下:<span class="ff4"> </span></div><div class="t m0 x4 h4 y53 ff4 fs2 fc0 sc1 ls19 ws4">osg::ref_ptr<osgGA::GUIEventAdapter> in<span class="_ _8"></span><span class="lsd ws5">itEvent = _eventQue<span class="_ _8"></span>ue->createEvent(); </span></div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
<div id="pf3" class="pf w0 h0" data-page-no="3"><div class="pc pc3 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/6251b9056caf596192437719/bg3.jpg"><div class="t m0 x4 h4 y27 ff4 fs2 fc0 sc1 ls2a ws0">initEvent->setEven<span class="_ _8"></span>tT<span class="_ _4"></span>ype(os<span class="_ _8"></span>gGA::GUIEventAdapter::FRAME); </div><div class="t m0 x4 h4 y28 ff4 fs2 fc0 sc1 ls15 ws6">if (_cameraManipulator<span class="_ _4"></span>.valid()) </div><div class="t m0 x4 h4 y29 ff4 fs2 fc0 sc1 ls18 ws7"> _cameraManipul<span class="_ _8"></span>ator->i<span class="_ _8"></span>nit(*initEvent, <span class="_ _c"></span>*this);<span class="_ _8"></span> </div><div class="t m0 x4 h4 y2a ff1 fs2 fc0 sc1 ls1 ws0">从变量的名称可以猜测出<span class="ff4 ls22">_eventQueue<span class="_ _16"> </span></span>的功能,它用于储存该视景器的事件队列。<span class="ff4 ls22">OS<span class="_ _2"></span>G</span></div><div class="t m0 x3 h4 y2b ff1 fs2 fc0 sc1 ls1 ws0">中代表事件的类是<span class="_ _0"> </span><span class="ff4 ls2d">osgGA::GUI<span class="_ _8"></span>EventAdapter<span class="ff1 ls1">,<span class="_ _a"></span>它可以用于表达各种类型的鼠标、<span class="_ _a"></span>键盘、<span class="_ _9"></span>触压</span></span></div><div class="t m0 x3 h4 y2c ff1 fs2 fc0 sc1 ls1 ws0">笔和窗口事件。<span class="_ _1"></span>在用户程序中,<span class="_ _1"></span>我们往往通过继承<span class="_ _17"> </span><span class="ff4 ls2f">osgGA::GUIEventHandler<span class="_ _17"> </span></span><span class="ls10">类,<span class="_ _1"></span><span class="ls1">并重写<span class="_ _17"> </span><span class="ff4 ls30">handle</span></span></span></div><div class="t m0 x3 h4 y2d ff1 fs2 fc0 sc1 ls31 ws0">函数的方法,获取实时的鼠标<span class="ff4 ls1">/<span class="_ _17"> </span></span>键盘输入,并进而实现相应的用户代码(参见</div><div class="t m0 x3 h4 y2e ff4 fs2 fc0 sc1 ls1f ws0">osgkeyboardm<span class="_ _8"></span>ouse<span class="ff1 ls1">)<span class="_ _1"></span>。<span class="ff4"> </span></span></div><div class="t m0 x4 h4 y2f ff4 fs2 fc0 sc1 ls2b ws0">_eventQueu<span class="_ _2"></span>e<span class="_ _e"> </span><span class="ff1 ls1">除了保存一个<span class="_ _e"> </span></span><span class="ls32">GUIEventAdapter<span class="_ _16"> </span><span class="ff1 ls1">的链表之外,还提供了一系列对链表及其</span></span></div><div class="t m0 x3 h4 y30 ff1 fs2 fc0 sc1 ls1 ws0">元素的操作函数,这其中,<span class="ff4 ls7">createEvent<span class="_"> </span></span>函数的作用是分配和返回一个新的<span class="_ _17"> </span><span class="ff4 ls32">GUIEventAdapter</span></div><div class="t m0 x3 h4 y31 ff1 fs2 fc0 sc1 ls1 ws0">事件的指针。<span class="ff4"> </span></div><div class="t m0 x4 h4 y32 ff1 fs2 fc0 sc1 ls1 ws0">随后,这个新事件的类型被指定为<span class="_ _0"> </span><span class="ff4 ls33">FRAME<span class="_"> </span></span>事件,即每帧都会触发的一个事件。<span class="ff4"> </span></div><div class="t m0 x4 h4 y33 ff1 fs2 fc0 sc1 ls1 ws0">那么,<span class="ff4 ls16">_cam<span class="_ _8"></span>eraManipulator<span class="_"> </span><span class="ff1 ls1">呢?没错,它就是视景器中所用的场景漫游器的实例。通常</span></span></div><div class="t m0 x3 h4 y34 ff1 fs2 fc0 sc1 ls34 ws0">我们都会使用<span class="_ _3"> </span><span class="ff4 lsd">setCam<span class="_ _8"></span>eraManipulator<span class="_ _1a"> </span><span class="ff1 ls34">来设置这个变量的内容,例如轨迹球漫游器</span></span></div><div class="t m0 x3 h4 y35 ff1 fs2 fc0 sc1 ls1 ws0">(<span class="ff4 ls35">T<span class="_ _8"></span>rackball<span class="_ _8"></span>Manipulator<span class="ff1 ls24">)<span class="_ _8"></span>可以使用鼠标拖动来观察场景,<span class="_ _8"></span>而驾驶漫游器(<span class="ff4 lsf">DriveMani<span class="_ _8"></span>pulator<span class="ff1 ls1">)</span></span></span></span></div><div class="t m0 x3 h4 y36 ff1 fs2 fc0 sc1 ls1 ws0">则使用类似于汽车驾驶的效果来实现场景的漫游。<span class="ff4"> </span></div><div class="t m0 x4 h4 y37 ff1 fs2 fc0 sc1 ls1 ws0">上面的代码将新创建的<span class="_ _19"> </span><span class="ff4 ls33">FRAME<span class="_ _19"> </span></span>事件和<span class="_ _1b"> </span><span class="ff4 ls25">V<span class="_ _4"></span>iewer<span class="_ _19"> </span><span class="ff1 ls1">对象本身传递给</span><span class="ls1f">_cameraManipulator<span class="_ _18"> </span><span class="ff1 ls1">的</span></span></span></div><div class="t m0 x3 h4 y38 ff4 fs2 fc0 sc1 ls36 ws0">init<span class="_"> </span><span class="ff1 ls1">函数,<span class="_ _8"></span>不同的漫游器(如<span class="_ _17"> </span><span class="ff4 ls36">T<span class="_ _8"></span>rackballMa<span class="_ _8"></span>nipulator<span class="_ _8"></span><span class="ff1 ls1">、<span class="ff4 ls37">DriveMani<span class="_ _8"></span>pulator<span class="ff1 ls1">)<span class="_ _8"></span>会重写各自的<span class="_ _17"> </span><span class="ff4 ls2d">init<span class="_"> </span></span>函</span></span></span></span></span></div><div class="t m0 x3 h5 y39 ff1 fs2 fc0 sc1 ls1 ws0">数,<span class="_ _a"></span>实现自己所需的初始化工作。<span class="_ _9"></span>如果读者希望自己编写一个场景的漫游器,<span class="_ _a"></span>那么覆写并使</div><div class="t m0 x3 h4 y3a ff1 fs2 fc0 sc1 ls1 ws0">用<span class="_ _e"> </span><span class="ff4 lsf">osgGA::MatrixManipula<span class="_ _8"></span>tor::init<span class="_ _16"> </span><span class="ff1 ls1">就可以灵活地初始化自定义漫游器的功能了,它的调用时</span></span></div><div class="t m0 x3 h4 y3b ff1 fs2 fc0 sc1 ls1 ws0">机就在这里。<span class="ff4"> </span></div><div class="t m0 x4 h4 y3c ff1 fs2 fc0 sc1 ls1 ws0">那么,回到<span class="_ _17"> </span><span class="ff4 ls14">viewerInit<span class="_"> </span></span>函数……很好,这次似乎没有更多的内容了。<span class="_ _8"></span>没想到一个短短的</div><div class="t m0 x3 h4 y3d ff1 fs2 fc0 sc1 ls1 ws0">函数竟然包含了那么多的信息,看来草率地阅读还真是使不得。<span class="ff4"> </span></div><div class="t m0 x4 h4 y3e ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h7 y3f ff1 fs2 fc0 sc0 ls24 ws0">解读成果:<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x4 h4 y40 ff4 fs2 fc0 sc1 ls1f ws0">osgGA::Event<span class="_ _8"></span>Queue::createEvent<span class="ff1 ls1">,<span class="_ _b"></span><span class="ff4 ls26">osgGA::MatrixMani<span class="_ _8"></span>pulator::init<span class="_ _8"></span><span class="ff1 ls1">,<span class="_ _11"></span><span class="ff4 lsd">osgV<span class="_ _6"></span>iewer::V<span class="_ _4"></span>iew::init<span class="ff1 ls1">,</span></span></span></span></span></div><div class="t m0 x3 h4 y41 ff4 fs2 fc0 sc1 ls3 ws0">osgV<span class="_ _4"></span>iewer::V<span class="_ _6"></span>iewer::viewerInit<span class="ff1 ls1">。<span class="ff4"> </span></span></div><div class="t m0 x4 h7 y42 ff1 fs2 fc0 sc0 ls24 ws0">悬疑列表:<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x4 h4 y43 ff1 fs2 fc0 sc1 ls1 ws0">无。<span class="ff4"> </span></div><div class="t m0 x3 h6 y54 ff1 fs3 fc0 sc0 ls1c ws0">第二日<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x4 h7 y55 ff1 fs2 fc0 sc0 ls24 ws0">当前位置:<span class="ff2 sc1 ls38">osgV<span class="_ _4"></span>iewer/V<span class="_ _8"></span>iewer<span class="_ _6"></span>.cpp<span class="_"> </span><span class="ff1 sc0 ls1">第<span class="_ _0"> </span></span><span class="lsc">385<span class="_"> </span><span class="ff1 sc0 ls24">行,</span></span>osgV<span class="_ _8"></span>iewer::V<span class="_ _4"></span>iewer::realiz<span class="_ _8"></span>e()<span class="ff4 ls1"> </span></span></div><div class="t m0 x4 h4 y56 ff4 fs2 fc0 sc1 ls25 ws0">V<span class="_ _4"></span>iewer::realize<span class="_"> </span><span class="ff1 ls1">函数是我们十分熟悉的另一个函数,<span class="_ _a"></span>从<span class="_ _17"> </span><span class="ff4 ls4">OSG<span class="_ _0"> </span></span>问世以来,<span class="_ _9"></span>我们就习惯于在</span></div><div class="t m0 x3 h4 y57 ff1 fs2 fc0 sc1 ls1 ws0">进入仿真循环之前调用它<span class="_ _a"></span>(现在的<span class="_ _0"> </span><span class="ff4 ls4">OSG<span class="_"> </span></span>会自动调用这个函数,<span class="_ _9"></span>如果我们忘记的话)<span class="_ _1"></span>,<span class="_ _a"></span>以完成</div><div class="t m0 x3 h5 y58 ff1 fs2 fc0 sc1 ls1 ws0">窗口和场景的“设置”工作。那么,什么叫做“设置”<span class="_ _1c"></span>,这句简单的场景设置又包含了多少</div><div class="t m0 x3 h4 y59 ff1 fs2 fc0 sc1 ls1 ws0">内容呢?艰辛的旅程就此开始吧。<span class="ff4"> </span></div><div class="t m0 x4 h4 y5a ff1 fs2 fc0 sc1 ls1 ws0">首先是一行:<span class="_ _6"></span><span class="ff4 ls3">setCameraW<span class="_ _4"></span>ithFocus(0)<span class="ff1 ls1">,<span class="_ _6"></span>其内容无非是设置类变量<span class="ff4 ls7">_cameraW<span class="_ _8"></span>ithFocus<span class="_"> </span><span class="ff1 ls10">指向</span></span></span></span></div><div class="t m0 x3 h4 y5b ff1 fs2 fc0 sc1 ls1 ws0">的内容为<span class="_ _0"> </span><span class="ff4 ls39">NULL</span>。<span class="_ _4"></span>至于这个<span class="_ _6"></span>“带有焦点的摄像机”<span class="_ _4"></span>是什么意思,<span class="_ _6"></span>我们似乎明白,<span class="_ _4"></span>似乎又不明</div><div class="t m0 x3 h4 y5c ff1 fs2 fc0 sc1 ls1 ws0">白,就先放入一个“悬疑列表”<span class="_ _1"></span>(<span class="ff4 lsf ws8">T<span class="_ _4"></span>odo List<span class="ff1 ls1 ws0">)中好<span class="_ _8"></span>了。<span class="ff4"> </span></span></span></div><div class="t m0 x4 h4 y5d ff1 fs2 fc0 sc1 ls1 ws0">下面遇到的函数就比较重要了,因为我们将会在很多地方遇到它:<span class="ff4"> </span></div><div class="t m0 x4 h4 y5e ff4 fs2 fc0 sc1 ls3a ws9">Context<span class="_ _2"></span>s context<span class="_ _2"></span>s; </div><div class="t m0 x4 h4 y5f ff4 fs2 fc0 sc1 ls18 ws0">getContexts(contex<span class="_ _8"></span>ts); </div><div class="t m0 x4 h4 y60 ff1 fs2 fc0 sc1 ls1 ws0">变量<span class="_ _0"> </span><span class="ff4 ls18">contexts<span class="_"> </span></span>是一个保存了<span class="_ _0"> </span><span class="ff4 ls3b">osg::GraphicsContext<span class="_"> </span></span>指<span class="_ _8"></span>针的向量组,而<span class="_ _0"> </span><span class="ff4 lsd">V<span class="_ _4"></span>iewer::getContexts</span></div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
<div id="pf4" class="pf w0 h0" data-page-no="4"><div class="pc pc4 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/6251b9056caf596192437719/bg4.jpg"><div class="t m0 x3 h4 y27 ff1 fs2 fc0 sc1 ls1 ws0">函数的作用是获取所有的图形上下文,并保存到这个向量组中来。<span class="ff4"> </span></div><div class="t m0 x4 h4 y28 ff1 fs2 fc0 sc1 ls1 ws0">对于需要将<span class="_ _0"> </span><span class="ff4 ls4">OSG<span class="_"> </span></span>嵌合到各式各样的<span class="_ _0"> </span><span class="ff4 ls22">GUI<span class="_"> </span></span>系统<span class="_ _4"></span>(如<span class="_ _17"> </span><span class="ff4 ls5">MFC</span>,<span class="_ _4"></span><span class="ff4 ls22">Qt<span class="ff1 ls1">,<span class="_ _6"></span><span class="ff4 ls3c">wxW<span class="_ _8"></span>idgets<span class="_"> </span><span class="ff1 ls1">等)<span class="_ _6"></span>的朋友来</span></span></span></span></div><div class="t m0 x3 h4 y29 ff1 fs2 fc0 sc1 ls1 ws0">说,<span class="_ _7"></span><span class="ff4 ls2a">osg::GraphicsC<span class="_ _8"></span>ontext<span class="_"> </span><span class="ff1 ls1">类是经常要打交道的对象之一。<span class="_ _c"></span>一种常用的嵌入方式也许是这样实</span></span></div><div class="t m0 x3 h4 y2a ff1 fs2 fc0 sc1 ls1 ws0">现的:<span class="ff4"> </span></div><div class="t m0 x4 h4 y2b ff4 fs2 fc0 sc1 lsf ws8">osg::ref_ptr<osg::GraphicsC<span class="_ _8"></span>ontext::T<span class="_ _4"></span>raits> tr<span class="ls26 wsa">aits = new osg::G<span class="_ _8"></span>raphicsContext:<span class="_ _8"></span>:T<span class="_ _8"></span>raits; </span></div><div class="t m0 x4 h4 y2c ff4 fs2 fc0 sc1 lsd wsb">osg::ref_ptr<osg::Referen<span class="_ _8"></span>ced> windata = <span class="_ _17"> </span> </div><div class="t m0 x4 h4 y2d ff4 fs2 fc0 sc1 ls19 wsc"> new <span class="_ _c"></span>osgV<span class="_ _6"></span>iewer::GraphicsW<span class="_ _4"></span>indowW<span class="_ _8"></span>in32::W<span class="_ _4"></span>indowData(hWnd); </div><div class="t m0 x4 h4 y2e ff4 fs2 fc0 sc1 ls2a wsd">traits->x = 0; </div><div class="t m0 x4 h4 y2f ff4 fs2 fc0 sc1 ls2a wsd">traits->y = 0; </div><div class="t m0 x4 h4 y30 ff1 fs2 fc0 sc1 ls1 ws0">……<span class="ff4"> </span></div><div class="t m0 x4 h4 y31 ff4 fs2 fc0 sc1 ls1a wse">traits->inheritedW<span class="_ _4"></span>indowData = windata; </div><div class="t m0 x4 h4 y32 ff4 fs2 fc0 sc1 ls3b wsf">osg::GraphicsCon<span class="_ _8"></span>text* gc = osg::G<span class="_ _8"></span>raphicsContext:<span class="_ _8"></span>:createGraphicsCo<span class="_ _8"></span>ntext(traits.get()); </div><div class="t m0 x4 h4 y33 ff4 fs2 fc0 sc1 ls3 ws10">Camera* camera = viewer<span class="_ _6"></span>.getCamera(); </div><div class="t m0 x4 h4 y34 ff4 fs2 fc0 sc1 ls18 ws0">camera->setGraphi<span class="ls15">csCont<span class="_ _8"></span>ext(gc); </span></div><div class="t m0 x4 h4 y35 ff1 fs2 fc0 sc1 ls1 ws0">……<span class="ff4"> </span></div><div class="t m0 x4 h4 y36 ff4 fs2 fc0 sc1 ls25 ws0">viewer<span class="_ _4"></span>.setCamera(camera); </div><div class="t m0 x4 h4 y37 ff1 fs2 fc0 sc1 ls1 ws0">这个过程虽然比较繁杂,<span class="_ _1d"></span>但是顺序还是十分清楚的:<span class="_ _1d"></span>首先设置嵌入窗口的特性<span class="_ _1d"></span>(<span class="ff4 ls3d">T<span class="_ _4"></span>raits<span class="ff1 ls1">)<span class="_ _1"></span>,</span></span></div><div class="t m0 x3 h4 y38 ff1 fs2 fc0 sc1 ls1 ws0">例如<span class="_ _19"> </span><span class="ff4">X</span>、<span class="ff4">Y<span class="_ _19"> </span></span>位置,宽度和高度,以及父窗口的句柄(<span class="ff4 ls3e">i<span class="_ _2"></span>nherited<span class="_ _2"></span>W<span class="_ _4"></span>indo<span class="_ _2"></span>wData<span class="ff1 ls1">)<span class="_ _1"></span>;然后根据特性</span></span></div><div class="t m0 x3 h4 y39 ff1 fs2 fc0 sc1 ls24 ws0">的设置创建一个新的图形设备<span class="_ _2"></span>上下文(<span class="ff4 ls26">GraphicsContext</span><span class="ls1">)<span class="_ _1"></span><span class="ls24">,将其赋予场景所用的摄像机。<span class="_ _2"></span>而</span></span></div><div class="t m0 x3 h4 y3a ff1 fs2 fc0 sc1 ls1 ws0">我们在<span class="_ _0"> </span><span class="ff4 ls9">getContexts<span class="_"> </span></span>函数中<span class="_ _8"></span>所要获取的,也许就包括这样一个用户建立的<span class="_ _0"> </span><span class="ff4 ls32">GraphicsCont<span class="_ _8"></span>ext<span class="_"> </span><span class="ff1 ls1">设</span></span></div><div class="t m0 x3 h4 y3b ff1 fs2 fc0 sc1 ls1 ws0">备。<span class="ff4"> </span></div><div class="t m0 x4 h4 y3c ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h7 y3d ff1 fs2 fc0 sc0 ls24 ws0">当前位置:<span class="ff2 sc1 ls38">osgV<span class="_ _4"></span>iewer/V<span class="_ _8"></span>iewer<span class="_ _6"></span>.cpp<span class="_"> </span><span class="ff1 sc0 ls1">第<span class="_ _0"> </span></span><span class="lsc">1061<span class="_"> </span><span class="ff1 sc0 ls1">行,<span class="_ _2"></span></span><span class="ls12">osgV<span class="_ _4"></span>iewer::Viewer::getContexts()<span class="ff4 ls1"> </span></span></span></span></div><div class="t m0 x4 h4 y3e ff1 fs2 fc0 sc1 ls3f ws0">在这个函数之中,首先判断场景的主摄像机<span class="ff4 ls40">_camera<span class="_ _1a"> </span></span>是否包含了一个有效的</div><div class="t m0 x3 h4 y3f ff4 fs2 fc0 sc1 ls32 ws0">GraphicsCont<span class="_ _8"></span>ext<span class="_"> </span><span class="ff1 ls1">设备,<span class="_ _7"></span>然后再遍历所有的从摄像机<span class="ff4 ls37">_slaves<span class="_ _c"></span><span class="ff1 ls1">(一个视景器可以包含一个主摄像</span></span></span></div><div class="t m0 x3 h4 y40 ff1 fs2 fc0 sc1 ls1 ws0">级和多个从摄像机)<span class="_ _1"></span>,将所有找到的<span class="_ _0"> </span><span class="ff4 ls32">GraphicsCo<span class="_ _8"></span>ntext<span class="_"> </span><span class="ff1 ls1">图形上下文设备记录下来。<span class="ff4"> </span></span></span></div><div class="t m0 x4 h4 y41 ff1 fs2 fc0 sc1 ls1 ws0">随后,将这些<span class="_ _5"> </span><span class="ff4 ls2d">GraphicsContext<span class="_ _5"> </span></span>的指针追加到传入参数(<span class="ff4 ls1a">contexts<span class="_ _5"> </span></span>向量组)中,并使用</div><div class="t m0 x3 h4 y42 ff4 fs2 fc0 sc1 ls2a ws0">std::sort<span class="_"> </span><span class="ff1 ls1">执行了一步排序的工作,所谓的排序是按照这样的原则来进行的:<span class="ff4"> </span></span></div><div class="t m0 x4 h4 y43 ff4 fs2 fc0 sc1 ls1 ws0">1<span class="ff1">、屏幕数量较少的<span class="_ _0"> </span></span><span class="ls41">GraphicsContext<span class="_"> </span></span><span class="ff1">设备排列靠前;</span> </div><div class="t m0 x4 h4 y44 ff4 fs2 fc0 sc1 ls1 ws0">2<span class="ff1">、窗口<span class="_ _0"> </span></span>X<span class="_"> </span><span class="ff1">坐标较小的设备排列靠前;</span> </div><div class="t m0 x4 h4 y61 ff4 fs2 fc0 sc1 ls1 ws0">3<span class="ff1">、窗口<span class="_ _0"> </span></span>Y<span class="_"> </span><span class="ff1">坐标较小的设备排列靠前。</span> </div><div class="t m0 x4 h5 y62 ff1 fs2 fc0 sc1 ls1 ws0">如果希望观察自己的程序中所有的图形设备上下文,<span class="_ _c"></span>不妨使用这个函数来收集一下。<span class="_ _7"></span>简</div><div class="t m0 x3 h4 y63 ff1 fs2 fc0 sc1 ls1 ws0">单的情形下,我们的程序中只有一个主摄像机,也就只有一个<span class="_ _19"> </span><span class="ff4 ls9">GraphicsContext<span class="_ _19"> </span></span>设备,它表</div><div class="t m0 x3 h4 y64 ff1 fs2 fc0 sc1 ls1 ws0">达了一个全屏幕的图形窗口;<span class="_ _9"></span>而<span class="_ _0"> </span><span class="ff4 ls16">osgcamera</span></div><div class="t m0 x7 h5 y65 ff1 fs2 fc0 sc1 ls1 ws0">这个例子程序可以创建六个从摄像机,<span class="_ _6"></span>因此可以</div><div class="t m0 x3 h4 y66 ff1 fs2 fc0 sc1 ls1 ws0">得到六个图形上下文设备,且各个图形窗口的<span class="_ _1b"> </span><span class="ff4">X<span class="_ _f"> </span></span>坐标各不相同,这也正是这个例子想要表</div><div class="t m0 x3 h4 y67 ff1 fs2 fc0 sc1 ls1 ws0">达的。<span class="ff4"> </span></div><div class="t m0 x4 h4 y68 ff1 fs2 fc0 sc1 ls1 ws0">可是,主摄像机的<span class="_ _18"> </span><span class="ff4 ls3d">GraphicsContext<span class="_ _18"> </span></span>呢?为什么<span class="_ _18"> </span><span class="ff4 ls16">osgcamera<span class="_ _e"> </span></span>中不<span class="_ _2"></span>是七个<span class="_ _18"> </span><span class="ff4 ls3d">GraphicsContext</span></div><div class="t m0 x3 h5 y69 ff1 fs2 fc0 sc1 ls1 ws0">设备呢?答案很简单,<span class="_ _a"></span>主摄像机没有创建图形上下文,<span class="_ _9"></span>因此也就得不到设备的指针。<span class="_ _a"></span>为了理</div><div class="t m0 x3 h4 y6a ff1 fs2 fc0 sc1 ls1 ws0">解这个现象的原因,我们不妨先回到<span class="_ _0"> </span><span class="ff4 ls3">realize<span class="_"> </span></span>函数中。<span class="ff4"> </span></div><div class="t m0 x4 h4 y6b ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h7 y6c ff1 fs2 fc0 sc0 ls24 ws0">当前位置:<span class="ff2 sc1 ls38">osgV<span class="_ _4"></span>iewer/V<span class="_ _8"></span>iewer<span class="_ _6"></span>.cpp<span class="_"> </span><span class="ff1 sc0 ls1">第<span class="_ _0"> </span></span><span class="lsc">394<span class="_"> </span><span class="ff1 sc0 ls24">行,</span></span>osgV<span class="_ _8"></span>iewer::V<span class="_ _4"></span>iewer::realiz<span class="_ _8"></span>e()<span class="ff4 ls1"> </span></span></div><div class="t m0 x4 h4 y6d ff1 fs2 fc0 sc1 ls1 ws0">有一个显而易见的事实是:当程序还没有进入仿真循环,且对于<span class="_ _f"> </span><span class="ff4 ls7">osgV<span class="_ _4"></span>iewer::V<span class="_ _6"></span>iewer<span class="_ _f"> </span><span class="ff1 ls1">还</span></span></div><div class="t m0 x3 h4 y6e ff1 fs2 fc0 sc1 ls1 ws0">没有任何的操作之时,系统是不会存在任何图形上下文的;创建一个新的<span class="_ _0"> </span><span class="ff4 ls2a">osg::Camera<span class="_"> </span></span>对象</div><div class="t m0 x3 h4 y6f ff1 fs2 fc0 sc1 ls1 ws0">也不会为其自动分配图形上下文。但是,图形上下文<span class="_ _19"> </span><span class="ff4 ls37">GraphicsContext<span class="_ _19"> </span></span>却是场景显示的唯一</div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>
<div id="pf5" class="pf w0 h0" data-page-no="5"><div class="pc pc5 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="https://static.pudn.com/prod/directory_preview_static/6251b9056caf596192437719/bg5.jpg"><div class="t m0 x3 h4 y27 ff1 fs2 fc0 sc1 ls1 ws0">平台,系统有必要在开始渲染之前完成其创建工作。<span class="ff4"> </span></div><div class="t m0 x4 h4 y28 ff1 fs2 fc0 sc1 ls1 ws0">假设用户已经在进入仿真循环之前,<span class="_ _8"></span>自行创建了新的<span class="_ _0"> </span><span class="ff4 ls14">Camera<span class="_"> </span></span>摄像机对象,<span class="_ _8"></span>为其分配了</div><div class="t m0 x3 h4 y29 ff1 fs2 fc0 sc1 ls1 ws0">自定义的<span class="_ _1b"> </span><span class="ff4 ls15">GraphicsContext<span class="_ _1b"> </span></span>设备,并将<span class="_ _f"> </span><span class="ff4">Camera<span class="_ _1b"> </span></span>对象传递给视景器,就像<span class="_ _f"> </span><span class="ff4 ls3">osgviewerMFC<span class="_ _1b"> </span></span>和</div><div class="t m0 x3 h4 y2a ff4 fs2 fc0 sc1 ls42 ws0">osgcamera<span class="_ _17"> </span><span class="ff1 ls1">例子,<span class="_ _6"></span>以及我们在编写与<span class="_ _0"> </span><span class="ff4 ls22">GUI<span class="_"> </span></span>系统嵌合的仿真程序时常做的那样。<span class="_ _4"></span>此时,<span class="_ _6"></span>系统已</span></div><div class="t m0 x3 h5 y2b ff1 fs2 fc0 sc1 ls1 ws0">经不必为图形上下文的创建作任何多余的工作,<span class="_ _b"></span>因为用户不需要更多的窗口来显示自己的场</div><div class="t m0 x3 h4 y2c ff1 fs2 fc0 sc1 ls1 ws0">景了。<span class="_ _4"></span>所以就算主摄像机<span class="ff4 lsf">_camera<span class="_"> </span></span>还没有分配<span class="_ _17"> </span><span class="ff4 ls3d">GraphicsContext</span>,<span class="_ _6"></span>只要系统中已经存在图形<span class="_ _2"></span>上</div><div class="t m0 x3 h4 y2d ff1 fs2 fc0 sc1 ls1 ws0">下文,即可以开始执行仿真程序了。<span class="ff4"> </span></div><div class="t m0 x4 h4 y2e ff1 fs2 fc0 sc1 ls1 ws0">但是,如果<span class="_ _19"> </span><span class="ff4 ls32">getContexts<span class="_ _19"> </span></span>没有得到任何图形上下文的话,就说明仿真系统还没有合适的</div><div class="t m0 x3 h4 y2f ff1 fs2 fc0 sc1 ls1 ws0">显示平台,<span class="_ _11"></span>此时就需要尝试创建一个缺省的<span class="_ _0"> </span><span class="ff4 ls41">GraphicsContext<span class="_"> </span></span><span class="ls10">设备,<span class="_ _10"></span><span class="ls1">并再次执行<span class="_ _0"> </span><span class="ff4 ls35">getContexts</span>,</span></span></div><div class="t m0 x3 h4 y30 ff1 fs2 fc0 sc1 ls1 ws0">如果还是没能得到任何图形上下文的话,那么就不得不退出程序了。<span class="ff4"> </span></div><div class="t m0 x4 h4 y31 ff1 fs2 fc0 sc1 ls1 ws0">创建缺省<span class="_ _0"> </span><span class="ff4 ls3d">GraphicsCont<span class="_ _8"></span>ext<span class="_"> </span><span class="ff1 ls1">设备的方法有以下几种:<span class="ff4"> </span></span></span></div><div class="t m0 x4 h4 y32 ff4 fs2 fc0 sc1 ls1 ws0">1<span class="ff1 ls43">、读<span class="_ _13"> </span>取<span class="_ _1b"> </span></span><span class="ls6">OSG<span class="_ _2"></span>_CONFIG_FILE<span class="_"> </span></span><span class="ff1">环境变量的内容:<span class="_ _9"></span>如果用户在这个环境变量中定义了一个</span></div><div class="t m0 x3 h4 y33 ff1 fs2 fc0 sc1 ls1 ws0">文件路径的话,那么系统会尝试用<span class="_ _0"> </span><span class="ff4 ls38">osgDB::readObjectFile<span class="_"> </span></span>函数读入这个文件,使用<span class="_ _17"> </span><span class="ff4 ls5">cfg<span class="_"> </span></span>插件</div><div class="t m0 x3 h4 y34 ff1 fs2 fc0 sc1 ls1 ws0">进行解析;如果成功的话,则调用<span class="_ _e"> </span><span class="ff4 lsf">osgV<span class="_ _4"></span>iewer::V<span class="_ _4"></span>iewer::take<span class="_ _16"> </span><span class="ff1 ls1">函数,使用配置信息设置当前的</span></span></div><div class="t m0 x3 h4 y35 ff1 fs2 fc0 sc1 ls1 ws0">视景器。这些工作在<span class="_ _0"> </span><span class="ff4 ls25">osgV<span class="_ _4"></span>iewer::V<span class="_ _6"></span>iewer::readConfiguration<span class="_"> </span><span class="ff1 ls1">函数中实现。<span class="ff4"> </span></span></span></div><div class="t m0 x4 h4 y36 ff4 fs2 fc0 sc1 ls1 ws0">2<span class="ff1 ls44">、读<span class="_ _d"></span>取<span class="_ _18"> </span></span><span class="ls3c">OSG_WINDOW<span class="_ _16"> </span></span><span class="ff1">环境变量的内容:<span class="_ _6"></span>如果用户以<span class="_ _4"></span>“<span class="ff4 lsa ws11">x y w h</span>”<span class="_ _6"></span>的格式在其中定义了</span></div><div class="t m0 x3 h4 y37 ff1 fs2 fc0 sc1 ls1 ws0">窗口的左上角坐标(<span class="ff4">x</span>,<span class="_ _4"></span><span class="ff4">y<span class="ff1">)和尺寸<span class="_ _8"></span>(<span class="ff4">w</span>,<span class="ff4">h</span>)<span class="_ _4"></span>的话(注意要以空格为分隔符)<span class="_ _1"></span>,<span class="_ _8"></span>系统会尝试使</span></span></div><div class="t m0 x3 h4 y38 ff1 fs2 fc0 sc1 ls1 ws0">用<span class="_ _0"> </span><span class="ff4 ls1a">osgV<span class="_ _4"></span>iewer::V<span class="_ _6"></span>iew::setUpV<span class="_ _4"></span>iewInW<span class="_ _8"></span>indow<span class="_"> </span><span class="ff1 ls1">函数来创建设备。<span class="ff4"> </span></span></span></div><div class="t m0 x4 h4 y39 ff4 fs2 fc0 sc1 ls1 ws0">3<span class="ff1">、<span class="_ _11"></span>读取<span class="_ _0"> </span><span class="ff4 ls45">OSG_SCREEN<span class="_"> </span></span>环境变量的内容:<span class="_ _11"></span>如果用户在其中定义了所用屏幕的数量的话,</span></div><div class="t m0 x3 h4 y3a ff1 fs2 fc0 sc1 ls1 ws0">系统会尝试用<span class="_ _0"> </span><span class="ff4 ls25">osgV<span class="_ _4"></span>iewer::V<span class="_ _6"></span>iew::setUpV<span class="_ _4"></span>iewOnSingleScreen<span class="_"> </span><span class="ff1 ls1">函数,<span class="_ _11"></span>为每一个显示屏创建一个全</span></span></div><div class="t m0 x3 h4 y3b ff1 fs2 fc0 sc1 ls24 ws0">屏幕的图形窗口;如果同时还设置了<span class="_ _5"> </span><span class="ff4 ls11">OSG_WI<span class="_ _2"></span>NDOW</span>,那么这两个环境变量都可以起到作</div><div class="t m0 x3 h4 y3c ff1 fs2 fc0 sc1 ls1 ws0">用,此时将调用<span class="_ _0"> </span><span class="ff4 ls2d">setUpV<span class="_ _4"></span>iewI<span class="_ _8"></span>nW<span class="_ _4"></span>indow<span class="_"> </span><span class="ff1 ls1">函数。<span class="ff4"> </span></span></span></div><div class="t m0 x4 h4 y3d ff4 fs2 fc0 sc1 ls1 ws0">4<span class="ff1">、<span class="_ _a"></span>如果上述环境变量都没有设置的话<span class="_ _a"></span>(事实上这也是最常见的情况)<span class="_ _1c"></span>,<span class="_ _a"></span>那么系统将调用</span></div><div class="t m0 x3 h4 y3e ff4 fs2 fc0 sc1 ls19 ws0">osgV<span class="_ _4"></span>iewer::V<span class="_ _4"></span>iew::setUpV<span class="_ _6"></span>iewAcrossAllScreens<span class="_"> </span><span class="ff1 ls1">函数,尝试创建一个全屏显示的图形设备。<span class="ff4"> </span></span></div><div class="t m0 x4 h5 y3f ff1 fs2 fc0 sc1 ls1 ws0">那么,下文就从这几种图形设备建立的方法开始。至于后面的路,果然遥遥无期呢。</div><div class="t m0 x8 h4 y70 ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h4 y71 ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x4 h7 y72 ff1 fs2 fc0 sc0 ls24 ws0">解读成果:<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x4 h4 y73 ff4 fs2 fc0 sc1 lsf ws0">osgV<span class="_ _4"></span>iewer::V<span class="_ _6"></span>iewer::getContexts<span class="ff1 ls1">,</span>osgV<span class="_ _6"></span>iewer::V<span class="_ _4"></span>iewer::readConfiguration<span class="_ _8"></span><span class="ff1 ls1">。<span class="ff4"> </span></span></div><div class="t m0 x4 h7 y74 ff1 fs2 fc0 sc0 ls24 ws0">悬疑列表:<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x4 h4 y75 ff1 fs2 fc0 sc1 ls1 ws0">类变量<span class="ff4 lsf">_cameraW<span class="_ _4"></span>ithFocus<span class="_"> </span><span class="ff1 ls1">的意义是什么?<span class="ff4"> </span></span></span></div><div class="t m0 x4 h4 y76 ff4 fs2 fc0 sc1 ls1 ws0"> </div><div class="t m0 x3 h6 y77 ff1 fs3 fc0 sc0 ls1c ws0">第三日<span class="ff2 sc1 ls1"> </span></div><div class="t m0 x4 h7 y78 ff1 fs2 fc0 sc0 ls24 ws0">当前位置:<span class="ff2 sc1 ls23">osgV<span class="_ _4"></span>i<span class="_ _2"></span>ewer/V<span class="_ _8"></span>iew<span class="_ _4"></span>.cpp<span class="_"> </span><span class="ff1 sc0 ls1">第<span class="_ _0"> </span></span><span class="lsa">575<span class="_"> </span><span class="ff1 sc0 ls24">行,</span><span class="ls1b">osgV<span class="_ _4"></span>iewer::V<span class="_ _8"></span>iew::setUpV<span class="_ _8"></span>iewInWindow()<span class="ff4 ls1"> </span></span></span></span></div><div class="t m0 x4 h4 y79 ff1 fs2 fc0 sc1 ls1 ws0">这个函数有五个传入参数:窗口左上角坐标<span class="_ _0"> </span><span class="ff4">x</span>,<span class="ff4">y</span>,宽度<span class="_ _0"> </span><span class="ff4">width</span>,高度<span class="_ _16"> </span><span class="ff4 ls27">height</span>,以及屏幕</div><div class="t m0 x3 h4 y7a ff1 fs2 fc0 sc1 ls1 ws0">数<span class="_ _0"> </span><span class="ff4 ls3">screenNum</span>。它的作用顾名思义是根据给定的窗口参数来创建一个图形设备。<span class="ff4"> </span></div><div class="t m0 x4 h4 y7b ff1 fs2 fc0 sc1 ls1 ws0">首先函数将尝试获取<span class="_ _e"> </span><span class="ff4 ls18">osg::DisplaySettings<span class="_ _e"> </span></span>的指针,这个类在<span class="_ _e"> </span><span class="ff4 ls4">OSG<span class="_ _18"> </span></span>的窗口显示中扮演了</div><div class="t m0 x3 h4 y7c ff1 fs2 fc0 sc1 ls1 ws0">重要的地位:它保存了<span class="_ _0"> </span><span class="ff4 ls4">OSG<span class="_ _16"> </span></span>目前用<span class="_ _2"></span>到的,与图形显示,尤其是立体显示有关的所有信息,</div><div class="t m0 x3 h4 y7d ff1 fs2 fc0 sc1 ls1 ws0">主要包括:<span class="ff4"> </span></div><div class="t m0 x4 h4 y7e ff4 fs2 fc0 sc1 ls2c ws0">_displayT<span class="_ _4"></span>yp<span class="_ _2"></span>e<span class="ff1 ls1">:显示器类型,默认为<span class="_ _1b"> </span></span><span class="ls17">MONITOR<span class="ff1 ls1">(监视器)<span class="_ _1"></span>,此外还支持<span class="_ _1b"> </span><span class="ff4 ls46">POWER<span class="_ _4"></span>W<span class="_ _6"></span>ALL</span></span></span></div><div class="t m0 x3 h4 y7f ff1 fs2 fc0 sc1 ls1 ws0">(威力墙)<span class="_ _1"></span>,<span class="ff4 ls12">REALITY_CENTER</span>(虚拟实境中心)和<span class="_ _5"> </span><span class="ff4 ls47">HEAD_MOUNTED_<span class="_ _2"></span>DISPLA<span class="_ _6"></span>Y<span class="ff1 ls24">(头盔</span></span></div><div class="t m0 x3 h4 y80 ff1 fs2 fc0 sc1 ls1 ws0">显示器)<span class="_ _1"></span>。<span class="ff4"> </span></div><div class="t m0 x4 h4 y81 ff4 fs2 fc0 sc1 lsd ws0">_stereoMode<span class="_ _1e"></span><span class="ff1 ls48">:立体显示模式,默认为<span class="_ _3"> </span></span><span class="ls4">ANAGL<span class="_ _6"></span>YPHIC<span class="_ _1e"> </span><span class="ff1 ls48">(互补色)<span class="_ _1"></span>,此外还支持</span></span></div></div><div class="pi" data-data='{"ctm":[1.611639,0.000000,0.000000,1.611639,0.000000,0.000000]}'></div></div>