给osg::Geometry(自己绘制的几何体)添加纹理(二)

目录

1. 前言

2. 自己绘制的几何体贴纹理 

   2.1. 一张图贴到整个几何体

   2.2. 几何体每个面贴不同的图片纹理

 3. 说明


1. 前言

             前文讲述了如何给osg自带的几何体,如:BOX等,添加纹理,文章参考链接如下:

  • osg给osg::Geometry(osg自带的几何体,如:BOX等)添加纹理(一)

2. 自己绘制的几何体贴纹理 

   2.1. 一张图贴到整个几何体

      下面代码展示将一张图作为纹理,贴到整个几何体上,即几何体由几个面组成,则每个面的纹理都是该图片形成的纹理。代码如下:

#include
#include
#include
#include
#include
#include
#include
#include
#include

osg::ref_ptr createBox()
{

	osg::ref_ptr spGeode = new osg::Geode;// Geode是Node的派生类,为了绘制图元的管理类

	osg::ref_ptr spGeometory = new osg::Geometry;
	spGeode->addChild(spGeometory);
    //spGeode->addDrawable(spGeometory);  // 可以将addChild替换为这句。
	osg::ref_ptr spCoordsArray = new osg::Vec3Array;

	// 右侧面
	spCoordsArray->push_back(osg::Vec3d(1.0, -1.0, -1.0));  // 前右下顶点
	spCoordsArray->push_back(osg::Vec3d(1.0, 1.0, -1.0));   // 后右下顶点
	spCoordsArray->push_back(osg::Vec3d(1.0, 1.0, 1.0));    // 后右上顶点 
	spCoordsArray->push_back(osg::Vec3d(1.0, -1.0, 1.0));   // 前右上顶点

	// 前面
	spCoordsArray->push_back(osg::Vec3d(1.0, -1.0, -1.0));  // 右下顶点
	spCoordsArray->push_back(osg::Vec3d(1.0, -1.0, 1.0));   // 右上顶点
	spCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, 1.0));  // 左上顶点 
	spCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, -1.0)); // 左下顶点

	// 左侧面
	spCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, -1.0));  // 前左下顶点
	spCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, 1.0));   // 前左上顶点
	spCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, 1.0));    // 后左上顶点 
	spCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, -1.0));   // 后左下顶点

	// 后面
	spCoordsArray->push_back(osg::Vec3d(1.0, 1.0, -1.0));    // 后下顶点
	spCoordsArray->push_back(osg::Vec3d(1.0, 1.0, 1.0));     // 后上顶点
	spCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, 1.0));    // 左上顶点 
	spCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, -1.0));   // 左下顶点

	// 上面
	spCoordsArray->push_back(osg::Vec3d(1.0, -1.0, 1.0));     // 前右顶点
	spCoordsArray->push_back(osg::Vec3d(1.0, 1.0, 1.0));      // 后右顶点
	spCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, 1.0));     // 后左顶点 
	spCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, 1.0));    // 前左顶点

	// 底面
	spCoordsArray->push_back(osg::Vec3d(1.0, -1.0, -1.0));     // 前右顶点
	spCoordsArray->push_back(osg::Vec3d(1.0, 1.0, -1.0));     // 后右顶点
	spCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, -1.0));    // 后左顶点 
	spCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, -1.0));   // 前左顶点

	spGeometory->setVertexArray(spCoordsArray);

	osg::DrawElementsUShort* pDrawElemt{ nullptr };
	for (auto nCoordIndex = 0; nCoordIndex < spCoordsArray->size(); ++nCoordIndex)
	{
		if (0 == (nCoordIndex % 4))
		{
			pDrawElemt = new osg::DrawElementsUShort(GL_QUADS);
			pDrawElemt->push_back(nCoordIndex);
			spGeometory->addPrimitiveSet(pDrawElemt);
		}
		else
		{
			pDrawElemt->push_back(nCoordIndex);
		}
	}

	// 设置纹理
	osg::ref_ptrspTexture2D = new osg::Texture2D;
	osg::ref_ptr spImage = osgDB::readImageFile("guangzhou_tower.jpg");
	if (spImage.valid()) 
	{
		spTexture2D->setImage(spImage.get());
	}

	spTexture2D->setWrap(osg::Texture2D::WRAP_S, osg::Texture::CLAMP);
	spTexture2D->setWrap(osg::Texture2D::WRAP_T, osg::Texture::CLAMP);
	spTexture2D->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture::LINEAR);
	spTexture2D->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture::LINEAR);

	// 设置纹理坐标
	osg::ref_ptr spTextureCoordsArray = new osg::Vec2Array;
	auto nPrimitiveSetSize = spGeometory->getPrimitiveSetList().size(); // 面的个数
	for (auto i = 0; i < nPrimitiveSetSize; i++) // 设置每个面的纹理坐标
	{
		spTextureCoordsArray->push_back(osg::Vec2(0, 0));
		spTextureCoordsArray->push_back(osg::Vec2(0, 1));
		spTextureCoordsArray->push_back(osg::Vec2(1, 1));
		spTextureCoordsArray->push_back(osg::Vec2(1, 0));
	}

	spGeometory->setTexCoordArray(0, spTextureCoordsArray, osg::Array::Binding::BIND_PER_PRIMITIVE_SET);
	spGeometory->getOrCreateStateSet()->setTextureAttributeAndModes(0, spTexture2D.get(), osg::StateAttribute::ON); // 开启纹理

	return spGeode;

}

