目录
1. 前言
2. 自会集合体贴纹理
2.1. 一张图贴到整个几何体
2.2. 几何体每个面贴不同的图片纹理
3. 说明
1. 前言
前文讲述了如何给osg自带的几何体,如:BOX等,添加纹理,文章参考链接如下:
- osg给osg::Geometry(osg自带的几何体,如:BOX等)添加纹理(一)
2. 自己绘制的几何体贴纹理
2.1. 一张图贴到整个几何体
下面代码展示将一张图作为纹理,贴到整个几何体上,即几何体由几个面组成,则每个面的纹理都是该图片形成的纹理。代码如下:
#include<osgDB\ReadFile>
#include<osgViewer\Viewer>
#include<osg\ShapeDrawable>
#include<osg\Geode>
#include<osg\StateSet>
#include<osg\Image>
#include<osg\Texture2D>
#include<osg\Material>
#include<osg\MatrixTransform>
osg::ref_ptr<osg::Geode> createBox()
{
osg::ref_ptr<osg::Geode> spGeode = new osg::Geode;// Geode是Node的派生类,为了绘制图元的管理类
osg::ref_ptr<osg::Geometry> spGeometory = new osg::Geometry;
spGeode->addChild(spGeometory);
//spGeode->addDrawable(spGeometory); // 可以将addChild替换为这句。
osg::ref_ptr<osg::Vec3Array> 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_ptr<osg::Texture2D>spTexture2D = new osg::Texture2D;
osg::ref_ptr<osg::Image> 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<osg::Vec2Array> 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<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::MatrixTransform> 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();
}
效果如下:
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<osg::Geometry> createGeometry(const std::string& strImagePath, osg::ref_ptr<osg::Vec3Array> spCoordsArray, osg::ref_ptr<osg::Vec3Array> spNormalArray)
{
osg::ref_ptr<osg::Geometry> spGeometory = new osg::Geometry;
spGeometory->setVertexArray(spCoordsArray);
spGeometory->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, spCoordsArray->size()));
// 设置纹理
osg::ref_ptr<osg::Texture2D>spTexture2D = new osg::Texture2D;
osg::ref_ptr<osg::Image> 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<osg::Vec2Array> 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<osg::Geode> createBoxEx()
{
osg::ref_ptr<osg::Geode> spGeode = new osg::Geode;// Geode是Node的派生类,为了绘制图元的管理类
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array> spRightFaceNormalArray = new osg::Vec3Array;
spRightFaceNormalArray->push_back(osg::Vec3(1, 0, 0));
osg::ref_ptr<osg::Geometry> spRightFaceGeometory = createGeometry("guangzhou_tower.jpg", spRightFaceCoordsArray, spRightFaceNormalArray);
spGeode->addChild(spRightFaceGeometory);
// 前面
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array> spFrontFaceNormalArray = new osg::Vec3Array;
spFrontFaceNormalArray->push_back(osg::Vec3(0, -1, 0));
osg::ref_ptr<osg::Geometry> spFrontGeometory = createGeometry("csdn.jpg", spFrontFaceCoordsArray, spFrontFaceNormalArray);
spGeode->addChild(spFrontGeometory);
// 左侧面
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array> spLeftFaceNormalArray = new osg::Vec3Array;
spLeftFaceNormalArray->push_back(osg::Vec3(-1, 0, 0));
osg::ref_ptr<osg::Geometry> spLeftGeometory = createGeometry("desktop.jpg", spLeftFaceCoordsArray, spLeftFaceNormalArray);
spGeode->addChild(spLeftGeometory);
// 后面
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array> spBackFaceNormalArray = new osg::Vec3Array;
spBackFaceNormalArray->push_back(osg::Vec3(0, 1, 0));
osg::ref_ptr<osg::Geometry> spBackGeometory = createGeometry("tower.jpg", spBackFaceCoordsArray, spBackFaceNormalArray);
spGeode->addChild(spBackGeometory);
// 上面
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array> spTopFaceNormalArray = new osg::Vec3Array;
spTopFaceNormalArray->push_back(osg::Vec3(0, 0, 1));
osg::ref_ptr<osg::Geometry> spTopGeometory = createGeometry("xi_an_tower.jpg", spTopFaceCoordsArray, spTopFaceNormalArray);
spGeode->addChild(spTopGeometory);
// 底面
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array> spBottomFaceNormalArray = new osg::Vec3Array;
spBottomFaceNormalArray->push_back(osg::Vec3(0, 1, 0));
osg::ref_ptr<osg::Geometry> spBottomGeometory = createGeometry("paris_tower.jpg", spBottoomFaceCoordsArray, spBottomFaceNormalArray);
spGeode->addChild(spBottomGeometory);
// 开启光照,要不然几何体有些面转到正对相机时是黑色的
spGeode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);
osg::ref_ptr<osg::Light> 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());
效果如下:
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插件来讲解)