天天看點

光照模型

光源類型

定向光

當一個光源很遠的時候,來自光源的每條光線接近于平行。這看起來就像所有的光線來自于同一個方向,無論物體和觀察者在哪兒。當一個光源被設定為無限遠時,它被稱為​

​定向光​

​(Directional Light),因為所有的光線都有着同一個方向;它會獨立于光源的位置。

光照模型
//光源位置 第四個參數 0.0 平行光 1.0 點光源
 light->setPosition(osg::Vec4(0.0, 0.0, 0.0, 0.0));
 //光源方向
 light->setDirection(osg::Vec3(0.0, 0.0, -1.0));      

點光

​點光​

​是一個在時間裡有位置的光源,它向所有方向發光,光線随距離增加逐漸變暗。

光照模型
//光源位置 第四個參數 0.0 平行光 1.0 點光源
light->setPosition(osg::Vec4(0.0, 0.0, 0.0, 1.0));
//設定恒定衰減系數
light->setConstantAttenuation(1.0);
//設定一次衰減系數
light->setLinearAttenuation(0.7);
//設定二次衰減系數
light->setQuadraticAttenuation(1.8);      

聚光

​聚光​

​是一種位于環境中某處的光源,它不是向所有方向照射,而是隻朝某個方向照射。結果是隻有一個聚光照射方向的确定半徑内的物體才會被照亮,其他的都保持黑暗。

光照模型
//光源位置 第四個參數 0.0 平行光 1.0 點光源
light->setPosition(osg::Vec4(0.0, 0.0, 0.0, 1.0));
//光源方向
light->setDirection(osg::Vec3(0.0, 0.0, -1.0));
//光強度分布
light->setSpotExponent(1.0);
//擴散角
light->setSpotCutoff(45.0);      

光照基礎

馮氏光照模型(Phong Lighting Model)的主要結構由3個元素組成:環境(Ambient)、漫反射(Diffuse)和鏡面(Specular)光照。這些光照元素看起來像下面這樣:

光照模型

環境光照(Ambient Lighting)

即使在黑暗的情況下,世界上也仍然有一些光亮(月亮、一個來自遠處的光),是以物體永遠不會是完全黑暗的。我們使用環境光照來模拟這種情況,也就是無論如何永遠都給物體一些顔色。

漫反射光照(Diffuse Lighting)

模拟一個發光物對物體的方向性影響(Directional Impact)。它是馮氏光照模型最顯著的組成部分。面向光源的一面比其他面會更亮。

鏡面光照(Specular Lighting)

模拟有光澤物體上面出現的亮點。鏡面光照的顔色,相比于物體的顔色更傾向于光的顔色。

//環境光
light->setAmbient(osg::Vec4(0.2, 0.2, 0.2, 1.0));
//漫反射光
light->setDiffuse(osg::Vec4(0.5, 0.5, 0.5, 1.0));
//鏡面反射光
light->setSpecular(osg::Vec4(1.0, 1.0, 1.0, 1.0));      

法向量

​法向量​

​(Normal Vector)是垂直于頂點表面的(機關)向量。由于頂點自身并沒有表面(它隻是空間中一個獨立的點),我們利用頂點周圍的頂點計算出這個頂點的表面。我們能夠使用叉乘這個技巧為立方體所有的頂點計算出法線,但是由于3D立方體不是一個複雜的形狀,是以我們可以簡單的把法線資料手工添加到頂點資料中。更新的頂點資料數組可以在這裡找到。試着去想象一下,這些法向量真的是垂直于立方體的各個面的表面的(一個立方體由6個面組成)。

//手動定義法線
osg::ref_ptr<osg::Vec3Array> normal=new osg::Vec3Array();
normal->push_back(osg::Vec3(0.0,0.0,-1.0));
geometry->setNormalArray(normal,osg::Geometry::BIND_OVERALL);

//自動生成法線
osgUtil::SmoothingVisitor::smooth(*geode);      

材質

在真實世界裡,每個物體會對光産生不同的反應。鋼看起來比陶瓷花瓶更閃閃發光,一個木頭箱子不會像鋼箱子一樣對光産生很強的反射。每個物體對鏡面高光也有不同的反應。有些物體不會散射(Scatter)很多光卻會反射(Reflect)很多光,結果看起來就有一個較小的高光點(Highlight),有些物體散射了很多,它們就會産生一個半徑更大的高光。如果我們想要在OpenGL中模拟多種類型的物體,我們必須為每個物體分别定義材質(Material)屬性。

​ambient​

​​材質向量定義了在環境光照下這個物體反射的是什麼顔色;通常這是和物體顔色相同的顔色。​

​diffuse​

​​材質向量定義了在漫反射光照下物體的顔色。漫反射顔色被設定為(和環境光照一樣)我們需要的物體顔色。​

​specular​