int main()
{
	osg::ref_ptr viewer = new osgViewer::Viewer;

	osg::ref_ptr spMatrixTransform = new osg::MatrixTransform;

	// 绕y、z轴转动下,这样便于观察效果
	spMatrixTransform->setMatrix(osg::Matrix::rotate(osg::PI / 3.0, osg::Vec3(0, 0, 1)) * osg::Matrix::rotate(osg::PI / 5.0, osg::Vec3(1, 0, 0)));
	spMatrixTransform->addChild(createBox());
	viewer->setSceneData(spMatrixTransform);

	return viewer->run();
}

效果如下:

给osg::Geometry(自己绘制的几何体)添加纹理(二)_第1张图片

   2.2. 几何体每个面贴不同的图片纹理

       有时需要对几何体每个面贴上不同图片形成纹理,即每个面纹理不同。解决思路是:每个面形成一个osg::Geometry,而不是所有面形成一个osg::Geometry,且每个osg::Geometry对象new出一个osg::Texture2D进行纹理关联。这是因为如下代码:

spGeometory->getOrCreateStateSet()->setTextureAttributeAndModes(0, spTexture2D.get(), osg::StateAttribute::ON); // 开启纹理

其中spGeometory表示 osg::Geometry对象,每个osg::Geometry对象只能设定一个osg::Texture2D对象,所以要想每个面绑定一个不同的osg::Texture2D对象,则必须对每个面单独形成一个osg::Geometry,而不是所有面形成一个osg::Geometry。为几何体每个面贴上不同图片的纹理代码如下:

