OSG系列文章目录
前言
先看下效果:下雨
效果:下雪
效果:雨夹雪
🌤️ Sundog Software 的 SilverLining SDK 是一个专为模拟真实天空和天气效果而设计的高性能图形库,广泛应用于飞行模拟、虚拟现实、游戏和科学可视化等领域。下面是它的核心功能介绍:
☁️ 3D动态云层系统
SilverLining 提供多种真实云层类型,支持动态变化和体积渲染:
积云(Cumulus):包括 Cumulus Mediocris、Congestus、Towering Cumulus、Cumulonimbus 等,支持体积建模、云成长动画、随风漂移。
层云(Stratus):适合模拟低空覆盖的厚重云层,支持雾化和光照穿透。
卷云(Cirrus):高空薄云,用于增强天空真实感。
沙尘暴(Sandstorm):模拟沙尘天气,带有特殊的雾效和颜色。
云层支持 无限区域覆盖、随时间变化、随风移动,并可通过配置文件或 API 动态控制。
降水系统(雨、雪、雨夹雪)
SilverLining 的降水系统支持三种主要天气粒子效果:
类型 控制参数示例 特效说明
雨(RAIN) SetPrecipitation(RAIN, intensity) 模拟雨滴下落、雨丝、地面湿润感
雪(SNOW) SetPrecipitation(SNOW, intensity) 模拟雪花飘落、雪粒旋转、积雪感
雨夹雪(SLEET) SetPrecipitation(SLEET, intensity) 混合雨雪效果,粒子更稠密
🔧 可调参数包括:
粒子数量(如 rain-max-particles)
粒子大小(如 rain-streak-width-multiplier、snowflake-size-multiplier)
可见度影响(如 rain-visibility-multiplier)
雨丝亮度、雪花颜色、粒子速度等
📁 所有这些都可以通过 SilverLining.config 文件进行精细调节,也可以在运行时通过 API 动态修改。
🌌 天空与光照模拟
真实的太阳、月亮、星辰轨道计算
晨昏光线(Crepuscular Rays),俗称“上帝之光”
HDR色调映射,自动调整亮度与对比度
天空颜色算法:支持 Hosek-Wilkie 模型,模拟不同大气条件下的天空色彩
⚡ 其他高级特效
闪电模拟:支持闪电分支、光照影响、HDR增强
云影图:自动生成云层阴影,增强真实感
从太空看地球大气边缘:支持地心坐标系渲染
🧩 集成与兼容性
支持 OpenGL、DirectX、Vulkan 渲染管线
提供 C++ 和 C# API
可与 Unity、osgEarth、OpenSceneGraph 等平台集成
跨平台支持:Windows、Linux、macOS、iOS、Android
一、3d动态云与下雨、下雪效果不能同时出现
遇到的问题:3d动态云与下雨、下雪效果不能同时出现
二、3d动态云与下雨、下雪效果不能同时出现的原因
这是 SilverLining SDK 的一个常见“陷阱”,其实并不是你的云层消失了,而是能见度效果在起作用。
SilverLining 在启用降水(如雨、雪、雨夹雪)时,会自动模拟真实天气中的能见度降低。这意味着:
云层距离摄像机较远时,会被“雾化”或“淡出”处理。
降水强度越高,能见度越低,云层越容易被遮蔽。
三、解决办法:
🛠️ 解决方法: 你可以通过修改配置文件 SilverLining.config 来调整或关闭这种能见度衰减效果:
找到SilverLining SDK安装路径:我的路径是
D:\workSpace\osg\sundog\SilverLining SDK\resources\SilverLining.config
1.提高能见度倍率:雨、雪、雨夹雪
# the simulated visibility here.
rain-visibility-multiplier = 5.0
# A similar fudge factor for snow visibility
snow-visibility-multiplier = 50.0
# And for sleet
sleet-visibility-multiplier = 1.0
2.完全关闭能见度衰减:
enable-precipitation-visibility-effects = no
apply-fog-from-cloud-precipitation = no
添加代码:
cumulusCongestusLayer->SetPrecipitation(SilverLining::CloudLayer::RAIN, 30.0);
🌧️ 参数说明:
RAIN 是降水类型(你也可以选择 DRY_SNOW、WET_SNOW或 SLEET)。
20.0 是降水强度,单位是毫米/小时。你可以根据需要调整这个值。
完整代码:
头文件
// Copyright (c) 2008-2012 Sundog Software, LLC. All rights reserved worldwide.
#pragma once
#include <osg/Drawable>
#include <osgViewer/Viewer>
#include "SilverLining.h"
class AtmosphereReference;
class SkyDrawable;
// SilverLining now supports separate cull and update methods, so we hook into OSG's cull and update
// passes using callbacks on our Drawable object.
struct SilverLiningCullCallback : public osg::Drawable::CullCallback
{
SilverLiningCullCallback() : atmosphere(0) {}
virtual bool cull(osg::NodeVisitor* nv, osg::Drawable* drawable, osg::RenderInfo* renderInfo) const
{
if (atmosphere) {
atmosphere->CullObjects();
}
return false;
}
SilverLining::Atmosphere *atmosphere;
};
// Our update callback just marks our bounds dirty each frame (since they move with the camera.)
struct SilverLiningUpdateCallback : public osg::Drawable::UpdateCallback
{
SilverLiningUpdateCallback() : camera(0) {}
virtual void update(osg::NodeVisitor*, osg::Drawable* drawable);
osg::Camera *camera;
};
// We also hook in with a bounding box callback to tell OSG how big our skybox is, plus the
// atmospheric limb if applicable.
struct SilverLiningSkyComputeBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback
{
SilverLiningSkyComputeBoundingBoxCallback() : camera(0) {}
virtual osg::BoundingBox computeBound(const osg::Drawable&) const;
osg::Camera *camera;
};
// The SkyDrawable wraps SilverLining to handle updates, culling, and drawing of the skybox - and works together with a CloudsDrawable
// to draw the clouds toward the end of the scene.
class SkyDrawable : public osg::Drawable
{
public:
SkyDrawable();
SkyDrawable(osgViewer::Viewer* view);
virtual bool isSameKindAs(const Object* obj) const {
return dynamic_cast<const SkyDrawable*>(obj)!=NULL;
}
virtual Object* cloneType() const {
return new SkyDrawable();
}
virtual Object* clone(const osg::CopyOp& copyop) const {
return new SkyDrawable();
}
void setSkyboxSize(double size) {_skyboxSize = size;}
double getSkyboxSize() const {return _skyboxSize;}
virtual void drawImplementation(osg::RenderInfo& renderInfo) const;
protected:
void setLighting(SilverLining::Atmosphere *atm) const;
void initializeSilverLining(AtmosphereReference *ar) const;
void initializeDrawable();
void logCloudInfo(SilverLining::Atmosphere* atmosphere) const;
osgViewer::Viewer* _view;
double _skyboxSize;
SilverLiningCullCallback *cullCallback;
SilverLiningUpdateCallback *updateCallback;
SilverLiningSkyComputeBoundingBoxCallback *computeBoundingBoxCallback;
};
实现文件:
// Copyright (c) 2008-2012 Sundog Software, LLC. All rights reserved worldwide.
#include "SkyDrawable.h"
#include "SilverLining.h"
#include "AtmosphereReference.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <assert.h>
using namespace SilverLining;
SkyDrawable::SkyDrawable()
: osg::Drawable()
, _view(0)
, _skyboxSize(100000)
{
}
SkyDrawable::SkyDrawable(osgViewer::Viewer* view)
: osg::Drawable()
, _view(view)
, _skyboxSize(100000)
{
initializeDrawable();
}
void SkyDrawable::initializeDrawable()
{
setDataVariance(osg::Object::DYNAMIC);
setUseVertexBufferObjects(false);
setUseDisplayList(false);
cullCallback = new SilverLiningCullCallback();
setCullCallback(cullCallback);
updateCallback = new SilverLiningUpdateCallback();
updateCallback->camera = _view->getCamera();
setUpdateCallback(updateCallback);
computeBoundingBoxCallback = new SilverLiningSkyComputeBoundingBoxCallback();
computeBoundingBoxCallback->camera = _view->getCamera();
setComputeBoundingBoxCallback(computeBoundingBoxCallback);
}
void SkyDrawable::setLighting(SilverLining::Atmosphere *atmosphere) const
{
osg::Light *light = _view->getLight();
osg::Vec4 ambient, diffuse;
osg::Vec3 direction;
if (atmosphere && light) {
float ra, ga, ba, rd, gd, bd, x, y, z;
atmosphere->GetAmbientColor(&ra, &ga, &ba);
atmosphere->GetSunOrMoonColor(&rd, &gd, &bd);
atmosphere->GetSunOrMoonPosition(&x, &y, &z);
direction = osg::Vec3(x, y, z);
ambient = osg::Vec4(ra, ga, ba, 1.0);
diffuse = osg::Vec4(rd, gd, bd, 1.0);
direction.normalize();
light->setAmbient(ambient);
light->setDiffuse(diffuse);
light->setSpecular(osg::Vec4(0,0,0,1));
light->setPosition(osg::Vec4(direction.x(), direction.y(), direction.z(), 0));
}
}
void SkyDrawable::initializeSilverLining(AtmosphereReference *ar) const
{
if (ar && !ar->atmosphereInitialized) {
ar->atmosphereInitialized = true; // only try once.
SilverLining::Atmosphere *atmosphere = ar->atmosphere;
if (atmosphere) {
srand(1234); // constant random seed to ensure consistent clouds across windows
// Update the path below to where you installed SilverLining's resources folder.
const char *slPath = getenv("SILVERLINING_PATH");
if (!slPath) {
printf("Can't find SilverLining; set the SILVERLINING_PATH environment variable ");
printf("to point to the directory containing the SDK.\n");
exit(0);
}
std::string resPath(slPath);
#ifdef _WIN32
resPath += "\\Resources\\";
#else
resPath += "/Resources/";
#endif
int ret = atmosphere->Initialize(SilverLining::Atmosphere::OPENGL, resPath.c_str(),
true, 0);
if (ret != SilverLining::Atmosphere::E_NOERROR) {
printf("SilverLining failed to initialize; error code %d.\n", ret);
printf("Check that the path to the SilverLining installation directory is set properly ");
printf("in SkyDrawable.cpp (in SkyDrawable::initializeSilverLining)\n");
exit(0);
}
// Let SilverLining know which way is up. OSG usually has Z going up.
atmosphere->SetUpVector(0, 0, 1);
atmosphere->SetRightVector(1, 0, 0);
// Set our location (change this to your own latitude and longitude)
SilverLining::Location loc;
loc.SetAltitude(0);
loc.SetLatitude(45);
loc.SetLongitude(-122);
atmosphere->GetConditions()->SetLocation(loc);
// Set the time to noon in PST
SilverLining::LocalTime t;
t.SetFromSystemTime();
t.SetHour(12);
t.SetTimeZone(PST);
atmosphere->GetConditions()->SetTime(t);
// Center the clouds around the camera's initial position
osg::Vec3d pos = _view->getCameraManipulator()->getMatrix().getTrans();
SilverLining::CloudLayer *cumulusCongestusLayer;
cumulusCongestusLayer = SilverLining::CloudLayerFactory::Create(CUMULUS_CONGESTUS, *atmosphere); //STRATUS //CUMULUS_CONGESTUS,CUMULUS_CONGESTUS
cumulusCongestusLayer->SetPrecipitation(SilverLining::CloudLayer::SLEET, 30.0);
cumulusCongestusLayer->SetIsInfinite(true);
cumulusCongestusLayer->SetBaseAltitude(4000);
cumulusCongestusLayer->SetThickness(1500);//设置云层厚度
cumulusCongestusLayer->SetBaseLength(80000); //40000
cumulusCongestusLayer->SetBaseWidth(80000); //40000
cumulusCongestusLayer->SetDensity(0.8);
cumulusCongestusLayer->SetAlpha(0.8);
cumulusCongestusLayer->SetWind(10.0f, 0.0f); //设置风速和风向
cumulusCongestusLayer->SetCloudAnimationEffects(1.0, true, 0, 0);//翻滚或卷动
//cumulusCongestusLayer->SetPrecipitation(SilverLining::CloudLayer::DRY_SNOW, 20.0);
cumulusCongestusLayer->SetFadeTowardEdges(true);
//cumulusCongestusLayer->EnableFadeTowardGround(true);
//cumulusCongestusLayer->EnablePrecipitationShadowing(true); // 让雨水与环境互动
//cumulusCongestusLayer->EnableFadeTowardGround(true);
// Note, we pass in X and -Y since this accepts "east" and "south" coordinates.
cumulusCongestusLayer->SetLayerPosition(pos.x(), -pos.y());
cumulusCongestusLayer->SeedClouds(*atmosphere);
atmosphere->GetConditions()->AddCloudLayer(cumulusCongestusLayer);
cullCallback->atmosphere = atmosphere;
}
}
}
void SkyDrawable::drawImplementation(osg::RenderInfo& renderInfo) const
{
SilverLining::Atmosphere *atmosphere = 0;
AtmosphereReference *ar = dynamic_cast<AtmosphereReference *>(renderInfo.getCurrentCamera()->getUserData());
if (ar) atmosphere = ar->atmosphere;
renderInfo.getState()->disableAllVertexArrays();
if (atmosphere) {
initializeSilverLining(ar);
osg::Matrix projMat = renderInfo.getState()->getProjectionMatrix();
atmosphere->SetProjectionMatrix(projMat.ptr());
osg::Matrix viewMat = renderInfo.getCurrentCamera()->getViewMatrix();
atmosphere->SetCameraMatrix(viewMat.ptr());
atmosphere->DrawSky(true, false, _skyboxSize, true, false);
setLighting(atmosphere);
}
//if (atmosphere) {
// initializeSilverLining(ar);
// osg::Matrix projMat = renderInfo.getState()->getProjectionMatrix();
// atmosphere->SetProjectionMatrix(projMat.ptr());
// osg::Matrix viewMat = renderInfo.getCurrentCamera()->getViewMatrix();
// atmosphere->SetCameraMatrix(viewMat.ptr());
// atmosphere->DrawSky(true, false, _skyboxSize, true, false);
// setLighting(atmosphere);
// // 新增:渲染云层和降水
// //atmosphere->DrawObjects(true, true, true);
//}
//logCloudInfo(atmosphere);
renderInfo.getState()->dirtyAllVertexArrays();
}
void SkyDrawable::logCloudInfo(SilverLining::Atmosphere* atmosphere) const{
if (!atmosphere) {
std::cerr << "Atmosphere 是空指针!" << std::endl;
return;
}
SilverLining::AtmosphericConditions* conditions = atmosphere->GetConditions();
if (!conditions) {
std::cerr << "无法获取 AtmosphericConditions!" << std::endl;
return;
}
// 自动推导类型,避免手动书写 allocator
const auto& layers = conditions->GetCloudLayers();
std::cout << "云层数量:" << layers.size() << std::endl;
for (const auto& entry : layers) {
int id = entry.first;
const SilverLining::CloudLayer* layer = entry.second;
if (layer) {
std::cout << "云层 ID " << id
<< ":高度=" << layer->GetBaseAltitude()
<< " 厚度=" << layer->GetThickness()
<< " 密度=" << layer->GetDensity()
<< " 透明度=" << layer->GetAlpha()
<< std::endl;
}
}
}
void SilverLiningUpdateCallback::update(osg::NodeVisitor*, osg::Drawable* drawable)
{
SilverLining::Atmosphere *atmosphere = 0;
AtmosphereReference *ar = dynamic_cast<AtmosphereReference *>(camera->getUserData());
if (ar) {
if (!ar->atmosphereInitialized) return;
atmosphere = ar->atmosphere;
}
//if (atmosphere) {
// atmosphere->UpdateSkyAndClouds();
//}
// Since the skybox bounds are a function of the camera position, always update the bounds.
drawable->dirtyBound();
}
osg::BoundingBox SilverLiningSkyComputeBoundingBoxCallback::computeBound(const osg::Drawable& drawable) const
{
osg::BoundingBox box;
if (camera) {
const SkyDrawable& skyDrawable = dynamic_cast<const SkyDrawable&>(drawable);
SilverLining::Atmosphere *atmosphere = 0;
AtmosphereReference *ar = dynamic_cast<AtmosphereReference *>(camera->getUserData());
if (ar) {
atmosphere = ar->atmosphere;
}
if (atmosphere) {
double skyboxSize;
if (skyDrawable.getSkyboxSize() != 0.0) {
skyboxSize = skyDrawable.getSkyboxSize();
} else {
skyboxSize = atmosphere->GetConfigOptionDouble("sky-box-size");
if (skyboxSize == 0.0) skyboxSize = 1000.0;
}
double radius = skyboxSize * 0.5;
osg::Vec3f eye, center, up;
camera->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d camPos = eye;
osg::Vec3d min(camPos.x() - radius, camPos.y() - radius, camPos.z() - radius);
osg::Vec3d max(camPos.x() + radius, camPos.y() + radius, camPos.z() + radius);
box.set(min, max);
double dToOrigin = camPos.length();
bool hasLimb = atmosphere->GetConfigOptionBoolean("enable-atmosphere-from-space");
if (hasLimb) {
// Compute bounds of atmospheric limb centered at 0,0,0
double earthRadius = atmosphere->GetConfigOptionDouble("earth-radius-meters");
double atmosphereHeight = earthRadius +
+ atmosphere->GetConfigOptionDouble("atmosphere-height");
double atmosphereThickness = atmosphere->GetConfigOptionDouble("atmosphere-scale-height-meters")
+ earthRadius;
osg::BoundingBox atmosphereBox;
osg::Vec3d atmMin(-atmosphereThickness, -atmosphereThickness, -atmosphereThickness);
osg::Vec3d atmMax(atmosphereThickness, atmosphereThickness, atmosphereThickness);
// Expand these bounds by it
box.expandBy(atmosphereBox);
}
}
}
return box;
}