用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教程中的含義相同
floatxrot=0.0f;// X Rotation
floatyrot=0.0f;// Y Rotation
floatxrotspeed=0.0f;// X Rotation Speed
floatyrotspeed=0.0f;// Y Rotation Speed
floatzoom= -7.0f;// Depth Into The Screen
floatheight=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中的這篇文章《》。
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開發庫。文章《》介紹瞭如何在VC中使用Coin。我使用的Coin版本是2.5。讀者可以到中免費下載。
讀者可以在遵循GNU協議的條件下自由使用、修改本文的程式碼。水平的原因,程式碼可能不是最優化的,我隨時期待讀者的指正和交流。轉載請註明。謝謝。
我的聯絡方式: