OSG之立方图纹理

lxf2023-11-14 04:10:01

1 立方图纹理

立方图纹理是一种特殊的技术,它是一个由6幅二维图像构成的、以原点为中心的纹理立方体。对于每个片段而言,纹理坐标(S,T,R)都被当作一个方向向量来看待,每个纹理单元表示从原点所看到的纹理立方体的东西。立方图纹理应用非常广泛,可以利用它来实现环境贴图、反射和光照等效果。

立方图纹理的功能与其他的纹理操作是相互独立的,不要认为它是一种特殊的纹理。因此,立方图纹理可以使用其他标准的纹理特性,如多重纹理、纹理边框等。但需要注意的是,立方图纹理渲染时需要很大的内存,是通常二维纹理的6倍左右,在为其指定纹理数据和创建纹理对象时,应该把它作为一个整体来处理,而不是作为单个的面来指定。

立方图纹理技术实现需要的主要类是 osg::TextureCubeMap,它继承自 osg::Texture 类,封装了 OpenGL 中的立方图纹理函数的功能。其继承关系图如下图所示。

OSG之立方图纹理

从继承关系中可以看出,osg::TextureCubeMap 同样继承自 osg::StateAttribute,因此,可以使用设 置渲染属性的方式来启用立方图纹理,代码如下:

//设置立方图纹理
osg::TextureCubeMap*skymap=osg:TextureCubeMapO:
stateset->setTextureAttributeAndModes(0, skymap, osg:StateAttribute::ON | osg:StateAttribute::OVERRIDE);

创建天空盒的方法有很多,可以直接绘制一个,当然效果是非常不好的。实际中,经常使用的天空盒包括两种,即方体天空盒和球体天空盒。更多的是球体天空盒的应用,看起来更真实,更能描述真实的天空效果。 创建球体天空盒的主要步骤如下:

(1)创建立方图纹理对象,读取立方图纹理贴图。

(2)设置自动生成纹理坐标。

(3)设置纹理矩阵。

(4)设置立方图纹理。

(5)设置矩阵变换节点,以实现合适的矩阵变换。

(6)关闭光照并关闭背面剔除,设置渲染顺序,加入到叶节点中绘制。在上面的步骤中有几点需要注意的地方:

读取立方图纹理贴图时,需要特别注意的是,纹理贴图要与立方体的各个面(+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负方向

一定要设置关闭背面剔除及光照,并设置正确的渲染顺序。为了实现更真实的效果,通常天 空盒只会根据视点做适当的变换,这就需要继承变换类实现一个新的变换类,下面的示例中会有详细说明,可仔细体会。

2 代码示例

立方体纹理(osg::TextureCubeMap)示例代码如下。


#include <osgViewer/Viewer>

#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Quat>
#include <osg/Matrix>
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osg/Geode>
#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 <Windows.h>

#include <iostream>
#include <string>
using namespace std;

string GetExePath(void)
{
	char szFilePath[MAX_PATH + 1] = { 0 };
	GetModuleFileNameA(NULL, szFilePath, MAX_PATH);
	(strrchr(szFilePath, '\\'))[0] = 0;
	string path = szFilePath;
	return path;
}

//读取立方图
osg::ref_ptr<osg::TextureCubeMap> readCubeMap()
{
	osg::ref_ptr<osg::TextureCubeMap> cubemap = new osg::TextureCubeMap;

	osg::ref_ptr<osg::Image> imagePosX = osgDB::readImageFile(GetExePath() + "/data/Images/TextureCubeMap/1.jpg");
	osg::ref_ptr<osg::Image> imageNegX = osgDB::readImageFile(GetExePath() + "/data/Images/TextureCubeMap/2.jpg");
	osg::ref_ptr<osg::Image> imagePosY = osgDB::readImageFile(GetExePath() + "/data/Images/TextureCubeMap/3.jpg");
	osg::ref_ptr<osg::Image> imageNegY = osgDB::readImageFile(GetExePath() + "/data/Images/TextureCubeMap/4.jpg");
	osg::ref_ptr<osg::Image> imagePosZ = osgDB::readImageFile(GetExePath() + "/data/Images/TextureCubeMap/5.jpg");
	osg::ref_ptr<osg::Image> imageNegZ = osgDB::readImageFile(GetExePath() + "/data/Images/TextureCubeMap/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)
	  {
		  //
	  }

	  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 MoveEarthySkyWithEyePointTransform : 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.postMult(osg::Matrix::translate(-eyePointLocal));
		}
		return true;
	}
};

//创建天空盒
osg::ref_ptr<osg::Node> createSkyBox()
{
	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);

	//自动生成纹理坐标,反射方式(REFLECTION_MAP)
	/*
	NORMAL_MAP 标准模式-立方图纹理
	REFLECTION_MAP 反射模式-球体纹理
	SPHERE_MAP 球体模型-球体纹理
	*/
	osg::ref_ptr<osg::TexGen> tg = new osg::TexGen;
	tg->setMode(osg::TexGen::NORMAL_MAP);
	stateset->setTextureAttributeAndModes(0, tg.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 MoveEarthySkyWithEyePointTransform();
	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()
{
	osg::ref_ptr<osgViewer::Viewer> viewer= new osgViewer::Viewer();

	osg::ref_ptr<osg::Group> rootnode = new osg::Group();

	//加入天空盒
	rootnode->addChild(createSkyBox());;

	//优化场景数据
	osgUtil::Optimizer optimzer;
	optimzer.optimize(rootnode.get());

	viewer->setSceneData(rootnode.get());

	setWindowSize(viewer, 640, 480);

	viewer->realize();

	viewer->run();

	return 0 ;
}

纹理图片

OSG之立方图纹理

纹理图片是在百度上,随意选取几张图片

效果图

OSG之立方图纹理

动态效果图

OSG之立方图纹理

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!