osg::ref_ptr createGeometry(const std::string& strImagePath, osg::ref_ptr spCoordsArray, osg::ref_ptr spNormalArray)
{
	osg::ref_ptr spGeometory = new osg::Geometry;
	spGeometory->setVertexArray(spCoordsArray);
	spGeometory->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, spCoordsArray->size()));

	// 设置纹理
	osg::ref_ptrspTexture2D = new osg::Texture2D;
	osg::ref_ptr spImage = osgDB::readImageFile(strImagePath);
	if (spImage.valid())
	{
		spTexture2D->setImage(0, spImage.get());
	}

	spTexture2D->setWrap(osg::Texture2D::WRAP_S, osg::Texture::CLAMP);
	spTexture2D->setWrap(osg::Texture2D::WRAP_T, osg::Texture::CLAMP);
	spTexture2D->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture::LINEAR);
	spTexture2D->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture::LINEAR);

	// 设置纹理坐标
	osg::ref_ptr spTextureCoordsArray = new osg::Vec2Array;
	spTextureCoordsArray->push_back(osg::Vec2(0, 0));
	spTextureCoordsArray->push_back(osg::Vec2(0, 1));
	spTextureCoordsArray->push_back(osg::Vec2(1, 1));
	spTextureCoordsArray->push_back(osg::Vec2(1, 0));

	spGeometory->getOrCreateStateSet()->setTextureAttributeAndModes(0, spTexture2D.get(), osg::StateAttribute::ON); // 开启纹理
	spGeometory->setTexCoordArray(0, spTextureCoordsArray, osg::Array::Binding::BIND_PER_PRIMITIVE_SET);
	spGeometory->setNormalArray(spNormalArray, osg::Array::Binding::BIND_PER_PRIMITIVE_SET);

	return spGeometory;
}
osg::ref_ptr createBoxEx()
{
	osg::ref_ptr spGeode = new osg::Geode;// Geode是Node的派生类,为了绘制图元的管理类

	osg::ref_ptr spRightFaceCoordsArray = new osg::Vec3Array;

	// 右侧面
	spRightFaceCoordsArray->push_back(osg::Vec3d(1.0, -1.0, -1.0));  // 前右下顶点
	spRightFaceCoordsArray->push_back(osg::Vec3d(1.0, 1.0, -1.0));   // 后右下顶点
	spRightFaceCoordsArray->push_back(osg::Vec3d(1.0, 1.0, 1.0));    // 后右上顶点 
	spRightFaceCoordsArray->push_back(osg::Vec3d(1.0, -1.0, 1.0));   // 前右上顶点

	osg::ref_ptr spRightFaceNormalArray = new osg::Vec3Array;
	spRightFaceNormalArray->push_back(osg::Vec3(1, 0, 0));
	osg::ref_ptr spRightFaceGeometory = createGeometry("guangzhou_tower.jpg", spRightFaceCoordsArray, spRightFaceNormalArray);
	spGeode->addChild(spRightFaceGeometory);

	// 前面
	osg::ref_ptr spFrontFaceCoordsArray = new osg::Vec3Array;
	spFrontFaceCoordsArray->push_back(osg::Vec3d(1.0, -1.0, -1.0));  // 右下顶点
	spFrontFaceCoordsArray->push_back(osg::Vec3d(1.0, -1.0, 1.0));   // 右上顶点
	spFrontFaceCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, 1.0));  // 左上顶点 
	spFrontFaceCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, -1.0)); // 左下顶点

	osg::ref_ptr spFrontFaceNormalArray = new osg::Vec3Array;
	spFrontFaceNormalArray->push_back(osg::Vec3(0, -1, 0));
	osg::ref_ptr spFrontGeometory = createGeometry("csdn.jpg", spFrontFaceCoordsArray, spFrontFaceNormalArray);
	spGeode->addChild(spFrontGeometory);

	// 左侧面
	osg::ref_ptr spLeftFaceCoordsArray = new osg::Vec3Array;
	spLeftFaceCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, -1.0));  // 前左下顶点
	spLeftFaceCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, 1.0));   // 前左上顶点
	spLeftFaceCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, 1.0));    // 后左上顶点 
	spLeftFaceCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, -1.0));   // 后左下顶点

	osg::ref_ptr spLeftFaceNormalArray = new osg::Vec3Array;
	spLeftFaceNormalArray->push_back(osg::Vec3(-1, 0, 0));
	osg::ref_ptr spLeftGeometory = createGeometry("desktop.jpg", spLeftFaceCoordsArray, spLeftFaceNormalArray);
	spGeode->addChild(spLeftGeometory);

	// 后面
    osg::ref_ptr spBackFaceCoordsArray = new osg::Vec3Array;
	spBackFaceCoordsArray->push_back(osg::Vec3d(1.0, 1.0, -1.0));    // 后下顶点
	spBackFaceCoordsArray->push_back(osg::Vec3d(1.0, 1.0, 1.0));     // 后上顶点
	spBackFaceCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, 1.0));    // 左上顶点 
	spBackFaceCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, -1.0));   // 左下顶点

	osg::ref_ptr spBackFaceNormalArray = new osg::Vec3Array;
	spBackFaceNormalArray->push_back(osg::Vec3(0, 1, 0));
	osg::ref_ptr spBackGeometory = createGeometry("tower.jpg", spBackFaceCoordsArray, spBackFaceNormalArray);
	spGeode->addChild(spBackGeometory);

	// 上面
	osg::ref_ptr spTopFaceCoordsArray = new osg::Vec3Array;
	spTopFaceCoordsArray->push_back(osg::Vec3d(1.0, -1.0, 1.0));     // 前右顶点
	spTopFaceCoordsArray->push_back(osg::Vec3d(1.0, 1.0, 1.0));      // 后右顶点
	spTopFaceCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, 1.0));     // 后左顶点 
	spTopFaceCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, 1.0));    // 前左顶点

	osg::ref_ptr spTopFaceNormalArray = new osg::Vec3Array;
	spTopFaceNormalArray->push_back(osg::Vec3(0, 0, 1));
	osg::ref_ptr spTopGeometory = createGeometry("xi_an_tower.jpg", spTopFaceCoordsArray, spTopFaceNormalArray);
	spGeode->addChild(spTopGeometory);

	// 底面
	osg::ref_ptr spBottoomFaceCoordsArray = new osg::Vec3Array;
	spBottoomFaceCoordsArray->push_back(osg::Vec3d(1.0, -1.0, -1.0));     // 前右顶点
	spBottoomFaceCoordsArray->push_back(osg::Vec3d(1.0, 1.0, -1.0));     // 后右顶点
	spBottoomFaceCoordsArray->push_back(osg::Vec3d(-1.0, 1.0, -1.0));    // 后左顶点 
	spBottoomFaceCoordsArray->push_back(osg::Vec3d(-1.0, -1.0, -1.0));   // 前左顶点

	osg::ref_ptr spBottomFaceNormalArray = new osg::Vec3Array;
	spBottomFaceNormalArray->push_back(osg::Vec3(0, 1, 0));
	osg::ref_ptr spBottomGeometory = createGeometry("paris_tower.jpg", spBottoomFaceCoordsArray, spBottomFaceNormalArray);
	spGeode->addChild(spBottomGeometory);
	
	// 开启光照,要不然几何体有些面转到正对相机时是黑色的
	spGeode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);
	osg::ref_ptr spLight = new osg::Light;
	spLight->setDiffuse(osg::Vec4d(0.0, 1.0, 0.5, 1.0)); // 漫反射光颜色
	spLight->setAmbient(osg::Vec4d(0.6, 0.6, 0.6, 1.0)); // 设置环境光颜色
	spLight->setPosition(osg::Vec4d(1, -1, 1, 0));       // 设置光源位置
	spGeode->getOrCreateStateSet()->setAttributeAndModes(spLight, osg::StateAttribute::ON); // 开启纹理
	return spGeode;

}

上述代码开启了光照,在2.1节没开启光照时,当几何体有些面转到正对相机时,是黑色的,开启光照,则看得清些。 

将main函数中的如下代码:

spMatrixTransform->addChild(createBox());

 改为:

spMatrixTransform->addChild(createBoxEx());

效果如下:

给osg::Geometry(自己绘制的几何体)添加纹理(二)_第2张图片

 3. 说明

      在绑定每个面的纹理时,绑定方式必须为osg::Array::Binding::BIND_PER_PRIMITIVE_SET,否则2.1节只有第1次绘制的面即右侧面才有纹理。关于 osg::Array::Binding枚举各值含义,请参见:osg图元绑定方式总结

     对于某些图片作为纹理,需要相应的插件才行。如:读取jpg,故请保证jpg插件存在,否则读取jpg会失败。如下为读取png类型图片时,因为没有png的插件弹出的错误:

Error reading file Qt.png: read error (Could not find plugin to read objects from file "Qt.png".)

关于怎么编译jpg插件到osg,请参见:osg第三方插件的编译方法(以jpeg插件来讲解)

你可能感兴趣的:(#,osg项目实战,osg为几何体增加纹理)