前言
osgearth_lights示例,模拟仿真了白天黑夜,添加了星空背景(太阳、月亮、其他天体),支持通过控件更改时间进而改变光照,支持随机更新图层颜色。
earth文件中,需要添加<sky>标签,否则无法加载skyNode节点。
<!--
osgEarth Sample
Demonstrates the use of the night color filter,
which only shows a layer in nighttime.
You need to have the sky activated for this shader to work.
-->
<map name="readymap.org" type="geocentric">
<options>
<terrain first_lod="1" normalize_edges="true"/>
</options>
<!--此为全球影像图-->
<image name="GlobeImage" driver="gdal">
<url>./globe/globel.tif</url>
</image>
<sky driver="simple" hours="14.0" ambient="1.0"/>
</map>
执行命令
// 运行白天黑夜地球
osgearth_lightsd.exe earth_image\lights.earth
// --update 参数,支持随机更新影像图层颜色
osgearth_lightsd.exe earth_image\lights.earth --update
可以看到星空、太阳、月亮。右下角控制面板显示UTC时间、月、年、环境光因子。可以通过滑块进行设置。在代码中,并没有找到此控制面板的创建代码。
代码分析
1、osgEarth::Util::Ephemeris 类:星历表类,给出了自然发生的天体天体的位置;其中包括太阳和月亮。还包括一些相关的实用函数方法。
2、关于光源的创建和添加:先创建 osg::Light* sunLight 并为其设置参数,再创建 osg::LightSource* sunLS,并 sunLS->setLight(sunLight);最后将 sunLS 添加到根节点 lights->addChild( sunLS );
3、关于自定义地形材质,并加入到mapNode节点 进行应用。
完整代码
/**
* Lights test. This application is for testing the LightSource support in osgEarth.
* 灯光测试。此应用程序用于测试osgEarth中的光源支持。
*/
#include <osgViewer/Viewer>
#include <osgEarth/Notify>
#include <osgEarth/Lighting>
#include <osgEarth/PhongLightingEffect>
#include <osgEarth/NodeUtils>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/Ephemeris>
#include <osgEarthUtil/Shadowing>
#define LC "[lights] "
using namespace osgEarth;
using namespace osgEarth::Util;
int
usage(const char* name)
{
OE_NOTICE
<< "\nUsage: " << name << " file.earth" << std::endl
<< MapNodeHelper().usage() << std::endl;
return 0;
}
// converts a double-precision Vec3d to an equivalent single-precision Vec4f position
// as needed for light positions.
// Vec3d转换为Vec4f ,根据光源位置的需要
osg::Vec4
worldToVec4(const osg::Vec3d& ecef)
{
osg::Vec4 result(0.0f, 0.0f, 0.0f, 1.0f);
osg::Vec3d d = ecef;
while (d.length() > 1e6)// 避免光源位置太远??
{
d *= 0.1;
result.w() *= 0.1;
}
return osg::Vec4(d.x(), d.y(), d.z(), result.w());
}
// 生成随机颜色
osg::Vec4
randomColor()
{
float r = (float)rand() / (float)RAND_MAX;
float g = (float)rand() / (float)RAND_MAX;
float b = (float)rand() / (float)RAND_MAX;
return osg::Vec4(r,g,b,1.0f);
}
// 添加光源
osg::Group*
addLights(osg::View* view, osg::Node* root, int lightNum)
{
// 获取地理坐标系
MapNode* mapNode = MapNode::get(root);
const SpatialReference* mapsrs = mapNode->getMapSRS();
const SpatialReference* geosrs = mapsrs->getGeographicSRS();
osg::Vec3d world;
osg::Group* lights = new osg::Group();
// Add a directional light that simulates the sun - but skip this if a sky
// was already added in the earth file.
// 添加模拟太阳的平行光
// 但如果地球文件中已经添加了天空,则跳过此操作。
if (lightNum == 0)
{
// Ephemeris 星历表类,给出了自然发生的天体天体的位置;
// 其中包括太阳和月亮。
// 还包括一些相关的实用程序功能。
Ephemeris e;
DateTime dt(2016, 8, 10, 14.0);// 设置UTC时间
CelestialBody sun = e.getSunPosition(dt); // 设置天体相对于地球的位置。
world = sun.geocentric;// 太阳的地理位置
// 定义太阳光
osg::Light* sunLight = new osg::Light(lightNum++);
world.normalize();// 归一化
sunLight->setPosition(osg::Vec4d(world, 0.0));
sunLight->setAmbient(osg::Vec4(0.2, 0.2, 0.2, 1.0));// 环境光照
sunLight->setDiffuse(osg::Vec4(1.0, 1.0, 0.9, 1.0));// 漫反射光照
// osg::LightSource 用于定义场景中的灯光的叶节点。
osg::LightSource* sunLS = new osg::LightSource();
sunLS->setLight(sunLight);
lights->addChild( sunLS );
// 为root节点 投射阴影
ShadowCaster* caster = osgEarth::findTopMostNodeOfType<ShadowCaster>(root);
if (caster)
{
OE_INFO << "Found a shadow caster!\n";
caster->setLight(sunLight);
}
std::cout << "because no skyNode,so create sunLS" << std::endl;
}
#if 1 // 这里主要是为测试加载其他光源
// A red spot light. A spot light has a real position in space
// and points in a specific direciton. The Cutoff and Exponent
// properties control the cone angle and sharpness, respectively
// 一束红光。拥有真实的位置和光方向。
// “Cutoff”和“Exponent”属性分别控制圆锥体角度和锐度
{
// 定义光照射 地点
GeoPoint p(geosrs, -121, 34, 5000000., ALTMODE_ABSOLUTE);
p.toWorld(world);
// 定义光
osg::Light* spot = new osg::Light(lightNum++);
spot->setPosition(worldToVec4(world));
spot->setAmbient(osg::Vec4(0,0.2,0,1));
spot->setDiffuse(osg::Vec4(1,0,0,1));
spot->setSpotCutoff(20.0f);
spot->setSpotExponent(100.0f);
// point straight down at the map:直接指向地图
world.normalize();
spot->setDirection(-world);
// 光源叶子节点
osg::LightSource* spotLS = new osg::LightSource();
spotLS->setLight(spot);
lights->addChild( spotLS );
}
// A green point light. A Point light lives at a real location in
// space and lights equally in all directions.
// 绿灯。点光源位于空间中的真实位置,并在所有方向上均匀发光。
{
// 定义光照射 地点
GeoPoint p(geosrs, -45, -35, 1000000., ALTMODE_ABSOLUTE);
p.toWorld(world);
// 定义光
osg::Light* point = new osg::Light(lightNum++);
point->setPosition(worldToVec4(world));
point->setAmbient(osg::Vec4(0,0,0,1));
point->setDiffuse(osg::Vec4(1.0, 1.0, 0.0,1));
// 光源叶子节点
osg::LightSource* pointLS = new osg::LightSource();
pointLS->setLight(point);
lights->addChild( pointLS );
}
#endif
// Generate the necessary uniforms for the shaders.
// 为着色器生成必要的uniforms。
// GenerateGL3LightingUniforms类的作用:遍历图形,查找灯光和材质,
// 并为它们生成静态 Uniforms 或动态剔除回调,
// 以便它们可以使用核心配置文件着色器。
GenerateGL3LightingUniforms gen;
lights->accept(gen);
return lights;
}
int
main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc,argv);
// help?
if ( arguments.read("--help") )
return usage(argv[0]);
// create a viewer:
osgViewer::Viewer viewer(arguments);
// Whether to test updating material
// 是否测试更新材质
bool update = arguments.read("--update");
// Tell the database pager to not modify the unref settings
viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy( true, false );
// install our default manipulator (do this before calling load)
viewer.setCameraManipulator( new EarthManipulator(arguments) );
// disable the small-feature culling
viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
// 在添加光源之前,需要关闭viewer本身的光
viewer.setLightingMode(viewer.NO_LIGHT);
// load an earth file, and support all or our example command-line options
osg::ref_ptr<osg::Node> node = MapNodeHelper().load(arguments, &viewer);
if (node.valid())
{
MapNode* mapNode = MapNode::get(node.get());
if ( !mapNode )
return -1;
// Example of a custom material for the terrain.
// 地形自定义材质示例。
osg::ref_ptr< osg::Material > material = 0;
if (update)// 开启update属性后,会创建material,进而调用回调方法,随机更改影像颜色
{
OE_NOTICE << "Custom material" << std::endl;
material = new osg::Material;// 材质决定材质颜色
material->setDiffuse(osg::Material::FRONT, osg::Vec4(1,1,1,1));//漫反射光照
material->setAmbient(osg::Material::FRONT, osg::Vec4(1,1,1,1));// 环境光照
// Attach our StateAttributeCallback so that uniforms are updated.绑定材质回调
material->setUpdateCallback(new MaterialCallback());
mapNode->getOrCreateStateSet()->setAttributeAndModes(material);
}
// Does a Sky already exist (loaded from the earth file)?
SkyNode* sky = osgEarth::findTopMostNodeOfType<SkyNode>(node.get());
if (!sky)// 如果没有深空节点
{
std::cout << "no skyNode " << std::endl;
// Add phong lighting.添加标签照明???
PhongLightingEffect* phong = new PhongLightingEffect();
phong->attach(node->getOrCreateStateSet());
}
// 添加光源. 当没有sky时,才会采用addLights中,创建光源的方式添加。
osg::Group* lights = addLights(&viewer, node.get(), sky?1:0);
mapNode->addChild(lights);
viewer.setSceneData(node.get());
while (!viewer.done())
{
if (viewer.getFrameStamp()->getFrameNumber() % 100 == 0)
{
// 每100帧,随机生成一个颜色
if (material)
{
material->setDiffuse(osg::Material::FRONT, randomColor());
}
}
viewer.frame();
}
return 0;
}
else
{
return usage(argv[0]);
}
}