查看: 4954|回复: 5

OSG原创教程:最长的一帧(3)

[复制链接]

该用户从未签到

发表于 2008-8-16 13:06:20 | 显示全部楼层 |阅读模式
当前位置:osgViewer/View.cpp第575行,osgViewer::View::setUpViewInWindow()
这个函数有五个传入参数:窗口左上角坐标x,y,宽度width,高度height,以及屏幕数screenNum。它的作用顾名思义是根据给定的窗口参数来创建一个图形设备。

首先函数将尝试获取osg:: DisplaySettings的指针,这个类在OSG的窗口显示中扮演了重要的地位:它保存了OSG目前用到的,与图形显示,尤其是立体显示有关的所有信息,主要包括:
  • _displayType:显示器类型,默认为MONITOR(监视器),此外还支持POWERWALL(威力墙),REALITY_CENTER(虚拟实境中心)和HEAD_MOUNTED_DISPLAY(头盔显示器)。
  • _stereoMode:立体显示模式,默认为ANAGLYPHIC(互补色),此外还支持QUAD_BUFFER(四方体缓冲),HORIZONTAL_SPLIT(水平分割),VERTICAL_SPLIT(垂直分割),LEFT_EYE(左眼用),RIGHT_EYE(右眼用),HORIZONTAL_INTERLACE(水平交错),VERTICAL_INTERLACE(垂直交错),CHECKERBOARD(棋盘式交错,用于DLP显示器)。
  • _eyeSeparation:双眼的物理距离,默认为0.05。
  • _screenWidth,_screenHeight:屏幕的实际宽度和高度,分别默认设置为0.325和0.26,目前它们影响的仅仅是视图采用透视投影时的宽高比。
  • _screenDistance:人眼到屏幕的距离,默认为0.5。
  • _splitStereoHorizontalEyeMapping:默认为LEFT_EYE_LEFT_VIEWPORT(左眼渲染左视口),也可设为LEFT_EYE_RIGHT_VIEWPORT(左眼渲染右视口)。
  • _splitStereoHorizontalSeparation:左视口和右视口之间的距离(像素数),默认为0。
  • _splitStereoVerticalEyeMapping:默认为LEFT_EYE_TOP_VIEWPORT(左眼渲染顶视口),也可设为LEFT_EYE_BOTTOM_VIEWPORT(左眼渲染底视口)。
  • _splitStereoVerticalSeparation:顶视口和底视口之间的距离(像素数),默认为0。
  • _splitStereoAutoAdjustAspectRatio:默认为true,用于屏幕分割之后对其宽高比进行补偿。
  • _maxNumOfGraphicsContexts:用户程序中最多可用的GraphicsContext(图形设备上下文)数目,默认为32个。
  • _numMultiSamples:多重采样的子像素样本数,默认为0。如果显示卡支持的话,打开多重采样可以大幅改善反走样(anti-aliasing)的效果。


此外还有很多可以设置的类变量,如_minimumNumberStencilBits(模板缓存的最小位数)等,其默认设置均在osg:: DisplaySettings::setDefaults函数中完成,其中有些变量可能还没有作用。要注意的是,DisplaySettings的作用仅仅是保存所有可能在系统显示中用到的数据,这个类本身并不会据此改变任何系统设置和渲染方式。

值得称道的是,DisplaySettings可以很方便地从系统环境变量或者命令行参数中获取用户对显示设备的设置,详细的调用方法可以参阅DisplaySettings::readEnvironmentalVariables和DisplaySettings::readCommandLine两个函数的内容,十分通俗易懂。

如果希望在用户程序中更改DisplaySettings中的显示设置,请务必在执行视景器的realize函数之前,当然也就是仿真循环开始之前。这一点也是要切记的。
不知不觉中,似乎完全跑题了,那么我们还是先设法回到主题上来……

当前位置:osgViewer/View.cpp第579行,osgViewer::View::setUpViewInWindow()
代码解读的工作完全没有进展,看来需要加快进度了。获取系统显示设备的设置参数之后,下面我们要开始创建新的GraphicsContext设备了,回忆“第二日”的内容中所介绍的OSG与GUI窗口嵌合的流程,第一步是新建一个显示设备特性实例:
  1. osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
复制代码
设置图形窗口的特性值。注意这里用到了一个函数ScreenIdentifier::readDISPLAY,它的工作仅仅是尝试检查系统环境变量DISPLAY,并调用ScreenIdentifier::setScreenIdentifier函数,将其中的内容按照“hostName:0.0”的格式解析为系统的主机名称(hostName),显示数(在Win32下必须为0)和屏幕数(0或者其它数字)。

根据立体显示模式的不同,窗口特性中的模板位数等参量也会有所区分。

