使用頂點(diǎn)和片斷腳本去做渲染工作可以得到額外的好處,最大的好處就是把CPU的一些工作交給了GPU,Cg提供了書寫這些強(qiáng)大的腳本的一種手段。
這篇教程有許多目的,第一向你展現(xiàn)了一個(gè)非常簡單的頂點(diǎn)腳本,第二向你說明如何在OpenGL中使用Cg編寫的腳本。
這個(gè)教程是基于最新的NeHeGL的基本代碼,為了獲得更多的信息,你可以訪問nVidia的官方網(wǎng)站(developer.nvidia.com),它會(huì)給你一個(gè)完整的答案。
注意:這個(gè)教程不是叫你如何去寫一個(gè)完整的Cg腳本,而是教你在OpenGL中載入并運(yùn)行腳本。
開始:
第一步,從nVidia的網(wǎng)站上下載Cg Compiler庫,最好去下載1.1版本的,因?yàn)閚vidia各個(gè)版本的變化很大,為了讓程序不出現(xiàn)任何問題,最好這樣做,因?yàn)槲覀冇玫氖?.1版本的。
下一步,包含編譯需要的頭文件和庫文件。
我已經(jīng)幫你把它們拷貝到了工程的文件夾里了。
Cg介紹
你必須有以下幾個(gè)概念:
1、頂點(diǎn)腳本會(huì)作用于你輸入的每一個(gè)頂點(diǎn),如果你想要作用于一些頂點(diǎn),那么你必須在作用前加載頂點(diǎn)腳本,并于作用后釋放頂點(diǎn)腳本。
2、頂點(diǎn)腳本輸出的結(jié)果被送入到片斷處理器中,你不用管這其中是如何實(shí)現(xiàn)的。
最后,記住頂點(diǎn)腳本在圖元裝配前被執(zhí)行,片斷腳本在光柵化后被執(zhí)行。
好了,現(xiàn)在我們創(chuàng)建一個(gè)空白的文件吧(保存為wave.cg),接著我們創(chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu),它被我們得腳本使用。下面的代碼被加入到wave.cg文件中。
struct appdata { float4 position : POSITION; float4 color : COLOR0; float3 wave : COLOR1;};
上面的結(jié)果說明,我們輸入的頂點(diǎn)包含一個(gè)位置坐標(biāo),一個(gè)顏色和我們自定義的波的顏色
下面的代碼定義一個(gè)輸出頂點(diǎn)的數(shù)據(jù),包括一個(gè)頂點(diǎn)和顏色
struct vfconn{ float4 HPos : POSITION; float4 Col0 : COLOR0;};
下面的代碼是Cg的主函數(shù),每個(gè)頂點(diǎn)都會(huì)被以下函數(shù)執(zhí)行:
vfconn main(appdata IN, uniform float4x4 ModelViewProj)
{
vfconn OUT; // 保存我們輸出頂點(diǎn)的數(shù)據(jù)
// 計(jì)算頂點(diǎn)y的坐標(biāo)
IN.position.y = ( sin(IN.wave.x + (IN.position.x / 5.0) ) + sin(IN.wave.x + (IN.position.z / 4.0) ) ) * 2.5f;
// 保存到輸出數(shù)據(jù)中
OUT.HPos = mul(ModelViewProj, IN.position);
// 不改變輸入的顏色
OUT.Col0.xyz = IN.color.xyz;
return OUT;
}
完成了上面的代碼,記得保存一下.
下面我們到了程序中,首先包含使用cg需要的頭文件,和庫文件
#include <cg\cg.h> #include <cg\cggl.h>
#pragma comment( lib, "cg.lib" ) #pragma comment( lib, "cggl.lib" )
下面我們定義一些全局變量,用來計(jì)算我們得網(wǎng)格和控制cg程序的開關(guān)
#define SIZE 64 // 定義網(wǎng)格的大小bool cg_enable = TRUE, sp; // 開關(guān)Cg程序GLfloat mesh[SIZE][SIZE][3]; // 保存我們的網(wǎng)格GLfloat wave_movement = 0.0f; // 記錄波動(dòng)的移動(dòng)
下面我們來定義一些cg相關(guān)的全局變量
CGcontext cgContext; // 用來保存cg腳本
我們需要的第一個(gè)變量是CGcontext,這個(gè)變量是多個(gè)Cg腳本的容器,一般來說,你獲得你可以用函數(shù)從這個(gè)容器中獲得你想要的腳本
接下來我們定義一個(gè)CGprogram變量,它用來保存我們得頂點(diǎn)腳本
CGprogram cgProgram; // 我們得頂點(diǎn)腳本
接下來我們需要一個(gè)變量來設(shè)置如何編譯這個(gè)頂點(diǎn)腳本
CGprofile cgVertexProfile; // 被頂點(diǎn)腳本使用
下面我們需要一些參數(shù)用來把Cg腳本使用的數(shù)據(jù)從程序中傳送過去。
CGparameter position, color, modelViewMatrix, wave; // 腳本中需要的參數(shù)
在初始化階段我們先要?jiǎng)?chuàng)建我們網(wǎng)格數(shù)據(jù)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
for (int x = 0; x < SIZE; x++)
{
for (int z = 0; z < SIZE; z++)
{
mesh[x][z][0] = (float) (SIZE / 2) - x;
mesh[x][z][1] = 0.0f;
mesh[x][z][2] = (float) (SIZE / 2) - z;
}
}
我們設(shè)置多邊形的現(xiàn)實(shí)模式為線框圖,接著遍歷沒有頂點(diǎn),設(shè)置其高度。
接下來,我們初始化Cg程序
// 設(shè)置Cg cgContext = cgCreateContext(); // 創(chuàng)建一個(gè)Cg容器
// 測試是否創(chuàng)建成功
if (cgContext == NULL)
{
MessageBox(NULL, "Failed To Create Cg Context", "Error", MB_OK);
return FALSE;
}
我們創(chuàng)建一個(gè)Cg程序的容器,并檢查它是否創(chuàng)建成功
cgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX); // 配置在OpenGL中使用頂點(diǎn)緩存
// 檢測Cg程序的是否創(chuàng)建成功
if (cgVertexProfile == CG_PROFILE_UNKNOWN)
{
MessageBox(NULL, "Invalid profile type", "Error", MB_OK);
return FALSE;
}
cgGLSetOptimalOptions(cgVertexProfile); // 啟用配置文件
如果你想使用片斷腳本,使用CG_GL_FRAGMENT變量。如果返回的變量為CG_PROFILE_UNKNOW表示沒有可用的配置文件,則不能編譯你需要的Cg程序。
// 從文件中載入Cg程序 cgProgram = cgCreateProgramFromFile(cgContext, CG_SOURCE, "CG/Wave.cg", cgVertexProfile, "main", 0);
// 檢測是否成功
if (cgProgram == NULL)
{
CGerror Error = cgGetError();
MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);
return FALSE;
}
我們嘗試從源文件中創(chuàng)建一個(gè)Cg程序,并返回編譯后的結(jié)果。
// 載入腳本 cgGLLoadProgram(cgProgram);
下面我們把頂點(diǎn)腳本載入到顯存,并準(zhǔn)備幫定給GPU。所有的腳本在使用前必須加載。
// 把數(shù)據(jù)變量地址發(fā)送給Cg程序 position = cgGetNamedParameter(cgProgram, "IN.position"); color = cgGetNamedParameter(cgProgram, "IN.color"); wave = cgGetNamedParameter(cgProgram, "IN.wave"); modelViewMatrix = cgGetNamedParameter(cgProgram, "ModelViewProj");
return TRUE;
在初始化的最后,我們必須告訴Cg腳本在那里去獲得輸入的數(shù)據(jù),我們需要把變量的指針傳遞過去,下面的函數(shù)完成了這個(gè)功能。
程序結(jié)束時(shí),記得釋放我們創(chuàng)建的內(nèi)容。
cgDestroyContext(cgContext);
下面的代碼使用空格切換是否使用Cg程序
if (g_keys->keyDown [' '] && !sp) { sp=TRUE; cg_enable=!cg_enable; }
if (!g_keys->keyDown [' ']) sp=FALSE;
現(xiàn)在我們已經(jīng)完成了所有的準(zhǔn)備工作了,到了我們實(shí)際繪制網(wǎng)格的地方了,按照慣例我們還是先設(shè)置我們得視口。
gluLookAt(0.0f, 25.0f, -45.0f, 0.0f, 0.0f, 0.0f, 0, 1, 0);
// 把當(dāng)前Cg程序的模型變化矩陣告訴當(dāng)前程序 cgGLSetStateMatrixParameter(modelViewMatrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
上面我們要做的事就是把當(dāng)前Cg程序的模型變化矩陣告訴當(dāng)前程序。
結(jié)下來如果使用cg程序,則把頂點(diǎn)的顏色設(shè)置為綠色
// 如果使用Cg程序 if (cg_enable) { // 使用頂點(diǎn)腳本配置文件 cgGLEnableProfile(cgVertexProfile); // 幫定到當(dāng)前的頂點(diǎn)腳本 cgGLBindProgram(cgProgram); // 設(shè)置繪制顏色 cgGLSetParameter4f(color, 0.5f, 1.0f, 0.5f, 1.0f); }
下面我們來繪制我們的網(wǎng)格。
// 開始繪制我們的網(wǎng)格 for (int x = 0; x < SIZE - 1; x++) { glBegin(GL_TRIANGLE_STRIP); for (int z = 0; z < SIZE - 1; z++) { // 設(shè)置Wave參數(shù) cgGLSetParameter3f(wave, wave_movement, 1.0f, 1.0f); //設(shè)置輸入的頂點(diǎn) glVertex3f(mesh[x][z][0], mesh[x][z][1], mesh[x][z][2]); glVertex3f(mesh[x+1][z][0], mesh[x+1][z][1], mesh[x+1][z][2]); wave_movement += 0.00001f; if (wave_movement > TWO_PI) wave_movement = 0.0f; } //經(jīng)過Cg程序處理,進(jìn)行繪制 glEnd(); }
上面的代碼完成具體的繪制操作,對于每一個(gè)頂點(diǎn),我們動(dòng)態(tài)的傳入波動(dòng)系數(shù)和輸入原始的頂點(diǎn)數(shù)據(jù)。在繪制開始前,頂點(diǎn)腳本接受所有的頂點(diǎn)數(shù)據(jù)并處理,接著進(jìn)行光柵華操作。
別忘了在繪制完成后,關(guān)閉我們啟用的頂點(diǎn)腳本,否則在繪制其它的模型時(shí)會(huì)讓你得到不想要的結(jié)果。
if (cg_enable) cgGLDisableProfile(cgVertexProfile); // 禁用頂點(diǎn)腳本配置文件
好了上面就是所有的內(nèi)容了,簡單吧.Cg就是這么簡單