光照模型
光源类型
定向光
当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光
(Directional Light),因为所有的光线都有着同一个方向;它会独立于光源的位置。
![light_casters_directional](http://www.learnopengl.com/img/lighting/light_casters_directional.png)
//光源位置 第四个参数 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));
点光
点光
是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。
![light_casters_point](http://www.learnopengl.com/img/lighting/light_casters_point.png)
//光源位置 第四个参数 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);
聚光
聚光
是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。
![light_casters_spotlight_angles](http://www.learnopengl.com/img/lighting/light_casters_spotlight_angles.png)
//光源位置 第四个参数 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)光照。这些光照元素看起来像下面这样:
![basic_lighting_phong](http://learnopengl.com/img/lighting/basic_lighting_phong.png)
环境光照(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
影响镜面高光的散射/半径。
![materials_real_world](http://www.learnopengl.com/img/lighting/materials_real_world.png)
//环境光
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();
}