个人中心

联系我们

搜索 登录 免费注册
界面美化
业务优化
开发工具
图像管理
文档管理
Parasoft

文章详情

如何开始使用SilverLining(中文教程四):集成技巧

2023-07-07 11:07:32

如何开始使用 SilverLining


本文为如何开始使用SilverLining(中文教程四):集成技巧,继续查看>>

如何开始使用SilverLining(中文教程一):演示示例

如何开始使用SilverLining(中文教程二):渲染场景

如何开始使用SilverLining(中文教程三):资源内存管理

如何开始使用SilverLining(中文教程五):调整外观


与流行场景图的集成技巧

与OpenSceneGraph的集成

Silverlining应该很容易集成到任何建立在OpenGL或DirectX之上的渲染引擎中,但我们收到的最多的问题是关于与OpenSceneGraph的集成。我们在SDK中包含了一些OpenSceneGraph 2.4及以上版本的示例代码,以帮助你开始使用;它是一个修改过的osgviewer应用程序,集成了天空、云和照明。还包括一个在OSG中使用多窗口的SilverLining的例子。


还提供了一个更有限的OSG 1.0的例子。


我们还包括一个来自osgRecipes(https://github.com/xarray/osgRecipes)的SilverLining与OSG的公共领域集成。在许多方面,这个集成比我们自己的更好--它更简单,只有一个SilverLiningNode。对于非地心的场景,这是一个更容易入门的方法。你可以在osgEarth的例子中找到这个代码的地心版本。


研究或扩展这个示例代码是最简单的入门方法,但如果你想从头开始,以下是你需要知道的关于OpenSceneGraph集成的事情:


  • 确保Atmosphere::DrawSky和Atmosphere::DrawObjects在正确的时间被调用的最简单方法是将这些调用包裹在osg::Drawable对象中。然后,为每个对象创建一个osg::Geode,并将Drawables添加到其中,并将Geodes设置到与你的场景的其他部分一起使用的renderbin。只要将调用DrawSky的对象设置为在渲染库中的早期位置(比如-1),并将DrawObjects的可绘制对象设置为一个非常高的位置。

  • 如果你要使用多线程模式,你需要注意Atmosphere::Initialize是在渲染线程中调用的。一个简单的方法是在调用DrawSky的drawable中保留一个静态布尔值,并在DrawSky第一次被调用之前调用初始化。这也是设置你的云层的正确时机。

  • 任何可能导致OpenGL调用的对SilverLining的调用都需要在一个有效的GL上下文中发生。这可能包括播种云层和移除云层,以及你期望绘制天空和云层的调用。为了安全起见,请确保这些调用都是在drawImplementation()方法中完成的。

  • 你可能想禁用OpenSceneGraph自动计算近处和远处的剪辑平面的方法,并明确地设置这些,因为OSG不会知道SilverLining的天空盒和云的边界。另外,你可以使用样本代码中包含的ClampProjectionMatrixCallback的实现来尝试包括天空物体的边界,并根据需要修改天空盒的大小。我们建议简单地禁用夹层平面的自动计算,除非你需要从空间做视点,为了保持深度缓冲区的分辨率,有必要把近处的夹层平面推出来。

  • 记住,当SilverLining绘制天空时,它将在这个过程中清除颜色缓冲区和深度缓冲区。你可以通过在OpenSceneGraph中禁用缓冲区清除来获得一点性能,因为它是多余的。


与osgEarth集成

如果你使用的是osgEarth,那么入门就更容易了;一个SilverLining的样本驱动被包含在osgEarth中,它可以成为一个快速启动和运行的方法。你可以在osgEarth的测试目录下找到一个silverlining.earth的样本文件,让你开始使用,而其内置的集成源在src/osgEarthSilverLining目录下。如果你需要扩展内置驱动程序的功能,底层的SilverLining::Atmosphere对象可以通过SilverLiningContext::getAtmosphere()方法获得--从Atmosphere接口,你应该可以做任何事情。


为了使用osgEarth的对数深度缓冲器,你需要将 src/osgEarthSilverLining/Shaders 中提供的用户着色器复制到你的应用程序中 SilverLining 的 resources/shaders 目录。


在安装了SilverLining SDK之后,一定要重建osgEarth,以便在osgEarth中启用SilverLining支持。


与Carmenta引擎的集成

将SilverLining集成到由Carmenta地理空间引擎驱动的三维运行场景中的示例代码也随SDK提供。


与Carmenta集成时的主要挑战是,在执行 "beforeUpdate "回调时,模型视图和投影矩阵没有被设置。这意味着视图矩阵需要从三维视图对象的属性中从头开始构建,并传递给SilverLining。请参考Carmenta集成代码的样本,看看如何正确地构建这个矩阵。


与NVidia SceniX的集成

我们还提供了一个与NVidia的SeniX场景引擎集成的样本。你会在SDK中找到一个修改过的 "简单查看器 "示例程序,该程序使用示例中包含的SilverLiningManager类,用SilverLining的渲染方法来包装你的场景根节点,并使一个定向光对象与模拟条件保持同步。


检查一下SimpleViewerView.cpp文件,看看配置大气条件的例子。


在地心或ECEF(以地球为中心的固定坐标)坐标中使用SilverLining

SilverLining已经成功地集成到许多 "整个地球 "的应用中,使用的坐标系统的原点是在地球的中心,主轴通过北极和经纬度(0,0)指向。如何配置SilverLining的基础向量,以及如何在这种环境中定位云层,是一个有点棘手的问题,所以这里有一些提示。


首先,你需要在Atmosphere::DrawSky()中为每一帧的地心参数传递true。


在为你的云播种之前,并且每一帧一次,你必须向SilverLining传递新的向上和向右的矢量,这样 "向上 "是指从原点(地球中心)到你的位置的归一化矢量,而 "向右 "则是从你的位置向东。最好的方法是通过将从地球中心到你的视角的矢量归一化来构建你的 "向上 "矢量。构建一个指向你的北极方向的 "北 "向量(即0,0,1),并计算北X上的交叉积。将得到的交叉积归一化,以建立指向东方的 "右 "向量。


如果你的地球模型是在一个Y为北而不是Z的系统中,你需要在resources/silverlining.config文件中把配置设置geocentric-z-is-up设置为 "no"。否则,太阳、月亮和星星的位置相对于你的天空和地球将是不正确的。


要定位一个局部的云层,首先设置向上和向右的向量,以反映该层在地球上的理想位置,然后调用SetLayerPosition(),坐标为(0,0)。对于无限的云层,根本就不需要设置层的位置。Silverlining会自动跟踪当前相机位置的海平面,并调整云层的位置以确保它们的海拔高度保持正确。


下面是一些可能更容易理解的示例代码,它在地心空间的相机位置上创建一个云层:


// cameraPosition is a Vector3 in ECEF coordinates, relative to the

// center of the Earth.

Vector3 up = cameraPosition;

up.Normalize();

Vector3 north(0, 0, 1); // We assume Z is up

Vector3 east = north.Cross(up);

east.Normalize();

atm->SetUpVector(up.x, up.y, up.z);

atm->SetRightVector(east.x, east.y, east.z);

 

// Note - be sure to also update AtmosphericConditions::SetLocation

// if your position changes, and update SetUpVector and SetRightVector

// as your position changes each frame.

 

// Creates a cumulus congestus cloud layer at 2500 meters above the camera. 

cumulusCongestusLayer = CloudLayerFactory::Create(CUMULUS_CONGESTUS, *atm);

cumulusCongestusLayer->SetBaseAltitude(2500);

cumulusCongestusLayer->SetThickness(100);

cumulusCongestusLayer->SetBaseLength(80000);

cumulusCongestusLayer->SetBaseWidth(80000);

cumulusCongestusLayer->SetDensity(0.4);

cumulusCongestusLayer->SetLayerPosition(0, 0);

cumulusCongestusLayer->SeedClouds(*atm);

atm->GetConditions()->AddCloudLayer(cumulusCongestusLayer); 


一些全地球环境会相对于当前的摄像机位置渲染所有物体,以保持浮点精度。如果这适用于你的系统,上述技术仍然有效,但你需要乘以一个平移矩阵,将摄像机的位置否定在每一帧传递给Atmosphere::SetCameraMatrix()的视图矩阵中,以使云层出现在正确的位置。


在地心/ECEF系统中,一定要使用Atmosphere::GetSunPositionGeographic(), Atmosphere::GetMoonPositionGeographic(), 和/或Atmosphere::GetSunOrMoonPositionGeographic()来检索正确坐标系中的太阳或月球光线的方向。通常对Atmosphere::GetSunPosition()等的调用会返回地平线坐标,这不是你想要的。


如果你打算从太空中渲染地球,一定要同时看一下Atmosphere::DrawSky()的地心参数。在非常高的海拔地区,使用地平线坐标来绘制太阳、月亮和星星会出现问题;这个参数会导致一切都以地理坐标来渲染。还可以查看resources/SilverLining.config中的 enable-atmosphere-from-space设置。开启这个参数后,SilverLining会在地球周围渲染一个环,代表你离开地球时的大气层--它假定你的坐标系中的一个单位代表一米,而你正在用一个现实的尺寸渲染地球本身。请确保你的近景和远景剪辑平面考虑到了这个环,否则你可能最终会把它剪掉--请参阅OpenSceneGraph示例代码中的投影矩阵回调类,以了解如何在设置近景和远景剪辑平面时考虑到大气的几何形状。


我们已经在SDK中包含的osgEarth示例项目中加入了一些示例代码。它在地心空间的OpenSceneGraph/osgEarth应用的背景下说明了上述的技术。


用SilverLining模拟降水

SilverLining包括粒子系统,可以模拟自然界中可能发生的任何数量的雨、雪或降雪。它还会传回雾的设置,让你准确地模拟由于降水而导致的能见度的降低。SilverLining采用你指定的降水率,并利用气象学界基于真实世界观察的研究,计算出颗粒大小、颗粒速度和可见度的分布。


降水可能附着在云层上,所以当你从云层下面经过时,降水会自动开始和停止。


虽然它很复杂,但它很容易使用。你所需要做的就是在一个地层或积云的CloudLayer对象上调用CloudLayer::SetPrecipitation()。你可以在初始化你的CloudLayer时这样做,也可以在之后的任何时候改变它。


CloudLayer::SetPrecipitation只接受两个值--降水类型(CloudLayer::RAIN , CloudLayer::SLEET, 或 CloudLayer::SNOW)和降水率。降水率的单位是毫米/小时--在雪的情况下,这是液体等效降水率,而不是累积率。速率的合理值范围是1.0到30.0。


你可以通过连续调用CloudLayer::SetPrecipitation和不同的降水类型来模拟混合降水。例如,为了模拟50/50的雨夹雪和雪,总体速率为20毫米/小时,你可以调用SetPrecipitation(CloudLayer::SLEET, 10.0);SetPrecipitation(CloudLayer::WET_SNOW, 10.0) ;


如果你想关闭云层上的降水,只需调用CloudLayer::SetPrecipitation(),降水类型为CloudLayer::NONE。这样做将删除你之前在这个云层上设置的所有降水效果。


如果你想让降水效果在全球范围内应用,独立于CloudLayer,你可以使用AtmosphericConditions::SetPrecipitation()方法。这允许你使用SilverLining的降水效果而不使用SilverLining的云层,如果你愿意的话。


为了利用降低能见度的效果,一定要实现下面 "使用SilverLining的雾效果 "中描述的代码。


用SilverLining操纵时间

默认情况下,SilverLining将模拟你用AtmosphericConditions::SetTime()指定的时间。云会随着时间的推移随风移动,但是太阳、月亮、星星和天空的外观不会改变,直到你再次调用SetTime()。这对大多数应用来说是足够的,而且可以确保良好的性能。


有些应用程序需要控制时间的流逝。你可能需要在时间上倒退,以便重新播放一个场景。或者你可能想加速时间以达到延时摄影的效果。或者,你可能只是想过滤帧之间的时间,以确保动画的流畅。SilverLining允许你用你自己的内部毫秒计时器来替换它,以完成这样的效果。


要做到这一点,请实现一个你自己的毫秒计时器类,并把它传递给AtmosphericConditions::SetMillisecondTimer()。下面是一个MillisecondTimer的例子,它将时间的流逝加快了10倍:


class MyMillisecondTimer : public MillisecondTimer

{

public:

        virtual unsigned long GetMilliseconds() const

        {

                return timeGetTime() * 10;

        }

};


要使用这个定时器,请将它传入你的AtmosphericConditions类,像这样:


MyMillisecondTimer *timer = new MyMillisecondTimer();

atm->GetConditions()->SetMillisecondTimer(timer);


你要负责在关机时删除你的定时器类。要恢复默认的定时器,请调用SetMillisecondTimer(NULL)。


你的新定时器将影响云的移动速度,但在默认情况下,这就是毫秒定时器影响的全部。你也可以设置太阳、月亮、星星和天空的外观随时间动态变化。要做到这一点,请调用AtmosphericConditions::EnableTimePassage()。比如说


atm->GetConditions()->EnableTimePassage(true, 20000);


第一个参数启用模拟时间的动态通过。第二个参数指定了云层重新点燃的最小时间间隔,以毫秒为单位(如果你使用的是你自己的MillisecondTimer,则由它定义)。重新点亮云层是一个相对昂贵的操作,而且你可能不想在一个交互式场景中重新点亮。你至少希望不经常这样做,就像上面的例子那样。如果你想让太阳、月亮、星星和天空随时间平滑地变化,但又不想产生云层重新点亮的费用,可以在第二个参数中输入-1。


在加速时间时要注意一件事--如果你的场景中有积雨云,这可能会导致闪电被更频繁地渲染,这既会看起来很奇怪,也会影响性能。为了解决这个问题,可以相应地增加配置中的lightning-max-discharge-period。另外,也可以通过将积雨云的闪电密度设置为0来完全禁止闪电。


多通道、多视口和多背景设置的考虑因素

许多训练模拟器的应用需要在几个系统或 "通道 "上共同绘制一个一致的场景。SilverLining提供了两种方法来确保云层在多台计算机上的一致性。


最简单的解决方案是给SilverLining的随机数发生器播种,使用Atmosphere::GetRandomNumberGenerator()和RandomNumberGenerator::Seed()(例如,atm->GetRandomNumberGenerator()->Seed(0))以相同的顺序和相同的参数生成的云层就会一致。


另外,你可以使用CloudLayer::Save()方法来为你的场景预先生成云层,并将它们保存到磁盘上。然后,你可以将这些保存的云层分布到每个通道,并在运行时使用CloudLayer::Restore()加载它们。这种方法还有一个好处,就是可以稍微加快你的应用程序的初始化速度。


有些应用程序还需要在一个通道上绘制多个场景,跨越多个窗口或图形上下文。其他应用程序在一个上下文中渲染多个视口。在同一个应用程序中,在多个上下文或视口中使用SilverLining是被支持的,但需要注意一些问题。


如果你在可能跨越不同图形上下文的多个窗口中绘图,一种方法是将一个SilverLining::Atmosphere实例与每个视口关联。这意味着,对于每个视口,你将实例化和初始化一个单独的大气层对象,并为你当前渲染的上下文使用正确的大气层。大气层必须在其相关的图形上下文当前处于活动状态时被初始化,并且任何添加到场景中的云层也必须在相关的上下文处于活动状态时被添加到每个大气层。


在内部,SilverLining维护了一个 "当前大气层 "的概念,每当调用Atmosphere::Initialize()或Atmosphere::DrawSky()时,这个概念就会被更新。对大气层的任何操作,比如添加或删除云层,必须在这些调用之后发生。


因此,总结一下:如果你在多个图形上下文中渲染,确保你为每个视口关联一个Atmosphere对象。在这种情况下,你应该只在调用Atmosphere::Initialize()或Atmosphere::DrawObjects()之后添加或删除云层,而且必须在正确的窗口或图形上下文当前处于活动状态时初始化和绘制大气层和云层。一般来说,完成这个任务的最简单方法是在你的应用程序的绘图方法中初始化和修改大气层对象。


另一种情况是在共享一个图形上下文的多个窗口或视口中绘图,例如使用WGL的 "上下文共享 "功能。在这种情况下,在每个视口上重复使用同一个大气层对象是安全的,注意在每个视口上绘制天空和云彩之前设置新的相机和投影矩阵。为了确保降水效果在不同的视图中正常工作,确保在Atmosphere::DrawSky()和Atmosphere::DrawObjects()方法中指定 "摄像机 "参数。


另一种多通道的方法是使用我们的多线程渲染功能;如果你注意为每个视图维护独立的ThreadCameraStreamData对象,就有可能使用同一个Atmosphere和CloudLayer实例渲染多个视图。如果你把配置选项textures-are-shared设置为 "no",它甚至可以在不共享资源的情况下工作。更多信息请参见与多线程渲染器的集成。


管理你自己的绘制

对于许多应用程序来说,让SilverLining来绘制它自己的云是一种简单的方法,它可以让你快速地运行并获得良好的效果。其他的应用程序可能更喜欢管理他们自己的云彩绘制,以确保云彩相对于场景中的其他半透明物体被正确排序。为了这个目的,SilverLining为你提供了对它通常在Atmosphere::DrawObjects()中渲染的底层半透明对象的访问。


如果你调用Atmosphere::DrawObjects(false),这就告诉SilverLining,它应该在DrawObjects调用中建立一个要绘制的半透明对象的列表,但不是要实际绘制它们。在调用DrawObjects(false)之后,你可以通过Atmosphere::GetObjects()调用获得一个半透明对象的列表。


半透明物体通常是在场景中最后渲染的,相对于当前视点来说,是从后到前的顺序。使用Atmosphere::GetObjectDistance()方法来获得从一个给定的视点到每个物体的距离,以便进行排序。一旦你的半透明物体被分类并且你准备好绘制它们,你可以使用Atmosphere::DrawObject()方法绘制一个SilverLining ObjectHandle。注意,你必须在调用DrawObject()之前启用混合功能。


下面是一个在调用Atmosphere::DrawObjects(false)之后手动绘制云的例子:


static bool comp(ObjectHandle c1, ObjectHandle c2)

{

        double d1 = atm->GetObjectDistance(c1, c2, camX, camY, camZ);

        double d2 = atm->GetObjectDistance(c2, c1, camX, camY, camZ);

 

        return (d1 > d2);

}

 

void DrawClouds()

{

        SL_VECTOR(ObjectHandle>& objs = atm-)GetObjects();

 

        sort(objs.begin(), objs.end(), comp);

 

        glEnable(GL_BLEND);

        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

        glEnable(GL_DEPTH_TEST);

        glDepthMask(0);

        glEnable(GL_TEXTURE_2D);

        glDisable(GL_LIGHTING);

        glDisable(GL_FOG);

 

        SL_VECTOR(ObjectHandle)::iterator it;

        for (it = objs.begin(); it != objs.end(); it++)

        {

                atm->DrawObject(*it);

        }

 

        glDepthMask(1);

}


如果你转而调用DrawObjects(true),这段代码就没有必要了,DrawObjects()会自行绘制云层对象。


如果你有任何问题,请随时联系我们 https://www.dhorde.com/ 在线客服,电话 023-62585653,或邮箱 sales@dhorde.com


本文为如何开始使用SilverLining(中文教程四):集成技巧,继续查看>>

如何开始使用SilverLining(中文教程一):演示示例

如何开始使用SilverLining(中文教程二):渲染场景

如何开始使用SilverLining(中文教程三):资源内存管理

如何开始使用SilverLining(中文教程五):调整外观


联系我们

周一至周日 8:00-23:00

免费热线

023-62585653

Gavin:13082556879

Jillian:17558866126

Angela:13057566525

开发外包

ERP-一体化

小程序

企业微信客服二维码

企业微信客服

版权所有:重庆庚乾信息科技有限公司 ©2025 Gengqian Information Technology Co., Ltd. 渝ICP备2022008063号-2 渝公网安备50010702505508

版权所有:重庆庚乾信息科技有限公司

©2025 Gengqian Information Technology Co., Ltd. 渝ICP备2022008063号-2 渝公网安备50010702505508