1. 程式人生 > >Cg程式設計入門編(OpenGL)

Cg程式設計入門編(OpenGL)

原注:B Alex D'Angelo ([email protected]) 譯:  華文廣www.PhysDev.com)未經本人同意,不能用於商業出版用途。 本文將介紹如何在你現有的圖形程式中加入對頂點和片段的高階著色。採用CG著色語言,將會另你的工作更加容易實現。在程式設計過程中,你可以通過呼叫一些已有的CG函式來完成你的工作。當然,當你學得足夠深入後,你也可以自已編寫一些著色器。最具有參考價值的資料,當然要數Fernando 和 Kilgard 編寫的“The Cg Tutorial”了。在Nvidia公司的網頁中可以下載到。 一、概述 本文的程式中,結合OpenGL和GLUT在屏冪中畫了一個立方體,程式中加入了CG著色程式程式碼。Listing1中包含了完整的原程式程式碼,其中CG部分用粗體字標識出來,本程式貫穿全文。Listing2是一個CG頂點著色的程式碼。 二、建立Cg程式設計環境
在進行CG程式設計之前,首先要下載CG工具包“Cg Toolkit”,在以下網址可以找到: 安裝CG工具包,為了能讓它在Visual C++ 中工作,你可用以下兩種方法之一。第一種:把CG標頭檔案和庫檔案中的內容,分別拷貝到Visual C++的標頭檔案和庫檔案的資料夾中。 From: C:/Program Files/NVIDIA Corporation/Cg/lib To:   C:/Program Files/Microsoft Visual Studio/VC98/Lib From: C:/Program Files/NVIDIA Corporation/Cg/include To:   C:/Program Files/Microsoft Visual Studio/VC98/Include
第二種方法就是在VC編譯器中增加一個搜尋路徑:Tools ->Options -> projects ->"directiress" C:/Program Files/NVIDIA Corporation/Cg/include C:/Program Files/NVIDIA Corporation/Cg/lib 我們的程式中雖要連線“cg.lib”“cgGL.lib”這兩個庫檔案,你可以把它寫到連線路徑中去:“properties ->Linker -> Input”, 式者在程式開頭加入以下程式碼: #ifdef _MSC_VER #pragma comment( lib,
"cg.lib" ) #pragma comment( lib, "cgGL.lib" ) #endif 三、Cg程式設計細節 為了使用CG著色器,我們必須在程式中建立一個著色上下文(CGcontext),一個程式,只需一個著色上下文就可以了。 要為每個著色器建立一個CGprogram,而每個著色器你都可以為它指定一個單獨的顯示卡適配參(CGprofile),CGprofile是用來告訴CG如何選擇最適合你顯示卡的方式來對頂點或片段著色。 著色器可以在任何你雖要用到的地方被載入,載入的時候只需要給程式傳遞一個著色器函式入口名,或是著色器檔名。本文例子中,著色器在繪圖迴圈前被載入,並且,為了適應各種顯示卡的在求,我們為著色器指定了一個基礎profile ,"CG_PROFILE_VP20"。 1、設定著色器要用到的變數。 首先,把CG標頭檔案包含到你的程式中:     #include <Cg/cg.h> #include <Cg/cgGL.h> 接著,增加一些用來儲存著色器入口地址的變數: static CGcontext Context = NULL; static CGprogram VertexProgram = NULL; 以及一些著色引數的地址變數,在初始化著色器之後,這些地址變數將通過使用“CGparameters”與具體引數繫結在一起。 static CGparameter KdParam = NULL; static CGparameter ModelViewProjParam = NULL; static CGparameter VertexColorParam = NULL; 最後指定頂點著色用到的profile: static CGprofile VertexProfile = CG_PROFILE_VP20; 2、初始化CG 程式中,初始化OpenGL這後,接著要對CG進行初始化。 首先,要建立一個著色上下文,一個程式中有一個著色上下文(context)就可以了,所有要用到的著色器都將會共享這一個上下文。 Context = cgCreateContext(); 其次,通過給上下文指定著色器檔名以及著色器型別(這裡是頂點著色),把一個頂點著色器加到著色上下文中去。 VertexProgram = cgCreateProgramFromFile(Context,                                                                                              CG_SOURCE, "vertexShader.cg", VertexProfile, NULL, NULL); 只有在頂點著色器被成功建立之後,著色器的程式碼才被真正地載入到程式中來,與此同時,各種著色引數地址也最終與著色器中的引數繫結在一起。在這之後的程式中,你便可以自由地使用和改變這些引數的內容了。如例子中所示的:反射光顏色(kdParam),頂點顏色(vertexColorParam)和模型透視矩陣(modelViewProj),些變數都是可以改變的。 在繪圖主迴圈結束之後,所有著色器佔用的資源都要及時釋放,在CG中,提供了兩個函式:cgDestroyProgram();和cgDestroyContext(); 讓我們完成這一工作變得更簡單。 3、繪圖迴圈 進入繪圖迴圈之後,著色器必雖在實際繪圖這前呼叫,繪圖結束這後就應該被禁用。這個概念和glBegin(); 及 glEnd(); 的意思是相似的,實際上CG的初始工作是不能在 glBegin(); 和 glEnd(); 之間被呼叫的。 在所有繪圖工作開始之前,我們通過呼叫函式 cgGLBindProgram(); 把著色程式碼與OpenGL繪圖程式碼關聯起來,緊接著呼叫cgGLEnableProfile(); 函式來指定CG程式到底是進行頂點著色,還是片段著色。之後我們就可以利用cgGLSetParamter*();函式來傳遞或使用著色器中的引數了。 在所有繪圖工作完成之後,我們應該馬上呼叫cgGLDisableProfile(); 來停止著色器的使用。 著色器的引數是可以隨時改變的,例如:立方體的頂點顏色可以通過函式cgGLSetParameter3f(vertexColorParam,0.0,1.0,0.00);來設定。 既然我們知道了如何在繪圖迴圈中使用一個著色器,那麼擴充套件到在一個繪圖迴圈中使用多個著色器的情況,那也是很容易實現的,簡單地把著色繫結包在繪圖程式碼的外層就可以了。 例如: void draw() { cgGLBindProgram(program); cgGLEnableProfile(profile); drawing_code() cgGLDisableProfile(profile); cgGLBindProgram(program2); cgGLEnableProfile(profile2); drawing_code2() cgGLDisableProfile(profile2); } 四、結論 現在我們已經學會了如何把著色程式碼加入到我們現有的程式中。在 http://developer.nvidia.com/CG 和別的一些相關網站中,有許多著色程式碼提供給我們使用,同時也可我和他們一起分享你的經驗。 Listing 1 : HelloCg.c Using Cg with your programs. Cg specific calls are in bold. Heavily based on the runtime_ogl project in the Cg toolkit. /* Minimalist program for using shaders, with no error checking */ #ifdef _MSC_VER #pragma comment( lib, "cg.lib" ) #pragma comment( lib, "cgGL.lib" ) #endif #include <math.h> #include <stdio.h> #include <stdlib.h> #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> #endif #include <Cg/cg.h> #include <Cg/cgGL.h> /******************************************************************************/ /*** Static Data ***/ /******************************************************************************/ /* New Cg global variables */ static CGcontext Context = NULL; static CGprogram VertexProgram = NULL; static CGparameter KdParam = NULL; static CGparameter ModelViewProjParam = NULL; static CGparameter VertexColorParam = NULL; #ifdef __APPLE__ static CGprofile VertexProfile = CG_PROFILE_ARBVP1; #else static CGprofile VertexProfile = CG_PROFILE_VP20; #endif /* End new Cg global variables */ GLfloat CubeNormals[6][3] = { {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, -1.0} }; GLint CubeFaces[6][4] = { {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4}, {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} }; GLfloat CubeVertices[8][3]; /******************************************************************************/ static void DrawCube(void) { int i; cgGLBindProgram(VertexProgram); /* * Set various uniform parameters including the ModelViewProjection * matrix for transforming the incoming position into HPOS. */ if(KdParam != NULL) cgGLSetParameter4f(KdParam, 1.0, 1.0, 0.0, 1.0); /* Set the concatenate modelview and projection matrices */ if(ModelViewProjParam != NULL) cgGLSetStateMatrixParameter(ModelViewProjParam, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); cgGLEnableProfile(VertexProfile); /* * Create cube with per-vertex varying attributes */ for(i = 0; i < 6; i++) { glBegin(GL_QUADS); { glNormal3fv(&CubeNormals[i][0]); cgGLSetParameter3f(VertexColorParam, 1.0, 0.0, 0.0); glVertex3fv(&CubeVertices[CubeFaces[i][0]][0]); cgGLSetParameter3f(VertexColorParam, 0.0, 1.0, 0.0); glVertex3fv(&CubeVertices[CubeFaces[i][1]][0]); cgGLSetParameter3f(VertexColorParam, 0.0, 0.0, 1.0); glVertex3fv(&CubeVertices[CubeFaces[i][2]][0]); cgGLSetParameter3f(VertexColorParam, 1.0, 1.0, 1.0); glVertex3fv(&CubeVertices[CubeFaces[i][3]][0]); } glEnd(); } cgGLDisableProfile(VertexProfile); } static void Display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); DrawCube(); glutSwapBuffers(); } static void InitializeCube(GLfloat v[8][3]) { /* Setup cube vertex data. */ v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1; v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1; v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1; v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1; v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1; v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1; } static void InitializeGlut(int *argc, char *argv[]) { glutInit(argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutCreateWindow(argv[0]); glutDisplayFunc(Display); InitializeCube(CubeVertices); /* Use depth buffering for hidden surface elimination. */ glEnable(GL_DEPTH_TEST); /* Setup the view of the cube. */ glMatrixMode(GL_PROJECTION); gluPerspective( /* field of view in degree */ 40.0, /* aspect ratio */ 1.0, /* Z near */ 1.0, /* Z far */ 10.0); glMatrixMode(GL_MODELVIEW); gluLookAt(0.0, 0.0, 5.0, /* eye is at (0,0,5) */ 0.0, 0.0, 0.0, /* center is at (0,0,0) */ 0.0, 1.0, 0.); /* up is in positive Y direction */ /* Adjust cube position to be asthetic angle. */ glTranslatef(0.0, 0.0, -1.0); #if 1 glRotatef(60, 1.0, 0.0, 0.0); glRotatef(-20, 0.0, 0.0, 1.0); #endif } int main(int argc, char *argv[]) { InitializeGlut(&argc, argv); /* Create one context which all shaders will use */ Context = cgCreateContext(); /* Adds shader to the context */ VertexProgram = cgCreateProgramFromFile(Context, CG_SOURCE, "vertexShader.cg", VertexProfile, NULL, NULL); if(VertexProgram != NULL) { /* Vertex shader only needs to be loaded once */ cgGLLoadProgram(VertexProgram); /* Bind parameters to give access to variables in the shader */ KdParam = cgGetNamedParameter(VertexProgram, "Kd"); ModelViewProjParam = cgGetNamedParameter(VertexProgram, "ModelViewProj"); VertexColorParam = cgGetNamedParameter(VertexProgram, "IN.VertexColor"); } glutMainLoop(); cgDestroyProgram(VertexProgram); cgDestroyContext(Context); return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Listing 2 : vertexShader.cg Cg vertex shader code written. Heavily based on the runtime_ogl project in the Cg toolkit. struct appdata { float4 position : POSITION; float3 normal : NORMAL; float3 color : DIFFUSE; float3 VertexColor : SPECULAR; }; struct vfconn { float4 HPOS : POSITION; float4 COL0 : COLOR0; }; vfconn main(appdata IN, uniform float4 Kd, uniform float4x4 ModelViewProj) { vfconn OUT; OUT.HPOS = mul(ModelViewProj, IN.position); OUT.COL0.xyz = Kd.xyz * IN.VertexColor.xyz; OUT.COL0.w = 1.0; return OUT; } // main //www.PhysDev.com