这节课我们将讨论凹凸纹理技术,使用凹凸纹理可以让物体表面更有质感。本节课实现的其实是一种被称为浮雕纹理的技术(Emboss Bump Mapping),并不能算真正的凹凸纹理实现方式(It is a Hack)。
光滑的表面它上面各点法线的方向是一致的,凹凸纹理通过扰乱它表面的法线方向,在进行光照计算的时候(像素光照计算)会使得光滑表面产生明暗不同的颜色,从而"欺骗"我们的大脑,让我们以为它的表面是凹凸不平的。凹凸纹理最早是由James F. Blinn在1978年提的
NeHe课程中的实现原理可以参考凹凸纹理随笔, 在开始之前先说明一下NeHe课程中的实现方式:
NeHe教程中通过空格切换在三种模式下进行:
1. 未使用多重纹理(MultiTexture)技术进行凹凸纹理处理
2.使用多重纹理(MultiTexture)技术进行凹凸纹理处理
3.渲染不带凹凸纹理的场景
这三个过程分别对应doMesh1TexelUnits、doMesh1Texe2Units、doMeshNoBumps三个函数。本课只实现了在1情形下的凹凸纹理。2的实现也在源码中,但是未找到对应的OpenGL API,希望知道的读者指点一二。
下面简单说一下Emboss Bump Mapping的实现过程:
浮雕纹理需要对模型表面贴两次纹理,第一次纹理的纹理坐标不进行偏移、第二次的纹理坐标会在光照方向上进行偏移。偏移量的计算方法可以参考PPT Emboss Bump Mapping
首先和NeHe教程一下初始化我们需要的纹理:
这里面需要使用osg中的Image类和处理Image的ImageUtil中的内容:部分代码如下:
osg::Image *image1 = new osg::Image; image1 = osgDB::readImageFile("Data/Base.bmp"); texture[0] = new osg::Texture2D; texture[0]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); texture[0]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); texture[0]->setImage(image1); texture[1] = new osg::Texture2D; texture[1]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture[1]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture[1]->setImage(image1);另外我们需要把进行纹理贴图的图像亮度减半,代码如下:
osg::Image *image2 = new osg::Image; image2 = osgDB::readImageFile("Data/Bump.bmp"); osg::offsetAndScaleImage(image2, osg::Vec4(), osg::Vec4(0.5, 0.5, 0.5, 1)); // RGB值都减半 bump[0] = new osg::Texture2D; bump[0]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); bump[0]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); bump[0]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); bump[0]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); bump[0]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); bump[0]->setImage(image2);此外反转图像RGB值,使得凹的纹理变成凸的纹理:
osg::Image *image3 = osgDB::readImageFile("Data/Bump.bmp"); osg::offsetAndScaleImage(image3, osg::Vec4(), osg::Vec4(0.5,0.5,0.5,1)); osg::offsetAndScaleImage(image3, osg::Vec4(-128, -128, -128, 1), osg::Vec4(-1,-1,-1,1)); invbump[0] = new osg::Texture2D; invbump[0]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); invbump[0]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); invbump[0]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); invbump[0]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); invbump[0]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); invbump[0]->setImage(image3);创建正常显示立方体的代码在createCube函数中。
为了进行凹凸纹理,我们先设置第一次纹理部分,代码如下:
可以看到这一次贴的纹理的纹理坐标并没有偏移,全部用到了data中的原始数据
osg::Geode* createUpMappingCube() { osg::Geode *cubeGeode = new osg::Geode; osg::Geometry *cubeGeometry = new osg::Geometry; osg::Vec3Array *vertices = new osg::Vec3Array; osg::Vec3Array *normals = new osg::Vec3Array; osg::Vec2Array *texcoords = new osg::Vec2Array; for (unsigned i = 0; i < 24; ++i) { texcoords->push_back(osg::Vec2(data[5*i],data[5*i + 1])); //第一次贴纹理不需要偏移 vertices->push_back(osg::Vec3(data[5*i + 2], data[5*i +3], data[5*i + 4])); } cubeGeometry->setVertexArray(vertices); cubeGeometry->setTexCoordArray(0, texcoords); cubeGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertices->size())); cubeGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, false); cubeGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, bump[1]); osg::Depth *depth = new osg::Depth(osg::Depth::LEQUAL); cubeGeometry->getOrCreateStateSet()->setAttributeAndModes(depth); cubeGeode->addDrawable(cubeGeometry); return cubeGeode; }在进行第二次贴纹理时由于要把光源位置变换到物体表面的切空间中,需要获取此时模型的模型视图矩阵,在设置中难以获得,在回调中获取:
//第二次贴纹理,这里面并没有纹理坐标,因为纹理坐标需要偏移,在回调中计算 osg::Geode* createDownMappingCube() { osg::Geode *cubeGeode = new osg::Geode; osg::Geometry *cubeGeometry = new osg::Geometry; cubeGeometry->setUpdateCallback(new TextureCallback()); osg::Vec3Array *vertices = new osg::Vec3Array; for (unsigned i = 0; i < 24; ++i) { vertices->push_back(osg::Vec3(data[5*i + 2], data[5*i +3], data[5*i + 4])); } cubeGeometry->setVertexArray(vertices); cubeGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertices->size())); cubeGeode->addDrawable(cubeGeometry); return cubeGeode; }
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable) { osg::Geometry *geometry = drawable->asGeometry(); if (!geometry) return; osg::Vec2Array *textureArray = dynamic_cast<osg::Vec2Array*>(geometry->getTexCoordArray(0)); if(textureArray) return; textureArray = new osg::Vec2Array; osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()); osg::Vec3 lightPos(LightPosition[0], LightPosition[1], LightPosition[2]); //获取当前模型视图矩阵 osg::Matrix currentModelViewMatrix = computeLocalToWorld(nv->getNodePath()) * g_viewer->getCamera()->getViewMatrix(); //反转模型视图矩阵 osg::Matrix currentInvModelViewerMatrix = osg::Matrix::inverse(currentModelViewMatrix); //将光源变换到物体建模坐标系下 osg::Vec3 lightInObjectSpace = lightPos * currentInvModelViewerMatrix; ////////////////////////////////////////////////////////////////////////// for (int i=0; i<4; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); //获取切空间中在S、T轴上的偏移量 double deltaS = orientation * osg::Vec3(1,0,0) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); }最后把这三次的结果添加到场景中
osg::Switch *switchNode = new osg::Switch; g_switch = switchNode; rotateYAxis->addChild(switchNode); switchNode->addChild(createUpMappingCube()); switchNode->addChild(createDownMappingCube()); switchNode->addChild(createCube());在交互操作中设置显示和隐藏原始的模型:
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_E) { if (g_switch->getValue(2) == true) { g_switch->setValue(2, false); } else { g_switch->setValue(2, true); } }编译运行程序:
附:本课源码(源码中可能存在错误和不足,仅供参考)
#include "../osgNeHe.h" #include <QtCore/QTimer> #include <QtGui/QApplication> #include <QtGui/QVBoxLayout> #include <osgViewer/Viewer> #include <osgDB/ReadFile> #include <osgQt/GraphicsWindowQt> #include <osg/MatrixTransform> #include <osg/LightSource> #include <osg/ImageUtils> #include <osg/Texture2D> #include <osg/BlendFunc> #include <osg/Depth> #include <osg/TexEnv> #include <osg/TexEnvCombine> ////////////////////////////////////////////////////////////////////////// const float MaxEmboss = 0.008f; bool multitextureSupported = false; bool useMultitexture = true; osgViewer::View *g_viewer; osg::Switch *g_switch; osg::Switch *g_switchMulti; GLfloat xrot; GLfloat yrot; GLfloat xspeed; GLfloat yspeed; GLfloat z=-5.0f; GLuint filter=1; osg::Texture2D *texture[3]; osg::Texture2D *bump[3]; osg::Texture2D *invbump[3]; osg::Texture2D *glLogo; osg::Texture2D *multiLogo; GLfloat LightAmbient[] = { 0.2f, 0.2f, 0.2f}; GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f}; GLfloat LightPosition[] = { 0.0f, 0.0f, 2.0f}; GLfloat data[]= { // FRONT FACE 0.0f, 0.0f, -1.0f, -1.0f, +1.0f, 1.0f, 0.0f, +1.0f, -1.0f, +1.0f, 1.0f, 1.0f, +1.0f, +1.0f, +1.0f, 0.0f, 1.0f, -1.0f, +1.0f, +1.0f, // BACK FACE 1.0f, 0.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, +1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, -1.0f, 0.0f, 0.0f, +1.0f, -1.0f, -1.0f, // Top Face 0.0f, 1.0f, -1.0f, +1.0f, -1.0f, 0.0f, 0.0f, -1.0f, +1.0f, +1.0f, 1.0f, 0.0f, +1.0f, +1.0f, +1.0f, 1.0f, 1.0f, +1.0f, +1.0f, -1.0f, // Bottom Face 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, +1.0f, -1.0f, -1.0f, 0.0f, 0.0f, +1.0f, -1.0f, +1.0f, 1.0f, 0.0f, -1.0f, -1.0f, +1.0f, // Right Face 1.0f, 0.0f, +1.0f, -1.0f, -1.0f, 1.0f, 1.0f, +1.0f, +1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, +1.0f, 0.0f, 0.0f, +1.0f, -1.0f, +1.0f, // Left Face 0.0f, 0.0f, -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, -1.0f }; ////////////////////////////////////////////////////////////////////////// //FindFirstNamedNodeVisitor用来遍历寻找与指定名称相同的节点 class FindFirstNamedNodeVisitor : public osg::NodeVisitor { public: FindFirstNamedNodeVisitor(const std::string& name): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _name(name), _foundNode(NULL) {} virtual void apply(osg::Node& node) { if (node.getName()==_name) { _foundNode = &node; return; } traverse(node); } std::string _name; osg::Node *_foundNode; }; ////////////////////////////////////////////////////////////////////////// //RotateCallback class RotateCallback : public osg::NodeCallback { public: RotateCallback(osg::Vec3d rotateAxis, double rotateSpeed) : osg::NodeCallback(), _rotateAxis(rotateAxis), _rotateSpeed(rotateSpeed), _rotateAngle(0.0) { //Nop } void setRotateSpeed(double speed) { _rotateSpeed = speed; } double getRotateSpeed() const { return _rotateSpeed; } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform *currentMT = dynamic_cast<osg::MatrixTransform*>(node); if (currentMT) { //获取当前的平移位置 osg::Vec3d currentTranslate = currentMT->getMatrix().getTrans(); osg::Matrix newMatrix = osg::Matrix::rotate(_rotateAngle, _rotateAxis) * osg::Matrix::translate(currentTranslate); currentMT->setMatrix(newMatrix); _rotateAngle += _rotateSpeed; } traverse(node, nv); } private: osg::Vec3d _rotateAxis; //旋转轴 double _rotateSpeed; //旋转速度 double _rotateAngle; //当前旋转的角度 }; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// class ManipulatorSceneHandler : public osgGA::GUIEventHandler { public: ManipulatorSceneHandler() { } virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&aa); if (!viewer) return false; if (!viewer->getSceneData()) return false; if (ea.getHandled()) return false; osg::Group *root = viewer->getSceneData()->asGroup(); switch(ea.getEventType()) { case(osgGA::GUIEventAdapter::KEYDOWN): { if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left) { FindFirstNamedNodeVisitor fnv("yRotMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *yRotMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!yRotMT) return false; RotateCallback *rotCallback = dynamic_cast<RotateCallback*>(yRotMT->getUpdateCallback()); if (!rotCallback) return false; double speed = rotCallback->getRotateSpeed(); speed += 0.02; rotCallback->setRotateSpeed(speed); } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right) { FindFirstNamedNodeVisitor fnv("yRotMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *yRotMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!yRotMT) return false; RotateCallback *rotCallback = dynamic_cast<RotateCallback*>(yRotMT->getUpdateCallback()); if (!rotCallback) return false; double speed = rotCallback->getRotateSpeed(); speed -= 0.02; rotCallback->setRotateSpeed(speed); } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up) { FindFirstNamedNodeVisitor fnv("xRotMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *xRotMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!xRotMT) return false; RotateCallback *rotCallback = dynamic_cast<RotateCallback*>(xRotMT->getUpdateCallback()); if (!rotCallback) return false; double speed = rotCallback->getRotateSpeed(); speed += 0.02; rotCallback->setRotateSpeed(speed); } if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Down) { FindFirstNamedNodeVisitor fnv("xRotMT"); root->accept(fnv); osg::Node *mtNode = fnv._foundNode; osg::MatrixTransform *xRotMT = dynamic_cast<osg::MatrixTransform*>(mtNode); if (!xRotMT) return false; RotateCallback *rotCallback = dynamic_cast<RotateCallback*>(xRotMT->getUpdateCallback()); if (!rotCallback) return false; double speed = rotCallback->getRotateSpeed(); speed -= 0.02; rotCallback->setRotateSpeed(speed); } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_E) { if (g_switch->getValue(2) == true) { g_switch->setValue(2, false); } else { g_switch->setValue(2, true); } } } default: break; } return false; } }; ////////////////////////////////////////////////////////////////////////// class ViewerWidget : public QWidget, public osgViewer::Viewer { public: ViewerWidget(osg::Node *scene = NULL) { QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,100,100), scene); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(renderWidget); layout->setContentsMargins(0, 0, 0, 1); setLayout( layout ); connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) ); _timer.start( 10 ); } QWidget* getRenderWidget( osgQt::GraphicsWindowQt* gw, osg::Node* scene ) { osg::Camera* camera = this->getCamera(); camera->setGraphicsContext( gw ); const osg::GraphicsContext::Traits* traits = gw->getTraits(); camera->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 0.5) ); camera->setClearDepth(1.0); camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); camera->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); camera->setViewMatrixAsLookAt(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0)); this->setSceneData( scene ); addEventHandler(new ManipulatorSceneHandler); g_viewer = this; return gw->getGLWidget(); } osgQt::GraphicsWindowQt* createGraphicsWindow( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false ) { osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->windowName = name; traits->windowDecoration = windowDecoration; traits->x = x; traits->y = y; traits->width = w; traits->height = h; traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); return new osgQt::GraphicsWindowQt(traits.get()); } virtual void paintEvent( QPaintEvent* event ) { frame(); } protected: QTimer _timer; }; ////////////////////////////////////////////////////////////////////////// //创建纹理:代码中只是用到第二个纹理bump[1]和invbmp[1] void initTextures() { osg::Image *image1 = new osg::Image; image1 = osgDB::readImageFile("Data/Base.bmp"); texture[0] = new osg::Texture2D; texture[0]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); texture[0]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); texture[0]->setImage(image1); texture[1] = new osg::Texture2D; texture[1]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture[1]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture[1]->setImage(image1); texture[2] = new osg::Texture2D; texture[2]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture[2]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_NEAREST); texture[2]->setImage(image1); osg::Image *image2 = new osg::Image; image2 = osgDB::readImageFile("Data/Bump.bmp"); osg::offsetAndScaleImage(image2, osg::Vec4(), osg::Vec4(0.5, 0.5, 0.5, 1)); bump[0] = new osg::Texture2D; bump[0]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); bump[0]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); bump[0]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); bump[0]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); bump[0]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); bump[0]->setImage(image2); bump[1] = new osg::Texture2D; bump[1]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); bump[1]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); bump[1]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); bump[1]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); bump[1]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); bump[1]->setImage(image2); bump[2] = new osg::Texture2D; bump[2]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); bump[2]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_NEAREST); bump[2]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); bump[2]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); bump[2]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); bump[2]->setImage(image2); osg::Image *image3 = osgDB::readImageFile("Data/Bump.bmp"); osg::offsetAndScaleImage(image3, osg::Vec4(), osg::Vec4(0.5,0.5,0.5,1)); osg::offsetAndScaleImage(image3, osg::Vec4(-128, -128, -128, 1), osg::Vec4(-1,-1,-1,1)); invbump[0] = new osg::Texture2D; invbump[0]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); invbump[0]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); invbump[0]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); invbump[0]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); invbump[0]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); invbump[0]->setImage(image3); invbump[1] = new osg::Texture2D; invbump[1]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); invbump[1]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); invbump[1]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); invbump[1]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); invbump[1]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); invbump[1]->setImage(image3); invbump[2] = new osg::Texture2D; invbump[2]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); invbump[2]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_NEAREST); invbump[2]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); invbump[2]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); invbump[2]->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); invbump[2]->setImage(image3); osg::Image *glLogoAlpha = osgDB::readImageFile("Data/OpenGL_ALPHA.bmp"); osg::Image *glLogoImage = osgDB::readImageFile("Data/OpenGL.bmp"); osg::Image *glLogoCombo = new osg::Image; glLogoCombo->allocateImage(glLogoImage->s(), glLogoImage->t(), 1, GL_RGBA, glLogoImage->getDataType()); unsigned char *comboData = glLogoCombo->data(); for (int i = 0; i < glLogoImage->s() * glLogoImage->t(); ++i) { comboData[4*i+3] = glLogoAlpha->data()[i*3]; comboData[4*i] = glLogoImage->data()[i*3]; comboData[4*i+1] = glLogoImage->data()[i*3+1]; comboData[4*i+2] = glLogoImage->data()[i*3+2]; } glLogo = new osg::Texture2D; glLogo->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); glLogo->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); glLogo->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); glLogo->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); glLogo->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); glLogo->setImage(glLogoCombo); osg::Image *glMultiAlpha = osgDB::readImageFile("Data/multi_on_alpha.bmp"); osg::Image *glMultiImage = osgDB::readImageFile("Data/multi_on.bmp"); osg::Image *glMultiCombo = new osg::Image; glMultiCombo->allocateImage(glLogoImage->s(), glLogoImage->t(), 1, GL_RGBA, glLogoImage->getDataType()); unsigned char *multiComboData = glMultiCombo->data(); for (int i = 0; i < glMultiImage->s() * glMultiImage->t(); ++i) { multiComboData[4*i+3] = glMultiAlpha->data()[i*3]; multiComboData[4*i] = glMultiImage->data()[i*3]; multiComboData[4*i+1] = glMultiImage->data()[i*3+1]; multiComboData[4*i+2] = glMultiImage->data()[i*3+2]; } multiLogo = new osg::Texture2D; multiLogo->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); multiLogo->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); multiLogo->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); multiLogo->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); multiLogo->setBorderColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); multiLogo->setImage(glMultiCombo); } osg::Group *createLogo() { osg::Group *logoGroup = new osg::Group; osg::Depth *depth = new osg::Depth(osg::Depth::ALWAYS); osg::BlendFunc *blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); osg::Geode *logoGeode = new osg::Geode; osg::Geometry *logoGeometry = new osg::Geometry; osg::Vec3Array *logoVertices = new osg::Vec3Array; logoVertices->push_back(osg::Vec3(0.23f, -0.4f,-1.0f)); logoVertices->push_back(osg::Vec3(0.53f, -0.4f,-1.0f)); logoVertices->push_back(osg::Vec3(0.53f, -0.25f,-1.0f)); logoVertices->push_back(osg::Vec3(0.23f, -0.25f,-1.0f)); osg::Vec2Array *textureVertices = new osg::Vec2Array; textureVertices->push_back(osg::Vec2(0.0f,0.0f)); textureVertices->push_back(osg::Vec2(1.0f,0.0f)); textureVertices->push_back(osg::Vec2(1.0f,1.0f)); textureVertices->push_back(osg::Vec2(0.0f,1.0f)); logoGeometry->setVertexArray(logoVertices); logoGeometry->setTexCoordArray(0, textureVertices); logoGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); logoGeometry->getOrCreateStateSet()->setAttributeAndModes(depth); logoGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); logoGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, glLogo); logoGeode->addDrawable(logoGeometry); osg::Geode *multiGeode = new osg::Geode; osg::Geometry *multiGeometry = new osg::Geometry; osg::Vec3Array *multiVertices = new osg::Vec3Array; multiVertices->push_back(osg::Vec3(-0.53f, -0.4f,-1.0f)); multiVertices->push_back(osg::Vec3(-0.33f, -0.4f,-1.0f)); multiVertices->push_back(osg::Vec3(-0.33f, -0.3f,-1.0f)); multiVertices->push_back(osg::Vec3(-0.53f, -0.3f,-1.0f)); osg::Vec2Array *tVertices = new osg::Vec2Array; textureVertices->push_back(osg::Vec2(0.0f,0.0f)); textureVertices->push_back(osg::Vec2(1.0f,0.0f)); textureVertices->push_back(osg::Vec2(1.0f,1.0f)); textureVertices->push_back(osg::Vec2(0.0f,1.0f)); multiGeometry->setVertexArray(multiVertices); multiGeometry->setTexCoordArray(0, tVertices); multiGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); multiGeometry->getOrCreateStateSet()->setAttributeAndModes(depth); multiGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); multiGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, multiLogo); multiGeode->addDrawable(multiGeometry); logoGroup->addChild(logoGeode); logoGroup->addChild(multiGeode); return logoGroup; } osg::Geode* createCube() { osg::Geode *cubeGeode = new osg::Geode; osg::Geometry *cubeGeometry = new osg::Geometry; osg::Vec3Array *vertices = new osg::Vec3Array; osg::Vec3Array *normals = new osg::Vec3Array; osg::Vec2Array *texcoords = new osg::Vec2Array; for (unsigned i = 0; i < 24; ++i) { texcoords->push_back(osg::Vec2(data[5*i],data[5*i + 1])); vertices->push_back(osg::Vec3(data[5*i + 2], data[5*i +3], data[5*i + 4])); } normals->push_back(osg::Vec3(0.0f, 0.0f, +1.0f)); normals->push_back(osg::Vec3(0.0f, 0.0f,-1.0f)); normals->push_back(osg::Vec3(0.0f, 1.0f, 0.0f)); normals->push_back(osg::Vec3(0.0f,-1.0f, 0.0f)); normals->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); normals->push_back(osg::Vec3(-1.0f, 0.0f, 0.0f)); cubeGeometry->setVertexArray(vertices); cubeGeometry->setTexCoordArray(0, texcoords); cubeGeometry->setNormalArray(normals, osg::Array::BIND_PER_PRIMITIVE_SET); cubeGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertices->size())); cubeGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture[1]); osg::BlendFunc *blendFunc = new osg::BlendFunc(osg::BlendFunc::DST_COLOR, osg::BlendFunc::SRC_COLOR); cubeGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); osg::Depth *depth = new osg::Depth(osg::Depth::LEQUAL); cubeGeometry->getOrCreateStateSet()->setAttributeAndModes(depth); osg::TexEnv *texEnv = new osg::TexEnv(osg::TexEnv::MODULATE); cubeGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texEnv); cubeGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); cubeGeode->addDrawable(cubeGeometry); return cubeGeode; } osg::Geode* createUpMappingCube() { osg::Geode *cubeGeode = new osg::Geode; osg::Geometry *cubeGeometry = new osg::Geometry; osg::Vec3Array *vertices = new osg::Vec3Array; osg::Vec3Array *normals = new osg::Vec3Array; osg::Vec2Array *texcoords = new osg::Vec2Array; for (unsigned i = 0; i < 24; ++i) { texcoords->push_back(osg::Vec2(data[5*i],data[5*i + 1])); vertices->push_back(osg::Vec3(data[5*i + 2], data[5*i +3], data[5*i + 4])); } cubeGeometry->setVertexArray(vertices); cubeGeometry->setTexCoordArray(0, texcoords); cubeGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertices->size())); cubeGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, false); cubeGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, bump[1]); osg::Depth *depth = new osg::Depth(osg::Depth::LEQUAL); cubeGeometry->getOrCreateStateSet()->setAttributeAndModes(depth); cubeGeode->addDrawable(cubeGeometry); return cubeGeode; } class TextureCallback : public osg::Drawable::UpdateCallback { public: TextureCallback(){ } virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable) { osg::Geometry *geometry = drawable->asGeometry(); if (!geometry) return; osg::Vec2Array *textureArray = dynamic_cast<osg::Vec2Array*>(geometry->getTexCoordArray(0)); if(textureArray) return; textureArray = new osg::Vec2Array; osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()); osg::Vec3 lightPos(LightPosition[0], LightPosition[1], LightPosition[2]); osg::Matrix currentModelViewMatrix = computeLocalToWorld(nv->getNodePath()) * g_viewer->getCamera()->getViewMatrix(); osg::Matrix currentInvModelViewerMatrix = osg::Matrix::inverse(currentModelViewMatrix); osg::Vec3 lightInObjectSpace = lightPos * currentInvModelViewerMatrix; ////////////////////////////////////////////////////////////////////////// for (int i=0; i<4; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(1,0,0) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=4; i<8; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(-1, 0, 0) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=8; i<12; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(1, 0, 0) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,0,-1) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=12; i<16; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(0, -1, 0) * MaxEmboss; double deltaT = orientation * osg::Vec3(-1,0,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=16; i<20; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(0, 0, -1) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=20; i<24; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(0, 0, 1) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } geometry->setTexCoordArray(0, textureArray); geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, invbump[1]); geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, false); osg::BlendFunc *blendFunc = new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE); geometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); osg::Depth *depth = new osg::Depth(osg::Depth::LEQUAL); geometry->getOrCreateStateSet()->setAttributeAndModes(depth); } }; osg::Geode* createDownMappingCube() { osg::Geode *cubeGeode = new osg::Geode; osg::Geometry *cubeGeometry = new osg::Geometry; cubeGeometry->setUpdateCallback(new TextureCallback()); osg::Vec3Array *vertices = new osg::Vec3Array; for (unsigned i = 0; i < 24; ++i) { vertices->push_back(osg::Vec3(data[5*i + 2], data[5*i +3], data[5*i + 4])); } cubeGeometry->setVertexArray(vertices); cubeGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertices->size())); cubeGeode->addDrawable(cubeGeometry); return cubeGeode; } ////////////////////////////////////////////////////////////////////////// //Method 2:Using MultiTexture to Render // //TODO:使用MultiTexture的方式: // TODO: 未在OpenGL文档中查询到 // glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); //glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE); //不清楚是否是新的扩展,请读者指点 ////////////////////////////////////////////////////////////////////////// #if 0 class MultiTextureUpdateCallback : public osg::Drawable::UpdateCallback { public: virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable) { osg::Geometry *geometry = drawable->asGeometry(); if (!geometry) return; osg::Vec2Array *textureArray = dynamic_cast<osg::Vec2Array*>(geometry->getTexCoordArray(1)); if(textureArray) return; textureArray = new osg::Vec2Array; osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()); osg::Vec3 lightPos(LightPosition[0], LightPosition[1], LightPosition[2]); osg::Matrix currentModelViewMatrix = computeLocalToWorld(nv->getNodePath()) * g_viewer->getCamera()->getViewMatrix(); osg::Matrix currentInvModelViewerMatrix = osg::Matrix::inverse(currentModelViewMatrix); osg::Vec3 lightInObjectSpace = lightPos * currentInvModelViewerMatrix; ////////////////////////////////////////////////////////////////////////// for (int i=0; i<4; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(1,0,0) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=4; i<8; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(-1, 0, 0) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=8; i<12; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(1, 0, 0) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,0,-1) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=12; i<16; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(0, -1, 0) * MaxEmboss; double deltaT = orientation * osg::Vec3(-1,0,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=16; i<20; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(0, 0, -1) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } for (int i=20; i<24; i++) { osg::Vec3 pos = vertices->at(i); osg::Vec3 orientation = lightInObjectSpace - pos; orientation.normalize(); double deltaS = orientation * osg::Vec3(0, 0, 1) * MaxEmboss; double deltaT = orientation * osg::Vec3(0,1,0) * MaxEmboss; textureArray->push_back(osg::Vec2(data[5*i]+deltaS,data[5*i + 1]+deltaT)); } geometry->setTexCoordArray(1, textureArray); geometry->getOrCreateStateSet()->setTextureAttributeAndModes(1, invbump[1]); osg::TexEnv *tex2Env = new osg::TexEnv(osg::TexEnv::ADD); geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex2Env); geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, false); } }; #endif osg::Node* buildScene() { initTextures(); osg::Group *root = new osg::Group; osg::MatrixTransform *zoomMT = new osg::MatrixTransform; zoomMT->setMatrix(osg::Matrix::translate(0.0, 0.0, -5.0)); osg::MatrixTransform *rotateXAxis = new osg::MatrixTransform; rotateXAxis->setName("xRotMT"); RotateCallback *xRotCallback = new RotateCallback(osg::X_AXIS, 0.0); rotateXAxis->setUpdateCallback(xRotCallback); zoomMT->addChild(rotateXAxis); osg::MatrixTransform *rotateYAxis = new osg::MatrixTransform; rotateYAxis->setName("yRotMT"); RotateCallback *yRotCallback = new RotateCallback(osg::Y_AXIS, 0.0); rotateYAxis->setUpdateCallback(yRotCallback); rotateXAxis->addChild(rotateYAxis); osg::Switch *switchNode = new osg::Switch; g_switch = switchNode; rotateYAxis->addChild(switchNode); switchNode->addChild(createUpMappingCube()); switchNode->addChild(createDownMappingCube()); switchNode->addChild(createCube()); root->addChild(zoomMT); return root; } int main( int argc, char** argv ) { QApplication app(argc, argv); ViewerWidget* viewWidget = new ViewerWidget(buildScene()); viewWidget->setGeometry( 100, 100, 640, 480 ); viewWidget->show(); return app.exec(); }