转载自:http://blog.csdn.net/zhangci226/article/details/5540153
OpenGL CG 系列教程1 - Hello CG
效果图先上:
注:/为自己在原作者的基础上添加的注释
这篇教程将介绍如果在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 <gl/glut.h> #include <cg/cg.h> #include <Cg/cgGL.h> #include <stdio.h>
int ww = 640, hh = 480;
void render(); void reshape(int w, int h);
static CGcontext myCgContext; /用来保存所创建的额Cg环境 static CGprofile myCgVertexProfile; /用来保存顶点着色器的编译参数profile static CGprofile myCgFragmentProfile; static CGprogram myCgVertexProgram;/用来保存Cg所创建的顶点着色程序 static CGprogram myCgFragmentProgram;
static const char *myProgramName = "Hello CG"; static const char *myVertexProgramFileName = "01vs.cg"; /顶点着色程序.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(); / 创建一个Context,相当于创建一个Cg环境 / Cg runtime在运行过程中会调用glGetError, cgGLSetDebugMode用来设置应用程序是否开启opengl的错误检查 cgGLSetDebugMode(CG_FALSE);/此处设置不开启错误检查 cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);
myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX); /返回一个支持当前GPU顶点着色器的最佳的profile /指定顶点着色器的编译器参数profile,指定完后之后的顶点着色器的profile皆为所指定的固定参数,直到Cg环境被消除 cgGLSetOptimalOptions(myCgVertexProfile);/此处这句省略也可以,因为后面创建Cg程序有指定profile /创建一个Cg顶点着色程序,然后将所创建顶点着色程序和所创建的Cg环境Context连接起来 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); /将顶点着色程序加载到所创建的Cg环境中去
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) { glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, (float)w/(float)h, 0.1, 100);
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); /绑定一个shader程序到想要表现的物体上 cgGLEnableProfile(myCgVertexProfile); /激活指定的profile来编译shader程序
cgGLBindProgram(myCgFragmentProgram); cgGLEnableProfile(myCgFragmentProfile);
//将ModelViewProjection矩阵传入shader CGparameter mvp = cgGetNamedParameter(myCgVertexProgram, "MVP"); //将当前的模型视图矩阵告诉给应用程序,赋值给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图形管线和坐标变换这篇文章。
个人补充:
myCgVertexProgram = cgCreateProgramFromFile()
原因:文件格式出现问题,在写Cg着色程序时,要求所写的Cg着色程序要以.cg的格式存储,而写cg程序中是在txt文本文档中写,最终命名为01vs.cg,01fs.cg时
实际上文件名是01vs.cg.txt,01fs.cg.txt,文本文档默认缺省了后缀名.txt,所以自己错误的以为自己所写的是.cg的文件,实际上却是文本文件,导致创建Cg程序时出错。
解决方法:利用文本文档写Cg程序后,点击另存为--->文件保存类型选择所有文件,再命名xx.cg文件
(当时运行只单纯出现蓝色背景,没有旋转的圆环,被这文件名给纠结了一段时间)
二:要在opengl中使用Cg着色语言,首先要在VS工程中先配置Cg环境(同配置opengl环境类似),在 http://developer.nvida.com/CgTutorial后,将其解压出来
(1)将其动态链接库.dll文件添加到C:\Windows\System32目录下(32位机情况下)
(2)将其静态链接库添.lib文件t添加到安装目录下VC\lib中去(我的安装目录是在D:\VS2008\ANZHUANF,故将其添加到D:\VS2008\ANZHUANF\VC\lib)
(3)将其对应的.h头文件添加到安装目录下的VC\include中去(我是在include目录下新建个Cg文件夹,将头文件都放这里了,D:\VS2008\ANZHUANF\VC\include)
(4)在VS软件下的工程项目名右击属性--->链接器-->输入-->附加依赖项中添加Cg的链接库cg.lib cgGL.lib(注意彼此间用空格隔开(VS08),在VS10彼此间好像用;隔开)
当然第四步如果不想这么做也可以这样在程序开头加上下面的代码: