目录
前文讲述了如何给osg自带的几何体,如:BOX等,添加纹理,文章参考链接如下:
下面代码展示将一张图作为纹理,贴到整个几何体上,即几何体由几个面组成,则每个面的纹理都是该图片形成的纹理。代码如下:
- #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_ptr
spTexture2D = 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); // 开启纹理
-
- // 开启光照,要不然几何体有些面转到正对相机时是黑色的
- 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;
-
- }
-
- 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,而不是所有面形成一个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_ptr
spTexture2D = 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::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插件来讲解)