Cg编程入门编(OpenGL)
原注:B Alex D'Angelo (alexz@FusionIndustries.com)
译: 川流不息 (huawenguang@hotmail.com)未经本人同意,不能用于商业用途。
本文将介绍如何在你现有的图形程序中加入对顶点和片段的高级着色。采用CG着色语言,将会另你的工作更加容易实现。在编程过程中,你可以通过调用一些已有的CG函数来完成你的工作。当然,当你学得足够深入后,你也可以自已编写一些着色器。最具有参考价值的资料,当然要数Fernando 和 Kilgard 编写的“The Cg Tutorial”了。在Nvidia公司的网页中可以下载到。
一、概述
本文的程序中,结合OpenGL和GLUT在屏幂中画了一个立方体,程序中加入了CG着色程序代码。Listing1中包含了完整的原程序代码,其中CG部分用粗体字标识出来,本程序贯穿全文。Listing2是一个CG顶点着色的代码。
二、建立Cg编程环境
在进行CG编程之前,首先要下载CG工具包“Cg Toolkit”,在以下网址可以找到:
http://developer.nvidia.com/CG ,请注意看一下你的显卡是否支持顶点着色编程。
安装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"
我们的程序中虽要连接“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
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
和别的一些相关网站中,有许多着色代码提供给我们使用,同时也可我和他们一起分享你的经验。
相关档案:sf_2005102615296.doc (58368bytes)
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
作者:B Alex D'Angelo | 日期:2005-11-28