一、立方图
立方图纹理是一种特殊的技术,它是-一个由6幅二维图像构成的、以原点为中心的纹理立方体。对于每个片段而言,纹理坐标(S,T,R)都被 当作-个方向向量来看待,每个纹理单元表示从原点所看到的纹理立方体的东西。立方图纹理应用非常广泛,可以利用它来实现环境贴图、反射和光照等效果。
立方图纹理的功能与其他的纹理操作是相互独立的,不要认为它是一种特殊的纹理。因此,立方图纹理可以使用其他标准的纹理特性,如多重纹理、纹理边框等。但需要注意的是,立方图纹理渲染时需要很大的内存,是通常二维纹理的6倍左右。在为其指定纹理数据和创建纹理对象时,应该把它作为一个整体来处理,而不是作为单个的面来指定。立方图纹理技术实现需要的主要类是osg::TextureCubeMap,它继承自osg:Texture类,封装了OpenGL中的立方图纹理函数的功能。
二、类
从继承关系中可以看出,osg:TextureCubeMap 同样继承自osg:StateAttribute 因此,可以使用设
置渲染属性的方式来启用立方图纹理,代码如下:
//设置立方图纹理
osg:TextureCubeMap* skymap = osg:TextureCubeMapO;
stateset->setTextureAttributeAndModes(0,skymap.osg:StateAttribute::ON osg:tatcAtribute:OVERRIDE);
创建天空盒的方法有很多,可以直接绘制一个,当然效果是非常不好的。实际中,经常使用的天空盒包括两种,即方体天空盒和球体天空盒。更多的是球体天空盒的应用,看起来更真实,更能描述真实的天空效果。创建球体天空盒的主要步骤如下:
(1)创建立方图纹理对象,读取立方图纹理贴图。
(2)设置自动生成纹理坐标。
(3)设置纹理矩阵。
(4)设置立方图纹理。
(5)设置矩阵变换节点,以实现合适的矩阵变换。
(6)关闭光照并关闭背面剔除,设置渲染顺序,加入到叶节点中绘制。
在上面的步骤中有几点需要注意的地方:团t读取立方图纹理贴图时,需要特别注意的是,纹理贴图要与立方体的各个面(+x, -x, +y,-Y, +Z, -Z) 一一对应,否则,生成的天空盒会非常难看。
POSITIVE X-0, //Left
X正方向
NEGATIVE X=1,
//Right
X负方向
POSITIVE Y-2,
//Front
Y正方向
NEGATIVE Y=3,
//Back
Y负方向
POSITIVE Z=4, //Up
Z正方向
NEGATIVE Z-5
//Down
Z负方向
一定要设置关闭背面剔除及光照,并设置正确的渲染顺序。为了实现更真实的效果,通常天空盒只会根据视点做适当的变换,这就需要继承变换类实现-一个新的变换类,下面示例中会有详细说明,可仔细体会。
三、案例
#include<osg/Vec3>
#include<osg/Vec4>
#include<osg/Quat>
#include<osg/Matrix>
#include<osg/ShapeDrawable>
#include<osg/Geode>
#include<osg/Geometry>
#include<osg/Transform>
#include<osg/Material>
#include<osg/NodeCallback>
#include<osg/Depth>
#include<osg/CullFace>
#include<osg/TexMat>
#include<osg/TexGen>
#include<osg/TexEnv>
#include<osg/TextureCubeMap>
#include<osgDB/WriteFile>
#include<osgDB/ReadFile>
#include<osgUtil/Optimizer>
#include<osgUtil/CullVisitor>
#include <osgViewer/Viewer>
//读取立方图
osg::ref_ptr<osg::TextureCubeMap> readCubeMap()
{
osg::ref_ptr<osg::TextureCubeMap> cubemap = new osg::TextureCubeMap;
osg::ref_ptr<osg::Image> imagePosX = osgDB::readImageFile("Images/1.jpg");
osg::ref_ptr<osg::Image> imageNegX = osgDB::readImageFile("Images/2.jpg");
osg::ref_ptr<osg::Image> imagePosY = osgDB::readImageFile("Images/3.jpg");
osg::ref_ptr<osg::Image> imageNegY = osgDB::readImageFile("Images/4.jpg");
osg::ref_ptr<osg::Image> imagePosZ = osgDB::readImageFile("Images/5.jpg");
osg::ref_ptr<osg::Image> imageNegZ = osgDB::readImageFile("Images/6.jpg");
if (imagePosX.get() && imageNegX.get() && imagePosY.get() &&
imageNegY.get() && imagePosZ.get() && imageNegZ.get())
{
//设置立方图的六个面的贴图
cubemap->setImage(osg::TextureCubeMap::POSITIVE_X, imagePosX.get());
cubemap->setImage(osg::TextureCubeMap::NEGATIVE_X, imageNegX.get());
cubemap->setImage(osg::TextureCubeMap::POSITIVE_Y, imagePosY.get());
cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Y, imageNegY.get());
cubemap->setImage(osg::TextureCubeMap::POSITIVE_Z, imagePosZ.get());
cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Z, imageNegZ.get());
//设置纹理环绕
cubemap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
cubemap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
cubemap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
//设置滤波;线性和Mipmap
cubemap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
cubemap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
}
return cubemap.get();
}
//更新立方图纹理
struct TexMatCallback :public osg::NodeCallback
{
public:
TexMatCallback(osg::TexMat& tm): _texMat(tm)
{
//int
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
if (cv)
{
//得到模型试图并且设置选转角度
const osg::Matrix& MV = *(cv->getModelViewMatrix());
const osg::Matrix R = osg::Matrix::rotate(osg::DegreesToRadians(112.0f), 0.0f, 0.0f, 1.0f)*
osg::Matrix::rotate(osg::DegreesToRadians(90.0f), 1.0f, 0.0f, 0.0f);
osg::Quat q = MV.getRotate();
const osg::Matrix C = osg::Matrix::rotate(q.inverse());
//设置纹理矩阵
_texMat.setMatrix(C * R);
}
traverse(node, nv);
}
//纹理矩阵
osg::TexMat& _texMat;
};
//一个变化类,使天空盒绕视点旋转
class MoveEarthSkyWithEyePointTransform :public osg::Transform
{
public:
//局部矩阵计算成世界矩阵
virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv)const
{
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
if (cv)
{
osg::Vec3 eyePointLocal = cv->getEyeLocal();
matrix.preMult(osg::Matrix::translate(eyePointLocal));
}
return true;
}
//世界矩阵计算为局部矩阵
virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv)const
{
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
if (cv)
{
osg::Vec3 eyePointLocal = cv->getEyeLocal();
matrix.preMult(osg::Matrix::translate(-eyePointLocal));
}
return true;
}
};
//创建天空盒
osg::ref_ptr<osg::Node> creatSkyBox()
{
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
//设置纹理映射方式,指定为替代方法,即纹理中的颜色代替原来的颜色
osg::ref_ptr<osg::TexEnv> te = new osg::TexEnv;
te->setMode(osg::TexEnv::REPLACE);
stateset->setTextureAttributeAndModes(0, te.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
//自动生成纹理坐标
/*NORMAL_MAP 标准模型-立方纹理
REFLECTION_MAP 反射模型-球体纹理
SPHERE_MAP 球体模型-球体纹理
*/
osg::ref_ptr<osg::TexGen> tg = new osg::TexGen;
tg->setMode(osg::TexGen::NORMAL_MAP);
stateset->setTextureAttributeAndModes(0, te.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
//设置纹理矩阵
osg::ref_ptr<osg::TexMat> tm = new osg::TexMat;
stateset->setTextureAttribute(0, tm.get());
//设置立方图的纹理
osg::ref_ptr<osg::TextureCubeMap> skymap = readCubeMap();
stateset->setTextureAttributeAndModes(0, skymap.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
//将深度设置为远平面
osg::ref_ptr<osg::Depth> depth = new osg::Depth;
depth->setFunction(osg::Depth::ALWAYS);
depth->setRange(1.0, 1.0);//远平面
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
//设置渲染顺序为-1,先渲染
stateset->setRenderBinDetails(-1, "RenderBin");
osg::ref_ptr<osg::Drawable> drawable = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f,0.0f), 1 ));
//吧球体加入到叶子节点中
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->setCullingActive(false);
geode->setStateSet(stateset.get());
geode->addDrawable(drawable.get());
//转置变化
osg::ref_ptr<osg::Transform>transform = new MoveEarthSkyWithEyePointTransform();
transform->setCullingActive(false);
transform->addChild(geode.get());
osg::ref_ptr<osg::ClearNode> clearnode = new osg::ClearNode;
clearnode->setCullCallback(new TexMatCallback(*tm));
clearnode->addChild(transform.get());
return clearnode.get();
}
int main()
{
//创建Viewer对象,场景浏览器
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
//创建场景组节点
osg::ref_ptr<osg::Group> root = new osg::Group();
//加入天空盒
root->addChild(creatSkyBox());
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
//设置场景数据
viewer->setSceneData(root.get());
//初始化并创建窗口
viewer->realize();
//viewer->setUpViewInWindow(200, 200, 800, 800);
viewer->run();
return 0;
}