下一步,创建新的GraphicsContext:并将其设置给视景器的主摄像机:
  1. osg::ref_ptr<osg::GraphicsContext> gc =
  2.   osg::GraphicsContext::createGraphicsContext(traits.get());
  3. _camera->setGraphicsContext(gc.get());
复制代码
千万不要简单地使用new来创建新的GraphicsContext指针,因为相比起来,createGraphicsContext还完成了这样一些工作:
  • 1、获取窗口系统API接口,即GraphicsContext::WindowingSystemInterface的实例;
  • 2、执行setUndefinedScreenDetailsToDefaultScreen函数,如果用户没有设置屏幕数,则自动设置为缺省值0;
  • 3、返回WindowingSystemInterface::createGraphicsContext的值。


看似一切顺利,但是稍一深究就会发现,这里面存在了一个重要但是不好理解的问题:WindowingSystemInterface::createGraphicsContext可是一个纯虚函数,它怎么可能返回新建立的图形设备上下文呢?事实上,这个看似简单的WindowingSystemInterface结构体也是另有玄机的,注意这个函数:
  1. void GraphicsContext::setWindowingSystemInterface(WindowingSystemInterface* callback);
复制代码
它的作用是指定操作平台所使用的视窗API接口,也就是在特定的系统平台上创建图形窗口的时候,将会使用到哪些本地API函数。当然,Windows系统要使用Win32 API,而Linux系统要使用X11 API,Apple系统则使用Carbon。

一切有关视窗API接口的工作都是由GraphicsWindowWin32,GraphicsWindowX11和GraphicsWindowCarbon这三个类及其协作类来完成;而指定使用哪一个窗口系统API接口的关键,就在于源文件osgViewer/GraphicsWindowWin32.cpp中定义的结构体RegisterWindowingSystemInterfaceProxy了,仔细研读一下这个结构体和刚才所述的setWindowingSystemInterface函数的关系,还有注意那个紧跟着结构体的全局变量(GraphicsWindowWin32.cpp,2367行),相信您一定会大呼巧妙的。

什么,GraphicsWindowX11.cpp中也有这个结构体?那么请仔细检查一下CMake自动生成的osgViewer.vcproj工程,看看有没有包含这个多余的文件(对于Windows系统来说)——这也许就是使用CMake来实现跨平台编译的好处之一了。

至于WindowingSystemInterface::createGraphicsContext函数是如何使用Win32 API来实现图形设备的创建的,鉴于本文并不想追赶《资本论》的宏伟规模,就不再深究了,读者不妨自行刨根问底。

回来吧,回来吧。还是让我们回到setUpViewInWindow函数中来。这个函数剩下的内容并不是很多,也不难理解,主要的工作有:
  • 1、调用osgGA::GUIEventAdapter::setWindowRectangle记录新建立的窗口设备的大小,因而这个设备上产生的键盘和鼠标事件可以以此为依据。
  • 2、设置主摄像机_camera的透视投影参数,并设置新的Viewport视口。
  • 3、执行osg::Camera::setDrawBuffer和执行osg::Camera:: setReadBuffer函数,这实质上相当于在渲染的过程中执行glDrawBuffer和glReadBuffer,从而自动设置此摄像机想要绘制和读取的缓存。


就这样。不过这回真是一次又一次地离题万里……希望我们还是从中得到了一些收获和启迪的,对吗?

解读成果:
osg:: DisplaySettings::setDefaults,osg::GraphicsContext::createGraphicsContext,
osgViewer::View:: setUpViewInWindow。
悬疑列表:
类变量_cameraWithFocus的意义是什么?

[ 本帖最后由 array 于 2008-8-16 13:07 编辑 ]

该用户从未签到

发表于 2008-8-17 19:50:38 | 显示全部楼层
顶顶顶~~

该用户从未签到

发表于 2008-8-21 08:48:40 | 显示全部楼层
好贴,高水平的好贴
堪比MFC深入浅出
感谢楼主
尽快出书《OSG深入浅出》哈

该用户从未签到

发表于 2008-9-27 11:19:34 | 显示全部楼层
POWERWALL(威力墙),_stereoMode:立体显示模式
这些都是个什么玩意?

该用户从未签到

发表于 2008-10-6 13:57:21 | 显示全部楼层
顶顶顶顶

该用户从未签到

 楼主| 发表于 2008-10-6 14:47:58 | 显示全部楼层
原帖由 shengrendan2 于 2008-9-27 11:19 发表
POWERWALL(威力墙),_stereoMode:立体显示模式
这些都是个什么玩意?


立体显示是很常见的实现立体效果和沉浸式虚拟环境的方式,例如立体眼镜,就是把左眼和右眼的略微有所区别的图像借由不同偏振方向的光线叠加而成。
至于POWERWALL,我看到国内代理商网站上有译作“威力墙”的,所以就采用了这种译法。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表