用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(SbVec 3f (1,0,0),xrot);
g_pYRotation = new SoRotation;
pSphereSep->addChild(g_pYRotation);
g_pYRotation->rotation.setValue(SbVec 3f (0,1,0),yrot);
SoRotation *pFixXRotation = new SoRotation;
pSphereSep->addChild(pFixXRotation);
pFixXRotation->rotation.setValue(SbVec 3f (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(SbVec 3f ( 0.0f , -1.0f , 0.0f ),SbVec 3f (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编程技巧。到目前为止(2007年11月),NeHe教程一共有48节。我的计划是使用OpenInventor来实现所有48节课程同样的效果。目的是复习和巩固OpenGL的知识,同时与各位读者交流OpenInventor的使用技巧。
因为篇幅的限制,我不会介绍NeHe教程中OpenGL的实现过程,因为NeHe的教程已经讲解的很清楚了,目前网络中也有NeHe的中文版本。我将使用VC 2003作为主要的编译器。程序框架采用和NeHe一样的Win32程序框架,不使用MFC。程序也可以在VC Express,VC 2005/2008中编译。我采用的OpenInventor开发环境是Coin,这是一个免费开源的OpenInventor开发库。文章 《OpenInventor-Coin3D开发环境》 介绍了如何在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 >