​​材質向量設定的是物體受到的鏡面光照的影響的顔色(或者可能是反射一個物體特定的鏡面高光顔色)。最後,​

​shininess​

​影響鏡面高光的散射/半徑。

光照模型
//環境光
material->setAmbient(osg::Material::FRONT, osg::Vec4(0.0215, 0.1725, 0.0215, 1.0));
//漫反射光
material->setDiffuse(osg::Material::FRONT, osg::Vec4(0.07568, 0.61424, 0.07568, 1.0));
//鏡面光
material->setSpecular(osg::Material::FRONT, osg::Vec4(0.633, 0.727811, 0.633, 1.0));
//光澤度
material->setShininess(osg::Material::FRONT, 0.6*128.0);      

測試

osg::LightSource* createDirectLight()
{
  osg::ref_ptr<osg::Light> light = new osg::Light();
  light->setLightNum(0);

  //光源位置 第四個參數 0.0 平行光 1.0 點光源
  light->setPosition(osg::Vec4(0, 0, 1, 0));

  //環境光
  light->setAmbient(osg::Vec4(1.0, 1.0, 1.0, 1.0));
  //漫反射光
  light->setDiffuse(osg::Vec4(1.0, 1.0, 1.0, 1.0));
  //鏡面反射光
  light->setSpecular(osg::Vec4(1.0, 1.0, 1.0, 1.0));

  osg::ref_ptr<osg::LightSource> light_source = new osg::LightSource();
  light_source->setLight(light);
  
  return light_source.release();
}

osg::LightSource* createPointLight()
{
  osg::ref_ptr<osg::Light> light = new osg::Light();
  light->setLightNum(0);

  //光源位置 第四個參數 0.0 平行光 1.0 點光源
  light->setPosition(osg::Vec4(0.0, 0.0, 2.0, 1.0));
  //設定恒定衰減系數
  light->setConstantAttenuation(1.0);
  //設定一次衰減系數
  light->setLinearAttenuation(0.045);
  //設定二次衰減系數
  light->setQuadraticAttenuation(0.0075);

  //環境光
  light->setAmbient(osg::Vec4(1.0, 1.0, 1.0, 1.0));
  //漫反射光
  light->setDiffuse(osg::Vec4(1.0, 1.0, 1.0, 1.0));
  //鏡面反射光
  light->setSpecular(osg::Vec4(1.0, 1.0, 1.0, 1.0));

  osg::ref_ptr<osg::LightSource> light_source = new osg::LightSource();
  light_source->setLight(light);

  return light_source.release();
}

osg::LightSource* createSpotLight()
{
  osg::ref_ptr<osg::Light> light = new osg::Light();
  light->setLightNum(0);

  //光源位置 第四個參數 0.0 平行光 1.0 點光源
  light->setPosition(osg::Vec4(0.0, 0.0, 2.0, 1.0));
  //光源方向
  light->setDirection(osg::Vec3(1.0, 0.0, 0.0));
  //擴散角
  light->setSpotCutoff(30.0);

  //環境光
  light->setAmbient(osg::Vec4(1.0, 1.0, 1.0, 1.0));
  //漫反射光
  light->setDiffuse(osg::Vec4(1.0, 1.0, 1.0, 1.0));
  //鏡面反射光
  light->setSpecular(osg::Vec4(1.0, 1.0, 1.0, 1.0));

  osg::ref_ptr<osg::LightSource> light_source = new osg::LightSource();
  light_source->setLight(light);

  return light_source.release();
}

osg::Node* createSphere()
{
  osg::ref_ptr<osg::Geode> geode = new osg::Geode();

  osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints();
  hints->setDetailRatio(5.0);
  geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0, 0.0, 0.0), 1.0f), hints));
  geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(3.0, 0.0, 0.0), 1.0f), hints));
  geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(6.0, 0.0, 0.0), 1.0f), hints));

  osg::ref_ptr<osg::Material> material = new osg::Material();
  //環境光
  material->setAmbient(osg::Material::FRONT, osg::Vec4(0.0215, 0.1725, 0.0215, 1.0));
  //漫反射光
  material->setDiffuse(osg::Material::FRONT, osg::Vec4(0.07568, 0.61424, 0.07568, 1.0));
  //鏡面光
  material->setSpecular(osg::Material::FRONT, osg::Vec4(0.633, 0.727811, 0.633, 1.0));
  //光澤度
  material->setShininess(osg::Material::FRONT, 0.6*128.0);
  //設定材質
  geode->getOrCreateStateSet()->setAttributeAndModes(material);

  return geode.release();
}

int main()
{
  osgViewer::Viewer viewer;

  osg::ref_ptr<osg::Group> group = new osg::Group();
  group->addChild(createSphere());
  group->addChild(createDirectLight());

  viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
  viewer.setSceneData(group);
  return viewer.run();
}