摘要:本文介紹了OpenGL在三維物體建模中的應用,並在VC++下通過該技術實現了對自由型曲線曲面的模型搭建。
關鍵詞:OpenGL;自由型曲線曲面;NURBS;Visual C++
引言
隨著電腦技術的飛速發展,電腦的性價比越來越高,為電腦圖形學的發展奠定了堅實的基礎。科學計算可視化、電腦動畫和虛擬現實等技術在工程應用中 得到了廣泛的使用,這些技術的實現離不開對真實物體的三維建模。在實體建模時為減少運算量和儲存空間多採用少量的控制點和各種光滑的樣條和樣條曲面如B樣 條、Nurbs曲面和貝塞爾(Bezier)曲面等來對其作精確的描述。自由型曲線曲面是一種非常重要的工業產品幾何定義,在電腦輔助設計類軟體中應用 較為普遍。自1991年ISO(International Standardization Organization,國際標準組織)頒布了STEP標準後由於在標準中明確規定了自由型曲線曲面在工程中唯一地用NURBS (Non_Uniform Rational B-Spline,非均勻有理B樣條)表示而且在OpenGL(Open Graphics Library,開放式圖形庫)中對NURBS提供有建立在求值器基礎之上的高層編程介面,因此在三維實體建模中多通過使用OpenGL求值器來完成對各 種複雜自由型曲線曲面的構造。本文將在VC下對OpenGL提供的這個編程介面的使用進行介紹,並通過對該介面的編程實現對自由型曲線曲面的繪製。
OpenGL基本程式架構
OpenGL三維圖形標準是由AT&T、IBM、DEC、SUN、HP、Microsoft和SGI等多家公司在GL圖形庫標準的基礎上聯合推出的開放 式標準,它的出現徹底改變過去只能依賴於價格昂貴的圖形工作站及複雜的三維圖形軟體從事三維圖形電腦應用的曆史,使廣大程式員能夠在PC機上用C語言開 發出複雜的三維圖形。由於OpenGL具有跨平台性、簡便、高效、功能完善,目前已經成為三維圖形製作中事實上的工業標準。它不僅提供了繪製點、線、多邊 形等基本形體的函數,還提供了繪製複雜三維曲線曲面和三維形體的函數。由於OpenGL在進行幾何建模時是以頂點為圖元,由點構成線,再由線及其拓撲結構 組成多邊形曲面,因此應用OpenGL提供的建模函數可構造出幾乎所有的3D 模型。
OpenGL的作用機制是客戶(Client)服 務器(Server)機制,客戶(用OpenGL繪製圖形的應用程式)向伺服器(OpenGL核心)發送OpenGL指令,由伺服器負責解釋執行這些命 令。OpenGL圖形庫被封裝在動態串連庫opengl32.dll內,在使用時從客戶應用程式發出的對OpenGL介面函數的調用首先被 opengl32.dll處理,然後傳送給伺服器並由Winsrv.dll做進一步處理後傳給Win32 DDI(Device Drivers Interface,裝置驅動介面),最後把處理過的圖形命令送給視頻顯示驅動程式完成最終的顯示。
由於OpenGL的輔助庫函數對於自由型曲線曲面的繪製提供有NURBS編程介面,因此使用起來比較簡單。使用OpenGL之前,必須通過初始化代碼對其進行初始化並定義像素的儲存格式等資訊:
PIXELFORMATDESCRIPTOR pixelDesc; pixelDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);//pfd的大小 pixelDesc.nVersion = 1; //版本號碼 pixelDesc.dwFlags = PFD_DRAW_TO_WINDOW | //支援視窗 PFD_SUPPORT_OPENGL| //支援OpenGL PFD_DOUBLEBUFFER; //支援雙緩衝 pixelDesc.iPixelType = PFD_TYPE_RGBA; //RGBA類型 pixelDesc.cColorBits = 32; //32位色深度 pixelDesc.cRedBits = 8; //各顏色位 pixelDesc.cRedShift = 16; pixelDesc.cGreenBits = 8; pixelDesc.cGreenShift = 8; pixelDesc.cBlueBits = 8; pixelDesc.cBlueShift = 0; pixelDesc.cAlphaBits = 0; //無alpha緩衝 pixelDesc.cAlphaShift = 0; //忽略轉換位 pixelDesc.cAccumBits = 64; //各累計位 pixelDesc.cAccumRedBits = 16; pixelDesc.cAccumGreenBits = 16; pixelDesc.cAccumBlueBits = 16; pixelDesc.cAccumAlphaBits = 0; pixelDesc.cDepthBits = 32; //32位元深度緩衝 pixelDesc.cStencilBits = 0; //無模版緩衝 pixelDesc.cAuxBuffers = 0; //無輔助緩衝 pixelDesc.iLayerType = PFD_MAIN_PLANE; //主層 pixelDesc.bReserved = 0; //保留 pixelDesc.dwLayerMask = 0; pixelDesc.dwVisibleMask = 0; pixelDesc.dwDamageMask = 0; |
這是初始化工作的主要內容,對描述像素儲存格式的PIXELFORMATDESCRIPTOR結構變數進行了填充,剩下的工作主要是通過調用 ChoosePixelFormat()和SetPixelFormat()函數以返回並設定首選的像素格式。最後在繪製自由型曲線曲面之前用 wglCreateContext()建立一個渲染上下文RC並將其作為參數通過wglMakeCurrent()來建立一個當前的繪圖描述表,並在繪製 完畢後將其釋放:
//設定一個線程的當前繪圖描述表 wglMakeCurrent(hDC,m_hGLContext); //對自由型曲線曲面的繪製 DoNURBS(); //釋放繪圖描述表 wglMakeCurrent(hDC,NULL); //釋放RC(一般在WM_DESTORY訊息響應函數OnDestory()中進行) wglDeleteContext(m_hGLContext); |
至此,一個基於OpenGL的基本程式架構構造完畢,具體對自由型曲線曲面的構造工作在DoNURBS()中完成。
對自由型曲線曲面的繪製
OpenGL的一個很大的優點就是在繪製構造三維實體的光滑曲線和曲面時 只需少量控制點就完成了對其的精確描述,而不必靠大量的基本圖元去擬合。NURBS就是我們熟悉的B樣條的擴充,只不過引入了權因子並以有理分式表示其曲 線。因此用OpenGL的NURBS編程介面繪製自由型曲線曲面只需預先對節點序列和控制點進行定義即可完成對NURBS樣條的精確描述:
//節點序列 GLfloat Notes[8]={0.0f,0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,1.0f}; //定義控制點 for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { LinchpinPt[i][j][0]=(GLfloat)(3.0f*(i-0.5f)); LinchpinPt[i][j][1]=(GLfloat)(3.0f*(j-0.5f)); if(0<i && i<3 && 0<j && j<3) LinchpinPt[i][j][2]=4.0f; else LinchpinPt[i][j][2]=-4.0f; } } |
使用OpenGL提供的NURBS編程介面進行編程比較簡潔:首先建立一個NURBS曲面對象並根據需要對其屬性進行設定,在調用了 gluBeginSurface()或是gluBeginCurve()後就可以根據前面定義的控制點對其曲線曲面進行繪製了,繪製完畢後以 gluEndSurface()或gluEndCurve()結束:
//建立NURBS曲面對象 Nurb=gluNewNurbsRenderer(); //設定NURBS曲面對象屬性 gluNurbsProperty(Nurb,GLU_SAMPLING_TOLERANCE,30.0f); gluNurbsProperty(Nurb,GLU_DISPLAY_MODE,GLU_FILL); //開始NURB區間繪製 gluBeginSurface(Nurb); //定義NURBS曲面的形狀 gluNurbsSurface(Nurb,8,Notes,8, Notes,4*3,3, &LinchpinPt[0][0][0], 4,4,GL_MAP2_VERTEX_3); //結束曲面繪製 gluEndSurface(Nurb); glPopMatrix(); //強制繪圖,不駐留緩衝 glFlush(); |
其中gluNurbsSurface()是定義NURBS曲線曲面的關鍵函數,其函數原型定義如下:
void gluNurbsSurface( GLUnurbsObj * nobj, //NURBS曲面對象 GLint sknot_count, //u方向節點數目 GLfloat * sknot, //u方向節點數組指標 GLint tknot_count, //v方向節點數目 GLfloat * tknot, //v方向節點數組指標 GLint s_stride, //u方向控制點資料跨度 GLint t_stride, //v方向控制點資料跨度 GLfloat * ctlarray, //控制點數組指標 GLint sorder, //u方向上多項式階數 GLint torder, //v方向上多項式階數 GLenum type //確定求值器類型 ); |
另有一gluNurbsCurve()函數也可用於對NURBS曲線曲面的定義,具體使用方法與之類似。
光照和材質的渲染
為更真實地對實體進行三維建模,需要對已構造成型的自由型曲線曲面進行光照和材質上的渲染。OpenGL對此提供有一系列的用於建立光照模型的庫函數, 使使用者可以很方便地在三維情境中建立所需的光照模型。OpenGL中的光照模型由環境光線(Ambient Light)、漫射光(Diffuse Light)、鏡面反射光(Specular Light)等組成,同時還可設定光線衰減因子來類比真實的光源效果。下面這段代碼定義了一個黃色光源:
GLfloat Light_position[]={1.0f,1.0f,1.0f,0.0f,}; GLfloat Light_diffuse[]={1.0f,1.0f,0.0f,1.0f,}; glLightfv(GL_LIGHT0,GL_POSITION,Light_position); //定義光源位置 glLightfv(GL_LIGHT0,GL_DIFFUSE,Light_diffuse); //定義光源漫射光 glEnable(GL_AUTO_NORMAL); //自動產生曲面法向量 glEnable(GL_LIGHTING); //啟動光照模型; glEnable(GL_LIGHT0); //使光源GL_LIGHT0有效 |
其中glEnable(GL_AUTO_NORMAL)函數可以自動產生曲面法向量來對光照進行處理。OpenGL裡提供了 GL_LIGHT0~GL_LIGHT7八個光源,在這裡glEnable()使用的是GL_LIGHT0光源。雖然通過定義光源可以提高一定的真實度, 但對三維實體的材質定義更可大幅提高程 序所繪製曲面的逼真程度。OpenGL中所指的材質均指構成三維實體的材料在光照模型中對於R、G、B三原色的反射率。與光源的定義類似,材質的定義分環 境、漫射、 鏡面反射等成分,另外還有鏡面高光指數、輻射成分等等:
//設定材質的反射成分 GLfloat mat_ambient[]={0.8f,0.8f,0.8f,1.0f}; GLfloat mat_diffuse[]={0.8f,0.0f,0.0f,1.0f}; //紫色 GLfloat mat_specular[]={1.0f,0.0f,1.0f,1.0f}; //鏡面高光亮紫色 GLfloat mat_shininess[]={100.0f}; //高光指數 glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient); //定義環境光線反射率 glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); //定義漫射光反射率 glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular); //定義鏡面光反射率 glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess); //定義高光指數 |
OpenGL中材質的顏色與光照模型中光源的顏色含義是不同的。對於在光源中的定義,R、G、B分別表示三原色在光源中所佔的比率;而對於材質的定義, R、G、B則表示具有這種材質屬性的物體對於三原色的反射率,情境中物體所呈現的顏色是光照模型、材質定義共同作用的結果。
結論
本文通過OpenGL輔助庫提供的NURBS編程介面,在VC++下對自由型曲線曲面進行了繪製,並介紹了對其曲面的光照和材質的渲染方法。通過對本文 的理解可以通過OpenGL繪製自由型曲線曲面來更好的對三維實體進行建模,並可進一步感受到OpenGL在三維圖形電腦應用中的微妙之處。