用OpenInventor实现的NeHe OpenGL教程-第二十六课

OpenInventor实现的NeHe OpenGL教程-第二十六课

      

 

 

NeHe教程在这节课中向我们介绍了如何创建镜面显示效果,它使用剪裁平面,蒙板缓存等OpenGL中一些高级的技巧。

 

OpenInventor中已经提供了剪裁面节点SoClipPlane。但蒙板缓存目前还没有直接支持,所以我们只能通过调用OpenGL的方式来实现蒙板缓存。这篇教程所讲述的技术比较高级,读者应该首先了解清楚蒙板缓存的功能。下面我们仍然在代码中介绍这些技术。

 

 

首先定义一些程序中使用的全局变量:

SoTranslation*              g_pOffsetTrans = NULL;

SoTranslation*              g_pHeightTrans = NULL;

SoRotation*                 g_pXRotation = NULL;

SoRotation*                 g_pYRotation = NULL;

 

//下面这些变量的含义和NeHe教程中的含义相同

float         xrot     =  0.0f;                         // X Rotation

float         yrot     =  0.0f;                         // Y Rotation

float         xrotspeed =  0.0f;                         // X Rotation Speed

float         yrotspeed =  0.0f;                         // Y Rotation Speed

float         zoom     = -7.0f;                         // Depth Into The Screen

float         height        =  2.0f;                    // Height Of Ball From Floor

 

下面的函数用来创建一个简单的地板场景

SoSeparator* BuildFloor(void)

{

     SoSeparator *pFloorSep = new SoSeparator;

 

     SoMaterial *pMaterial = new SoMaterial;

     pMaterial->diffuseColor.setValue(1,1,1);

     pMaterial->transparency = 0.3;

     pFloorSep->addChild(pMaterial);

 

     SoTexture2 *pTexture = new SoTexture2;

     pTexture->filename.setValue("../Data/Envwall.PNG");

     pTexture->model = SoTexture2::DECAL;

     pFloorSep->addChild(pTexture);

 

     SoCoordinate3 *pCoord = new SoCoordinate3;

     pCoord->point.set1Value(0,-2.0, 0.0, 2.0);

     pCoord->point.set1Value(1,2.0, 0.0, 2.0);

     pCoord->point.set1Value(2,2.0, 0.0,-2.0);

     pCoord->point.set1Value(3,-2.0, 0.0,-2.0);

     pFloorSep->addChild(pCoord);

 

     pFloorSep->addChild(new SoFaceSet);

 

     return pFloorSep;

}

 

这个函数用来创建球体场景,里面使用了前面第23课中使用的环境纹理映射,以及纹理的Alpha混合。

SoSeparator* BuildObject(void)

{

     SoSeparator *pSphereSep = new SoSeparator;

     SoTexture2 *pTexture = new SoTexture2;

     pTexture->filename.setValue("../Data/Ball.PNG");

     pSphereSep->addChild(pTexture);

 

     g_pHeightTrans = new SoTranslation;

     pSphereSep->addChild(g_pHeightTrans);

     g_pHeightTrans->translation.setValue(0,height,0);

 

     g_pXRotation = new SoRotation;

     pSphereSep->addChild(g_pXRotation);

     g_pXRotation->rotation.setValue(SbVec3f(1,0,0),xrot);

 

     g_pYRotation = new SoRotation;

     pSphereSep->addChild(g_pYRotation);

     g_pYRotation->rotation.setValue(SbVec3f(0,1,0),yrot);

 

     SoRotation *pFixXRotation = new SoRotation;

     pSphereSep->addChild(pFixXRotation);

     pFixXRotation->rotation.setValue(SbVec3f(1,0,0),3.1415 / 2.0);

 

     SoSphere *pSphere = new SoSphere;

     pSphere->radius = 0.35f;

     pSphereSep->addChild(pSphere);

     

     //创建环境纹理节点

     SoTextureCoordinateEnvironment *pTexPlane = new SoTextureCoordinateEnvironment;

     pSphereSep->addChild(pTexPlane);

    

     SoTexture2 *pEnvTexture = new SoTexture2;

     pEnvTexture->filename.setValue("../Data/Envroll.PNG");

     pSphereSep->addChild(pEnvTexture);

     //这里进行Alpha纹理混合操作

     SoCallback *pGlCallback = new SoCallback();

     pGlCallback->setCallback(GlCB, 0);

     pSphereSep->addChild(pGlCallback);

 

     pSphereSep->addChild(pSphere);

 

     pGlCallback = new SoCallback();

     pGlCallback->setCallback(GlCB, (void *)1);

     pSphereSep->addChild(pGlCallback);

 

     return pSphereSep;

}

 

构建整个场景

void BuildScene(void)

{

     //定义键盘事件节点

     SoEventCallback* pEventCallback = new SoEventCallback;

     pEventCallback->addEventCallback(SoKeyboardEvent::getClassTypeId(),

KeyboardEventCB,g_pOivSceneRoot);

     g_pOivSceneRoot->addChild(pEventCallback);

     //

     SoComplexity *pComplexity = new SoComplexity;

     pComplexity->value = 1.0;

     pComplexity->textureQuality = 1.0;

     g_pOivSceneRoot->addChild(pComplexity);

 

     g_pOffsetTrans = new SoTranslation;

     g_pOivSceneRoot->addChild(g_pOffsetTrans);

 

     SoSeparator *pFloorSep = BuildFloor();

     SoSeparator *pObjectSep = BuildObject();

     SoCallback *pGlCallback = NULL;

 

     //下面的节点调用OpenGL的蒙板缓存操作

     SoSeparator *pStencilSep = new SoSeparator;

     g_pOivSceneRoot->addChild(pStencilSep);

     pGlCallback = new SoCallback();

     pGlCallback->setCallback(GlCB, (void *)2);

     pStencilSep->addChild(pGlCallback);

     pStencilSep->addChild(pFloorSep);

     pGlCallback = new SoCallback();

     pGlCallback->setCallback(GlCB, (void *)3);

     pStencilSep->addChild(pGlCallback);

 

     //创建剪裁平面节点

     SoClipPlane *pClipPlane = new SoClipPlane;

     pClipPlane->plane.setValue(SbPlane(SbVec3f(0.0f,-1.0f, 0.0f),SbVec3f(0,0,0)));

     pStencilSep->addChild(pClipPlane);

 

     //定义反射矩阵

     SoScale *pInvert = new SoScale;

     pInvert->scaleFactor.setValue(1,-1,1);

     pStencilSep->addChild(pInvert);

     pStencilSep->addChild(pObjectSep);

    

//下面的节点调用OpenGL的蒙板缓存操作

     pGlCallback = new SoCallback();

     pGlCallback->setCallback(GlCB, (void *)4);

     pStencilSep->addChild(pGlCallback);

     /

     g_pOivSceneRoot->addChild(pFloorSep);

     g_pOivSceneRoot->addChild(pObjectSep);

     //

     SoTimerSensor * texttimer = new SoTimerSensor(TimerSensorCB, NULL);

     texttimer->setInterval(0.001);

     texttimer->schedule();

}

下面是具体的OpenGL调用,其含义和NeHe教程中相同

void GlCB(void *data, SoAction *action)

{

     if (action->isOfType(SoGLRenderAction::getClassTypeId()))

     {

         switch((int)data)

         {

         case 0:

              glEnable(GL_BLEND);                   // Enable Blending

              glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Set Blending Mode To Mix Based On SRC Alpha

              break;

         case 1:

              glDisable(GL_BLEND);             // Disable Blending

              break;

         case 2:

              glClearStencil(0);               // Clear The Stencil Buffer To 0

              glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

              glDepthFunc(GL_LEQUAL);          // The Type Of Depth Testing To Do

              glColorMask(0,0,0,0);            // Set Color Mask

              glEnable(GL_STENCIL_TEST);       // Enable Stencil Buffer For "marking" The Floor

              glStencilFunc(GL_ALWAYS, 1, 1);  // Always Passes, 1 Bit Plane, 1 As Mask

              glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

              glDisable(GL_DEPTH_TEST);        // Disable Depth Testing

              break;

         case 3:

              glEnable(GL_DEPTH_TEST);         // Enable Depth Testing

              glColorMask(1,1,1,1);            // Set Color Mask to TRUE, TRUE, TRUE, TRUE

              glStencilFunc(GL_EQUAL, 1, 1);   // We Draw Only Where The Stencil Is 1

                                               // (I.E. Where The Floor Was Drawn)

              glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);   // Don't Change The Stencil Buffer

              break;

         case 4:

              glDisable(GL_STENCIL_TEST); // We Don't Need The Stencil Buffer Any More (Disable)

              break;

         }

     }

}

 

剩下的代码和以前的课程类似,我们就不在详细介绍了。

 

       现在编译运行我们程序,屏幕会显示出一个地板和一个球体。按上下左右箭头可以旋转球体。按下A/Z键,放大/缩小场景。按下PageDown/PageUP可以抬高/降低球体。效果和NeHe第二十六课是相同的。

 

本课的完整代码下载。(VC 2003  Coin2.5

 

 

后记

OpenInventor是一种基于OpenGL的面向对象的三维图形软件开发包。使用这个开发包,程序员可以快速、简洁地开发出各种类型的交互式三维图形软件。这里不对OpenInventor做详细的介绍,读者如果感兴趣,可以阅读我的blog中的这篇文章《OpenInventor 简介》。

 

NeHe教程是目前针对初学者来说最好的OpenGL教程,它可以带领读者由浅入深,循序渐进地掌握OpenGL编程技巧。到目前为止(200711月),NeHe教程一共有48节。我的计划是使用OpenInventor来实现所有48节课程同样的效果。目的是复习和巩固OpenGL的知识,同时与各位读者交流OpenInventor的使用技巧。

 

       因为篇幅的限制,我不会介绍NeHe教程中OpenGL的实现过程,因为NeHe的教程已经讲解的很清楚了,目前网络中也有NeHe的中文版本。我将使用VC 2003作为主要的编译器。程序框架采用和NeHe一样的Win32程序框架,不使用MFC。程序也可以在VC ExpressVC 2005/2008中编译。我采用的OpenInventor开发环境是Coin,这是一个免费开源的OpenInventor开发库。文章 OpenInventorCoin3D开发环境 介绍了如何在VC中使用Coin。我使用的Coin版本是2.5。读者可以到www.coin3d.org 中免费下载。

 

       读者可以在遵循GNU协议的条件下自由使用、修改本文的代码。水平的原因,代码可能不是最优化的,我随时期待读者的指正和交流。转载请注明。谢谢。

我的联系方式:

E-mail: < openinventor@gmail.com > < openinventor@126.com >

Blog: < http://blog.csdn.net/RobinHao >

Site: < http://www.openinventor.cn >

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值