OSG系列文章目錄
文章目錄
- OSG系列文章目錄
- 前言
- 一、3d動態云與下雨、下雪效果不能同時出現
- 二、3d動態云與下雨、下雪效果不能同時出現的原因
- 三、解決辦法:
前言
先看下效果:下雨
效果:下雪
效果:雨夾雪
🌤? 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 _WIN32resPath += "\\Resources\\";
#elseresPath += "/Resources/";
#endifint 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 PSTSilverLining::LocalTime t;t.SetFromSystemTime();t.SetHour(12);t.SetTimeZone(PST);atmosphere->GetConditions()->SetTime(t);// Center the clouds around the camera's initial positionosg::Vec3d pos = _view->getCameraManipulator()->getMatrix().getTrans();SilverLining::CloudLayer *cumulusCongestusLayer;cumulusCongestusLayer = SilverLining::CloudLayerFactory::Create(CUMULUS_CONGESTUS, *atmosphere); //STRATUS //CUMULUS_CONGESTUS,CUMULUS_CONGESTUScumulusCongestusLayer->SetPrecipitation(SilverLining::CloudLayer::SLEET, 30.0);cumulusCongestusLayer->SetIsInfinite(true);cumulusCongestusLayer->SetBaseAltitude(4000);cumulusCongestusLayer->SetThickness(1500);//設置云層厚度cumulusCongestusLayer->SetBaseLength(80000); //40000cumulusCongestusLayer->SetBaseWidth(80000); //40000cumulusCongestusLayer->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;}// 自動推導類型,避免手動書寫 allocatorconst 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,0double 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 itbox.expandBy(atmosphereBox);}}}return box;
}