sundog公司的SilverLining SDK库实现3d动态云层和下雨、下雨、雨夹雪效果

发布于:2025-07-16 ⋅ 阅读:(14) ⋅ 点赞:(0)

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;
}

网站公告

今日签到

点亮在社区的每一天
去签到