这篇教程将介绍如果在openGL中使用CG(C for Graphic)语言。想在程序中使用CG,首先要下载并安装 NVIDIA的Cg Toolkit。( http://developer.nvida.com/CgTutorial ) 安装好后,在openGL程序中就可以添加cg.h和cgGL.h头文件了。要在openGl程序中使用Cg,这两个头文件必不可少。
- #include <Cg/cg.h>
- #include <Cg/cgGL.h>
添加了头文件后,再添加库文件cg.lib cgGL.lib。Cg shader程序必须要有包含在Context中,Context是装载shader的容器。所以首先要创建一个Context。
- CGcontext myCgContext = cgCreateContext();
上面这个语句就创建了一个名叫myCgContext的Context,不管是vertex shader还是fragment shader,在编译之前都放到这个Context里。可以通过cgCreateProgramFromFile函数创建一个Cg程序,然后将Context和这个程序连接起来。
- CGprogram myCgVertexProgram =
- cgCreateProgramFromFile(
- Context, /* Cg runtime context */
- CG_SOURCE, /* shader程序的源代码类型 */
- programString, /* shader源代码文件的名字 */
- Profile, /* Profile: OpenGL ARB vertex program */
- main, /* shader程序入口函数*/
- args); /* 附加参数,一般为NULL */
上面程序段中,CG_SOURCE和programString可以指定使用哪一种shader程序源代码,如果使用后缀名为.cg的文件作为shader的源代码文件,那么这两个参数分别设置为CG_SOURCE和shader源程序代码文件名即可。Cg也支持预编译好的目标文件作为shader程序的源文件,这时将这两个参数设置为CG_OBJECT和预编译好的shader程序文件名即可。Profile参数指定使用哪一种版本的profile来编译该shader程序,根据profile版本的不同,shader的能力有很大的差别。具体有哪些版本的profile,以及各个不同版本profile的详细情况,大家可以参考http://developer.nvida.com/CgTutorial。
创建好Context,且链接了shader程序后,在openGL中还要使用函数cgGLLoadProgram加载该shader程序。
- cgGLLoadProgram(CGprogram Program);
上面的语句在openGL中加载一个创建好的shader程序。参数Program指定该程序是vertex shade程序还是fragment shader程序。
以上的工作都是为了使用shader而做好了准备,下面就可以直接使用这些shader程序了。表现一个3D场景的时候,可能会使用很多不懂得物体。不同的物体可能有不同的物理属性,所以我们可能希望不同的shader程序可以应用到不同的物体上。Cg提供了这样的函数,函数cgGLBindProgram可以绑定一个shader程序到想要表现的物体上。绑定的时候只需要简单的将要表现的物体的渲染代码放到该函数语句后面即可。绑定后,再调用函数cgGLEnableProfile激活指定的profile来编译shader程序。最后,shader程序使用完毕后,调用cgGLDisableProfile函数关闭当前profile。
- cgGLBindProgram(Program);
- cgGLEnableProfile(Profile);
- // 渲染某物体
- cgGLDisableProfile(Profile);
上面的代码简单的展示了绑定,编译shader程序并且用该shader程序渲染一个物体。
现在给出一个完整的代码,在画面上显示一个圆环,通过vertex shader和 fragment shader来渲染它,在vertex shader里几乎没做任何事情,只是将该圆环的本地坐标转换到剪裁坐标,为光栅化做好准备。然后将每个顶点在本地坐标中的位置作为顶点的颜色输出传入fragment shader,在fragment shader中没有做任何工作,直接根据vertex shader中设定的顶点的颜色,通过插值计算各个像素的颜色并输出。
下面分别是HelloCG.cpp,vertex shader 01vs.cg和fragment shader 01fs.cg的代码。
HelloCG.cpp
- #include
- #include
- #include
- #include
- int ww = 640, hh = 480;
- void render();
- void reshape(int w, int h);
- static CGcontext myCgContext;
- static CGprofile myCgVertexProfile;
- static CGprofile myCgFragmentProfile;
- static CGprogram myCgVertexProgram;
- static CGprogram myCgFragmentProgram;
- static const char *myProgramName = "Hello CG";
- static const char *myVertexProgramFileName = "01vs.cg";
- static const char *myVertexProgramName = "vs_main";
- static const char *myFragmentProgramFileName = "01fs.cg";
- static const char *myFragmentProgramName = "fs_main";
- int main(int argc, char** argv)
- {
- glutInit(&argc, argv);
- glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
- glutInitWindowSize(ww,hh);
- glutCreateWindow(myProgramName);
- glutDisplayFunc(render);
- glutReshapeFunc(reshape);
- glEnable(GL_DEPTH_TEST);
- myCgContext = cgCreateContext();
- cgGLSetDebugMode(CG_FALSE);
- cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);
- myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
- cgGLSetOptimalOptions(myCgVertexProfile);
- myCgVertexProgram =
- cgCreateProgramFromFile(
- myCgContext, /* Cg runtime context */
- CG_SOURCE, /* Program in human-readable form */
- myVertexProgramFileName, /* Name of file containing program */
- myCgVertexProfile, /* Profile: OpenGL ARB vertex program */
- myVertexProgramName, /* Entry function name */
- NULL); /* No extra compiler options */
- cgGLLoadProgram(myCgVertexProgram);
- myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
- cgGLSetOptimalOptions(myCgFragmentProfile);
- myCgFragmentProgram =
- cgCreateProgramFromFile(
- myCgContext, /* Cg runtime context */
- CG_SOURCE, /* Program in human-readable form */
- myFragmentProgramFileName, /* Name of file containing program */
- myCgFragmentProfile, /* Profile: OpenGL ARB vertex program */
- myFragmentProgramName, /* Entry function name */
- NULL); /* No extra compiler options */
- cgGLLoadProgram(myCgFragmentProgram);
- glutMainLoop();
- return 0;
- }
- void reshape(int w, int h)
- {
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(45, (float)w/(float)h, 0.1, 100);
- glViewport(0,0,w,h);
- ww = w;
- hh = h;
- }
- void render()
- {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glClearColor(.0f, .0f, .2f, 1.0f);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- gluLookAt(.0,.0,5.0, .0,.0,.0, .0,1.0,.0);
- static float angle;
- glRotatef(angle, 0.0,1.0,0.0);
- cgGLBindProgram(myCgVertexProgram);
- cgGLEnableProfile(myCgVertexProfile);
- cgGLBindProgram(myCgFragmentProgram);
- cgGLEnableProfile(myCgFragmentProfile);
- //将ModelViewProjection矩阵传入shader
- CGparameter mvp = cgGetNamedParameter(myCgVertexProgram, "MVP");
- cgGLSetStateMatrixParameter(mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
- glutSolidTorus(0.3,1.0,30,30);
- cgGLDisableProfile(myCgVertexProfile);
- cgGLDisableProfile(myCgFragmentProfile);
- angle += 0.5;
- if(angle >=360) angle = 0.0f;
- glutSwapBuffers();
- glutPostRedisplay();
- }
01vs.cg
- struct output
- {
- float4 position : POSITION;
- float4 color : COLOR;
- };
- output vs_main( float4 position : POSITION,
- float4 color : COLOR,
- uniform float4x4 MVP
- )
- {
- output OUT;
- OUT.position = mul(MVP, position);
- OUT.color = position;
- return OUT;
- }
01fs.cg
- float4 fs_main( float4 color : COLOR ) : COLOR
- {
- return color;
- }
下面来解释一下这两个shader程序。在01vs.cg中,struct是Cg的关键字,和C语言是一样的,申明一个结构体。然后该shader的入口函数vs_main返回这样的一个结构体。在结构体中,申明了position和color,分别表示要在vertex shader中输出顶点的位置和颜色。要注意的是,这里的冒号是Cg的一个新语法,它表示“语义”。它紧跟在一个变量后,表示对该变量进行说明。大写字母POSITION和COLOR都是Cg的关键字。它们表示变量position和color分别作为顶点位置和颜色传入fragment shader。有了POSITION和COLOR的说明,在随后的渲染管线中,要用到顶点位置和颜色的时候就可以分别使用这两个变量。当然GPU也会自动将这两个变量作为顶点位置和颜色来使用。mul是Cg内置函数,表示矩阵的乘法,变量MVP是float4x4的类型,该类型表示一个4*4的矩阵。矩阵和向量的乘法就可以使用函数mul来实现。
同样地,在fragment shader 01fs.cg中,shader入口函数fs_main返回类型是一个有4维向量,该函数有个传入参数float4 color:COLOR,这里的语义COLOR就表示这个变量是从vertex shader里传来的要作为颜色使用的变量。在函数fs_main的后面也有个语义COLOR,它表示该shader程序返回的4维向量要作为颜色来是使用。
整个HelloCG程序很简单,如果在代码中有不懂得部分,特别是对为什么要传入ModelViewProjection矩阵等不明白的同学,那么说明你对图形管线的各个阶段还不了解,建议这些同学多看看图形学方面的书籍,也可以参考我的OpenGL图形管线和坐标变换这篇文章。