1. 程式人生 > >用OpenInventor實現的NeHe OpenGL教程-第二十六課

用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程式設計技巧。到目前為止(200711月),NeHe教程一共有48節。我的計劃是使用OpenInventor來實現所有48節課程同樣的效果。目的是複習和鞏固OpenGL的知識,同時與各位讀者交流OpenInventor的使用技巧。

因為篇幅的限制,我不會介紹NeHe教程中OpenGL的實現過程,因為NeHe的教程已經講解的很清楚了,目前網路中也有NeHe的中文版本。我將使用VC 2003作為主要的編譯器。程式框架採用和NeHe一樣的Win32程式框架,不使用MFC。程式也可以在VC ExpressVC 2005/2008中編譯。我採用的OpenInventor開發環境是Coin,這是一個免費開源的OpenInventor開發庫。文章介紹瞭如何在VC中使用Coin。我使用的Coin版本是2.5。讀者可以到中免費下載。

讀者可以在遵循GNU協議的條件下自由使用、修改本文的程式碼。水平的原因,程式碼可能不是最優化的,我隨時期待讀者的指正和交流。轉載請註明。謝謝。

我的聯絡方式: