光照模型

光源类型

定向光

当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light),因为所有的光线都有着同一个方向;它会独立于光源的位置。

light_casters_directional
 //光源位置 第四个参数 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
//光源位置 第四个参数 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
//光源位置 第四个参数 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

环境光照(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
//环境光
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();
}
posted @ 2021-09-07 17:58  暹罗吹雪  阅读(557)  评论(0编辑  收藏  举报