天天看點

OSG學習:紋理映射(五)——計算紋理坐标

以下内容來自: 

1、《OpenSceneGraph三維渲染引擎程式設計指南》肖鵬 劉更代 徐明亮 清華大學出版社 

2、《OpenSceneGraph三維渲染引擎設計與實踐》王銳 錢學雷 清華大學出版社

3、自己的總結

下載下傳完整工程OSG_13_TextureCoords

建立C++項目後,首先需要配置OSG環境,具體步驟看OSG學習:WIN10系統下OSG+VS2017編譯及運作第六步:建立OSG項目測試。

在很多的時候,直接指定紋理坐标是非常不友善的(如曲面紋理坐标),隻有少數的曲面(如圓錐、圓柱)可以在不産生扭曲的情況下映射到平面上,其他的曲面在映射表面時都會産生一定程度的扭曲。一般而言,曲面表面的曲率越大,紋理所需要的扭曲度就越大。這時,直接指定紋理坐标一般就比較困難了。

以下代碼通過一個繼承自osg::NodeVisitor通路器的紋理生成器周遊模型的所有頂點及法線,然後根據頂點、法線及一定的比例來确定紋理坐标。看具體代碼:

// stdafx.h

#include <osg/Node>
#include <osg/Geometry>
#include <osg/Geode>  
#include <osg/Group>

#include <osg/Camera>

#include <osg/MatrixTransform> //移動節點的矩陣類,最常用的移動節點的類。可随動、旋轉控制節點。
#include <osg/PositionAttitudeTransform>
#include <osg/NodeVisitor>
#include <osg/TexGen> //指定用于自動生成紋理坐标的函數,可以設定紋理的計算方式是以物體坐标空間還是相機坐标空間來進行不同的計算
#include <osg/TexEnv>

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include <osgUtil/Optimizer>
#include <osgViewer/Viewer>
           
// .cpp

/*
 *隻有少數的曲面(如圓錐、圓柱)可以在不産生扭曲的情況下映射到平面上
 *其他的曲面在映射表面時都會産生一定程度的扭曲
 *曲面表面的曲率越大,紋理所需要的扭曲度就越大
 *因而直接指定紋理坐标是非常不友善的(如曲面紋理坐标)
 *
 *通過一個繼承自osg::NodeVisitor通路器的紋理生成器周遊模型的所有頂點及法線
 *根據頂點、法線及一定的比例來确定紋理坐标
*/

//紋理坐标生成器,繼承自NodeVisitor
class TexCoordGenerator:public osg::NodeVisitor
{
public:
	//周遊所有子節點
	TexCoordGenerator() :NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
	{
		//
	}

	//通過包圍盒來确定合适的比例
	void apply(osg::Geode &geode)
	{
		const osg::BoundingSphere &bsphere = geode.getBound();

		float scale = 10;

		if (bsphere.radius() != 0)
		{
			scale = 5 / bsphere.radius();
		}

		//周遊所有幾何體,并設定紋理坐标
		for (unsigned i = 0; i < geode.getNumDrawables(); ++i)
		{
			osg::Geometry *geo = dynamic_cast<osg::Geometry *>(geode.getDrawable(i));

			if (geo)
			{
				osg::Vec2Array *tc = generate_coords(geo->getVertexArray(), geo->getNormalArray(), scale);
				geo->setTexCoordArray(0, tc);
			}
		}
		NodeVisitor::apply(geode);
	}
protected:
	//計算紋理坐标
	osg::Vec2Array *generate_coords(osg::Array *vx, osg::Array *nx, float scale)
	{
		osg::Vec2Array *v2a = dynamic_cast<osg::Vec2Array *>(vx);
		osg::Vec3Array *v3a = dynamic_cast<osg::Vec3Array *>(vx);
		osg::Vec4Array *v4a = dynamic_cast<osg::Vec4Array *>(vx);
		osg::Vec2Array *n2a = dynamic_cast<osg::Vec2Array *>(vx);
		osg::Vec3Array *n3a = dynamic_cast<osg::Vec3Array *>(vx);
		osg::Vec4Array *n4a = dynamic_cast<osg::Vec4Array *>(vx);

		osg::ref_ptr<osg::Vec2Array> tc = new osg::Vec2Array();
		for (unsigned i = 0; i < vx->getNumElements(); ++i)
		{
			osg::Vec3 P;
			if (v2a)
				P.set((*v2a)[i].x(), (*v2a)[i].y(), 0);
			if (v3a)
				P.set((*v3a)[i].x(), (*v3a)[i].y(), (*v3a)[i].z());
			if (v4a)
				P.set((*v4a)[i].x(), (*v4a)[i].y(), (*v4a)[i].z());
			
			osg::Vec3 N(0, 0, 1);
			if (n2a)
				N.set((*n2a)[i].x(), (*n2a)[i].y(), 0);
			if (n3a)
				N.set((*n3a)[i].x(), (*n3a)[i].y(), (*n3a)[i].z());
			if (n4a)
				N.set((*n4a)[i].x(), (*n4a)[i].y(), (*n4a)[i].z());

			int axis = 0;
			if (N.y() > N.x() && N.y() > N.z())
				axis = 1;
			if (-N.y() > N.x() && -N.y() > N.z())
				axis = 1;
			if (N.z() > N.x() && N.z() > N.y())
				axis = 2;
			if (-N.z() > N.x() && -N.z() > N.y())
				axis = 2;
			
			
			osg::Vec2 uv;

			switch (axis)
			{
				case 0: uv.set(P.y(), P.z());
					break;
				case 1: uv.set(P.x(), P.z());
					break;
				case 2: uv.set(P.x(), P.y());
					break;
				default:;
			}

			tc->push_back(uv * scale);
		}
		return tc.release();
	}
};

//建立二維紋理狀态對象
osg::ref_ptr<osg::StateSet> createTexture2DState(osg::ref_ptr<osg::Image> image)
{
	//建立二維紋理對象
	osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
	texture->setDataVariance(osg::Object::DYNAMIC);
	//設定貼圖
	texture->setImage(image.get());
	//設定濾波
	texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
	texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	//設定環繞模式
	texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
	texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);

	//建立狀态集對象
	osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
	stateset->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);

	return stateset.release();
}

int main()
{
	osg::ref_ptr<osg::Image> image = osgDB::readImageFile("Images/primitives.gif");
	osg::ref_ptr<osg::StateSet> stateset = createTexture2DState(image.get());

	TexCoordGenerator tcg;
	osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("dumptruck.osg");
	node->accept(tcg);
	node->setStateSet(stateset.get());

	osg::ref_ptr<osg::Group> root = new osg::Group();
	root->addChild(node.get());

	//優化場景資料
	osgUtil::Optimizer optimizer;
	optimizer.optimize(root.get());

	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
	viewer->setSceneData(root.get());
	viewer->realize();

	return viewer->run();
}
           
OSG學習:紋理映射(五)——計算紋理坐标