通过OSG实现对模型的日照模拟
文章目录
- 1. 加载模型
- 2. 光照
- 1) 环境反射
- 2) 漫反射
- 3) 日照方向
- (1) 太阳高度角和太阳方位角
- (2) 计算过程
- 4) 改进实现
- 3. 阴影
- 4. 太阳高度角与太阳方位角的计算
- 1) 太阳高度角计算公式
- 2) 太阳方位角计算公式
- 3) 太阳赤纬计算公式
- 4) 时角计算公式
- 5) 真太阳时
- 5. 参考文献
1. 加载模型
通过OpenSceneGraph加载一个倾斜摄影的场景模型数据:
#include <iostream>
#include <Windows.h>#include <osgViewer/Viewer>
#include <osgDB/ReadFile>using namespace std;int main()
{string osgPath = "D:/Data/Dayanta_OSGB/Data/MultiFoderReader.osgb";osg::Node * node = osgDB::readNodeFile(osgPath);osgViewer::Viewer viewer;viewer.setSceneData(node);viewer.setUpViewInWindow(100, 100, 800, 600);return viewer.run();
}
运行结果显示的场景如下:
想要对模型进行日照模拟,就需要用到光照和阴影技术。注意此时模型上的部分阴影是纹理上自带的。
2. 光照
osgViewer的默认场景中是有灯光的,调整上述的场景的视角,某些地方是全黑的,而且场景效果偏暗。这里需要设置自己需要的环境反射和漫反射。
1) 环境反射
环境反射是针对环境光而言的,在环境反射中,环境光照射物体是各方面均匀、强度相等的,因此环境光不用设置位置和方向,只需要指定颜色。
2) 漫反射
漫反射是针对平行光和点光源光而言的。太阳光照就是平行光,由于太阳距离地球很远,阳光到达地球的时可以认为是平行的。平行光可以用一个方向和一个颜色来定义。当然,对于像灯泡那样的点光源光,还需要指定光源的位置。
3) 日照方向
(1) 太阳高度角和太阳方位角
对于太阳光照来说,其方向并不是随便设置的。这里需要引入太阳高度角和太阳方位角两个概念,通过这两个角度,可以确定日照的方向。
太阳高度角指的就是太阳光的入射方向和地平面之间的夹角;而太阳方位角略微复杂点,指的是太阳光线在地平面上的投影与当地子午线的夹角,可近似地看作是竖立在地面上的直线在阳光下的阴影与正南方向的夹角。其中方位角以正南方向为0,由南向东向北为负,有南向西向北为正。例如太阳在正东方,则其方位角为-90度;在正东北方时,方位角为-135度;在正西方时,方位角是90度,在正西北方为135度;当然在正北方时方位角可以表示为正负180度。
(2) 计算过程
根据上述定义,对于空间某一点的日照光线,可以有如下示意图。
令太阳光线长度L1=1,有如下推算过程:
α是太阳高度角,则日照方向Z长度L3=sin(α);
L1在地平面(XY)平面的长度L2 = cos(α);
β是太阳方位角,则日照方向X长度L5 = L2cos(β);
同时日照方向Y长度L4 = L2sin(β)。
因此,对于太阳高度角α和太阳方位角β,日照光线的单位向量n(x,y,z)为:
X = cos(α)*cos(β);
Y = cos(α)*sin(β);
Z = sin(α);
4) 改进实现
在OSG中是通过设置光照节点加入到场景节点中来实现光照的。这里把太阳高度角设置成45度,太阳方位角度设置成315度。通过上述转换,得到光照方向。有一点要注意的是osg::Light没有显式的设置平行光的接口,请教大牛才知道只需要在setPosition()函数中设置w分量为0就可以了。关于这一点我也确实有点不理解。
#include <iostream>
#include <Windows.h>#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Light>using namespace std;
using namespace osg;//添加灯光节点
void AddLight(osg::ref_ptr<osg::Group> group)
{double solarAltitude = 45.0;double solarAzimuth = 315.0;//开启光照osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();stateset = group->getOrCreateStateSet();stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); // 启用光照stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON); // 启用指定光源//创建一个Light对象osg::ref_ptr<osg::Light> light = new osg::Light();light->setLightNum(0);//设置方向:平行光osg::Vec3 arrayvector(0.0f, 0.0f, -1.0f);double fAltitude = osg::DegreesToRadians(solarAltitude); //光源高度角double fAzimuth = osg::DegreesToRadians(solarAzimuth); //光源方位角arrayvector[0] = cos(fAltitude)*cos(fAzimuth);arrayvector[1] = cos(fAltitude)*sin(fAzimuth);arrayvector[2] = sin(fAltitude);light->setDirection(arrayvector);//平行光位置任意,但是w分量要为0osg::Vec4 lightpos(arrayvector[0], arrayvector[1], arrayvector[2], 0.0f);light->setPosition(lightpos); //设置环境光的颜色light->setAmbient(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));//设置散射光颜色light->setDiffuse(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); // //设置恒衰减指数// light->setConstantAttenuation(1.0f);// //设置线形衰减指数// light->setLinearAttenuation(0.0f);// //设置二次方衰减指数// light->setQuadraticAttenuation(0.0f);//创建光源osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource();lightSource->setLight(light);group->addChild(lightSource);
}int main()
{//根节点osg::ref_ptr<osg::Group> root = new osg::Group; root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); //默认去掉光照//string osgPath = "C:/Data/baoli/Production_3/Data/MultiFoderReader.osgb";osg::Node * node = osgDB::readNodeFile(osgPath);root->addChild(node);//AddLight(root);//osgViewer::Viewer viewer;viewer.setSceneData(root);viewer.setUpViewInWindow(100, 100, 800, 600);return viewer.run();
}
最终运行结果是模型整体有了亮度,但是由于纹理的效果,光照的明暗效果的效果没有显现出来。但是如果是白模,将会看到很明显的明暗效果。
3. 阴影
在OSG中已经实现了生成阴影的组件osgShadow。其具体调用方式也比较简单,首先将节点和灯光加入到ShadowedScene对象,然后标明投射者和被投射者,最后选择一种阴影渲染算法应用到场景就可以了。
注意这里的阴影渲染算法应该选用ShadowMap,因为我这里的投射者和被投射者都是同一个物体,很多例子里面用的ShadowTexture算法是不支持自投影的。
#include <iostream>
#include <Windows.h>#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Light>#include <osgShadow/ShadowedScene>
#include <osgShadow/ShadowMap>using namespace std;
using namespace osg;//添加灯光节点
void AddLight(osg::ref_ptr<osg::Group> group)
{double solarAltitude = 45.0;double solarAzimuth = 315.0;//开启光照osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();stateset = group->getOrCreateStateSet();stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); // 启用光照stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON); // 启用指定光源//创建一个Light对象osg::ref_ptr<osg::Light> light = new osg::Light();light->setLightNum(0);//设置方向:平行光osg::Vec3 arrayvector(0.0f, 0.0f, -1.0f);double fAltitude = osg::DegreesToRadians(solarAltitude); //光源高度角double fAzimuth = osg::DegreesToRadians(solarAzimuth); //光源方位角arrayvector[0] = cos(fAltitude)*cos(fAzimuth);arrayvector[1] = cos(fAltitude)*sin(fAzimuth);arrayvector[2] = sin(fAltitude);light->setDirection(arrayvector);//平行光位置任意,但是w分量要为0osg::Vec4 lightpos(arrayvector[0], arrayvector[1], arrayvector[2], 0.0f);light->setPosition(lightpos); //设置环境光的颜色light->setAmbient(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));//设置散射光颜色light->setDiffuse(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); // //设置恒衰减指数// light->setConstantAttenuation(1.0f);// //设置线形衰减指数// light->setLinearAttenuation(0.0f);// //设置二次方衰减指数// light->setQuadraticAttenuation(0.0f);//创建光源osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource();lightSource->setLight(light);group->addChild(lightSource);
}int main()
{//根节点osg::ref_ptr<osg::Group> root = new osg::Group; root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); //默认去掉光照//标识阴影接收对象 const int ReceivesShadowTraversalMask = 0x1;//标识阴影投影对象const int CastsShadowTraversalMask = 0x2;//阴影节点osg::ref_ptr<osgShadow::ShadowedScene> shadowedScene = new osgShadow::ShadowedScene();shadowedScene->setReceivesShadowTraversalMask(ReceivesShadowTraversalMask);shadowedScene->setCastsShadowTraversalMask(CastsShadowTraversalMask); root->addChild(shadowedScene);//场景节点string osgPath = "C:/Data/baoli/Production_3/Data/MultiFoderReader.osgb";osg::Node * node = osgDB::readNodeFile(osgPath);shadowedScene->addChild(node);//设置投射者node->setNodeMask(CastsShadowTraversalMask); //只需要设置投射体,那么默认情况下所有的物体都是被投物体//ShadowMap阴影算法osg::ref_ptr<osgShadow::ShadowMap> sm = new osgShadow::ShadowMap;shadowedScene->setShadowTechnique(sm.get());//AddLight(shadowedScene);//osgViewer::Viewer viewer;viewer.setSceneData(root);viewer.setUpViewInWindow(100, 100, 800, 600);return viewer.run();
}
最后的实现效果如下,可以看到很明显的阴影效果:
4. 太阳高度角与太阳方位角的计算
到这里光照和阴影的效果就已经完全实现了,但是我这里模拟的是太阳日照的效果,那么一个新的问题又产生了。前面说根据太阳高度角与太阳方位角计算光照的方向。那么太阳高度角与太阳方位角又是怎么计算出来的呢?这里推荐一篇写的不错的文章:太阳高度角方位角计算。可惜这篇文章的图片已失效,我这里就把四个计算公式再贴一下:
1) 太阳高度角计算公式
2) 太阳方位角计算公式
3) 太阳赤纬计算公式
4) 时角计算公式
5) 真太阳时
那篇文章中其他的公式都很清晰,但是关于真太阳时的描述其实我觉得没有讲清楚,看的是一头雾水。后来我也在网上查阅一些资料,令人好笑的是这个真太阳时关联的最多的却是算命算生辰八字。那我就通过这个一步步来讲这个真太阳时是怎么来的。
我们知道,古代是通过日晷等方式来计时的,例如午时就是影子最短的时候。但是由于日照到达地球的差异,乌鲁木齐和北京的午时肯定不是同一时刻。古代的人没有那个技术条件,将各地的时间统一起来,都是各地用各自的地方时来计时。所以算生辰八字和计算日照一样,都需要当地最精确的太阳光照造成的时间,这个时间就是真太阳时。
但是我们现在都是有行政时间的,无论在北京或者乌鲁木齐,用的都是东经120度的中国北京时间。而在世界上是分24个时区的,每15度就是一个时区。那么可以算算北京时间12点整在乌鲁木齐的真太阳时是多少。
经查阅乌鲁木齐的经度大约为87.68,那么时差为(87.68- 120.0)/15.0=-2.154667,也就是负2小时9分钟16.8秒,因此可算得乌鲁木齐的地方时就是9时50分43.2秒。那么这个算出来的地方时是不是就是真太阳时呢?
其实也不是的。这个时间其实是平太阳时。平太阳时假设地球绕太阳是标准的圆形,一年中每天都是均匀的。但是地球绕日运行的轨道是椭圆的,则地球相对于太阳的自转并不是均匀的,每天并不都是24小时,有时候少有时候多。这个时间差异就是真太阳时差。
在查阅真太阳时差的时候发现资料真的挺少,而且各有说法。有的说真太阳时差每年都不一样,是根据天文信息计算出来的,每年都会发布一次;而在维基百科上面给出了每天的真太阳时差的模拟计算公式;更多的是给了一张表,按照表的日期取值就行了[什么是真太阳时]。我这里只能采信第三种,例如5月29日的真太阳时差是+2分22秒,那么将上面计算的平太阳时加上这个时差,为9时53分5.2秒。即5月29日北京时间乌鲁木齐的真太阳时为9时53分5.2秒。
5. 参考文献
- Shadows
- 太阳高度角方位角计算
- 什么是真太阳时
- (转载)关于太阳(卫星)天顶角,太阳高度角,太阳方位角的整理
- DEM-地貌晕渲图的生成原理
- OSG 学习第四天:光照
通过OSG实现对模型的日照模拟相关推荐
- Django(part49)--用Django自带的User模型类进行模拟登录
学习笔记,仅供参考 用Django自带的User模型类进行模拟登录 基于上个Blog中学习的用户认证系统,我们再用Django自带的User模型类,进行模拟登录(模拟登录的意思就是没有真正的登录,不涉 ...
- 【Pytorch神经网络实战案例】17 带W散度的WGAN-div模型生成Fashon-MNST模拟数据
1 WGAN-div 简介 W散度的损失函数GAN-dv模型使用了W散度来替换W距离的计算方式,将原有的真假样本采样操作换为基于分布层面的计算. 2 代码实现 在WGAN-gp的基础上稍加改动来实现, ...
- 【Pytorch神经网络实战案例】15 WGAN-gp模型生成Fashon-MNST模拟数据
1 WGAN-gp模型生成模拟数据案例说明 使用WGAN-gp模型模拟Fashion-MNIST数据的生成,会使用到WGAN-gp模型.深度卷积GAN(DeepConvolutional GAN,DC ...
- lstm原文_LSTM模型与水文模型在径流模拟中的比较
学术简报 题目:Comparison of Long Short Term Memory Networks and the Hydrological Model in Runoff Simulatio ...
- 15 单因子利率模型蒙卡模拟
15 单因子利率模型蒙卡模拟 15.1 简介 15.1.1 单因子模型蒙卡抽样 15.1.2 由抽样结果计算债券期权价格 15.2 蒙卡模拟步骤 15.2.1 蒙卡抽样利率变化路径 15.2.2 计算 ...
- Fluent常用模型介绍-流体模拟仿真ansys
Fluent常用模型介绍-流体模拟仿真ansys FLUENT是ANSYS CFD的核心求解器,其拥有广泛的用户群.ANSYS Fluent的主要特点如下. 1.湍流和噪声模型 FLUENT的湍流模型 ...
- linux跑wrf.exe程序的前提,用WRF模型进行气象模拟入门(3)——WRF的运行
在前两篇文章已经介绍了WRF的编译安装,以及WPS的配置.本文将开始介绍如何运行WRF 前面已经提到了,WRF有两种运行模式:理想案例(idealized)与实际案例(real-data),这里只关注 ...
- Python使用GARCH,EGARCH,GJR-GARCH模型和蒙特卡洛模拟进行股价预测
全文下载链接:http://tecdat.cn/?p=20678 在本文中,预测股价已经受到了投资者,政府,企业和学者广泛的关注.然而,数据的非线性和非平稳性使得开发预测模型成为一项复杂而具有挑战性的 ...
- 山东省2021年模拟高考成绩从哪里查询,2020年夏季山东日照模拟高考成绩查询入口(已开通)...
摘要: 2020年夏季山东日照模拟高考成绩查询入口(已开通)为你介绍[导语]日照高考频道从山东省教育招生考试院了解到,2020年夏季山东日照模拟高考成绩查询入口已于1月10日正式开通,考生可登录山东省 ...
最新文章
- Visual C++ 2008入门经典 第十五章 在窗口中绘图
- C语言面试算法题(一)
- Android-View点击水波纹特效
- 使用windows调用Linux远程桌面
- Spring Data JPA 教程(翻译)
- 【PMP】项目风险管理~重点知识
- 工业级光电转换器产品介绍
- Python语法糖之:列表解析、集合解析和字典解析
- 在TextView中插入图片
- 打印数组的几种方式--java
- 纯净的windows官方WinPE制作
- 指定的网络名不再可用
- 使用 Kubernetes Ingress 对外暴露服务
- 云效平台性能测试功能:一个基于Jmeter的性能压测平台
- 模拟电路实验 01 - | 基本共射放大电路
- Android逆向之旅---应用的反调试方案解析(附加修改IDA调试端口和修改内核信息)
- FeignClient调用接口接收不到参数问题
- 小周SEO:网站关键词【杭州SEO】排名到前3名SEO技巧
- pandas中Series的apply函数
- 内最有份量的破解下载网站一览
热门文章
- XCPC2020赛季流水账
- 一行代码教你撩妹手到擒来html+css+js烟花告白3D相册(含音乐+可自定义文字)520表白/七夕情人节/求婚...
- python数据可视化学习-饼状图
- 如何用javaweb实现网上招聘系统、基于SSM+mysql的校园大学生兼职招聘平台
- 服务器ghost备份后无法进入系统还原,使用Ghost备份/还原系统的速度很慢原因分析与解决...
- 国内MEMS企业、研究所以及科研院校
- 通过pyhton认识一等函数
- matlab ccd驱动,CCD常见驱动比较
- 【机器学习11】LAD,K-means,SVM分析鸢尾花和月亮数据集
- 《卓有成效的管理者》读后感