用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編程技巧。到目前爲止(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: < [email protected] > < [email protected] >
Blog: < http://blog.csdn.net/RobinHao >
Site: < http://www.openinventor.cn >