通过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 = L2
sin(β)。

因此,对于太阳高度角α和太阳方位角β,日照光线的单位向量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分量要为0
    osg::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分量要为0
    osg::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. 参考文献

  1. Shadows
  2. 太阳高度角方位角计算
  3. 什么是真太阳时
  4. (转载)关于太阳(卫星)天顶角,太阳高度角,太阳方位角的整理
  5. DEM-地貌晕渲图的生成原理
  6. OSG 学习第四天:光照

原文地址:https://www.cnblogs.com/charlee44/p/11093103.html

时间: 2024-11-08 15:29:44

通过OSG实现对模型的日照模拟的相关文章

【大话QT之九】ZMQ偏执海盗模型调研以及模拟实现网盘负载均衡间消息通讯

应用需求: 由于网盘服务端既需要承载用户文件目录的监控又要负责文件的上传和下载,当某一时刻用户访问量较大或用户操作较为频繁是,单台文件监控服务器和文件传输服务器往往无法满足需求,极端情况下很可能造成服务器内存和CPU使用率爆表的情况,而且当Client与文件监控服务器间网络状况不好的情况下,很有可能造成用户操作序列的丢失,即用户在客户端的操作序列没有及时反映到服务端,造成用户本地目录和服务器端存储的文件不一致的情况.基于上述情况的考虑,必须要设计一套负载均衡系统,它能够满足在用户访问量增加或用户

LVS DR模型的实现模拟

实验环境:4台虚拟机,安装CentOS6.5系统.实验前注意要将各虚拟机的防火墙规则都改为Permissive,关闭selinux,时间同步.模拟时,DIP和RIP在同一个网段,与VIP不在同一个网段. 实验用拓扑图: 先说一下自己的实验中的感受:在实际的应用中,如果这种应用确实可行的话,实验中的Director和Router两个设备实际上应该是一个设备,很多时候会是一个多接口的防火墙设备,可以配置多个公网IP地址同时具有多个内网接口,用于与内网的不同网段之间的主机通信使用. 设备配置,(各虚拟

日照模拟赛DAY7 T1

日照模拟赛DAY7 T2

lvs基础知识及lvs-nat模型的简单模拟

  lvs是什么? lvs即linux virtual server,是LB(load balance)集群调度器的一种实现方法,是一种layer4 router,它能够根据目标地址.端口及调度方法,做出转发至哪一个后端的决策. 通常来讲,lvs由ipvs和ipvsadm两部分组成,ipvs工作于内核中,是lvs的核心组件,ipvsadm用于编.修改.保存规则. ipvs工作于内核中,查看某redhat系列的系统是否支持此功能的方法是查看内核的编译参数. 如果没有ipvs相关的项目,则需要为内核

WebGL简易教程(十):光照

目录 1. 概述 2. 原理 2.1. 光源类型 2.2. 反射类型 2.2.1. 环境反射(enviroment/ambient reflection) 2.2.2. 漫反射(diffuse reflection) 2.2.3. 综合 3. 实例 3.1. 具体代码 3.2. 改动详解 3.2.1. 设置日照 3.2.2. 着色器光照设置 4. 结果 5. 参考 1. 概述 在上一篇教程<WebGL简易教程(九):综合实例:地形的绘制>中,实现了对一个地形场景的渲染.在这篇教程中,就给这个地

OSG模型简单控制

OSG模型简单控制 转自:http://milkcu.sintune.net/blog/archives/1392673560.html 结点基本操作 添加结点 OSG中使用osg::Node和osg::Group装载模型,Node是Group的父类. 可以通过下面代码再场景中显示多个模型: #include <osgDB/ReadFile> #include <osgViewer/Viewer> #include <osg/Node> int main(void) {

OSG 3.0 三维视景仿真技术开发详解

第一章 OSG三维渲染引擎概述 OSG的主要功能包括以下几个方面: 1. 可以实时高效地绘制和控制使用建模软件所建立的3D模型, 如3DMAX.MAYA.Creator等制作的3D模型, 该功能是场景渲染的基本功能. 2. 支持多种外设, 如操作杆.游戏柄.轨迹球.方向盘.键盘鼠标等. 3. 除了传统的二维屏幕上进行三维展示外, OSG还可以完成红绿偏移的立体投影, 实现真正的立体展示. 4. 支持骨骼动画.关键帧动画.颜色动画等各种流行的动画. OSG的相关扩展: OSG的相关扩展,OSG针对

OSG开发概览(转载)

OSG开发概览 1 OSG基础知识 Ø OSG是Open Scene Graphic 的缩写,OSG于1997年诞生于以为滑翔机爱好者之手,Don burns  为了对滑翔机的飞行进行模拟,对openGL的库进行了封装,osg的雏形就这样诞生了,1998年Don burns 遇到了同样喜欢滑翔机和计算机图形学的Robert Osfield ,从此Robert Osfield加入了osg小组的开发并一直担任开发小组的组长. Ø OSG不但有openGL的跨平台的特性和较高的渲染性能,还提供了一系列