【GPU编程】The Cg Runtime:OpenGL中调用Cg程序

本文深入介绍了CgRuntime的原理和应用,涵盖了Cg程序的编译、加载、执行流程,以及参数管理和错误处理等内容。通过具体示例展示了如何在OpenGL环境中使用CgRuntime进行实时渲染。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:《The Cg Tutorial》(Cg手册):Appendix B:The Cg Runtime,Page195

很多地方可能翻译得不好,还请大家见谅、指教!

注意:我省略了其中的Direct3D的部分,因为本人现在正使用OpenGL开发。


B.1 什么是Cg Runtime?

Cg程序支持运行在GPU上的程序,但是它们需要应用程序的支持才能渲染图像。

为了把Cg程序和应用程序链接起来,你必须做两件事情:

(1)把程序编译为不同的版本。这一步把你的Cg程序转化成与应用程序的3D编程接口和硬件兼容的形式。

(2)把程序和应用程序链接起来。这一步使得应用程序能够配置程序来执行,并且能够赋予不同的以及全局变量参数。

你可以在任何想使用这些操作的时候来选择它。你可以在编译时刻使用它们,此时应用程序已经被编译成可执行的形式,或者是在运行时刻使用它们,此时应用程序已经被执行。Cg Runtime是这样一些应用程序编写接口的集合:它们使得一个应用程序能够在运行时刻编译和链接Cg程序。


B.2 为什么要使用Cg Runtime?

B.2.1 面向未来

大多数应用程序需要在不同性能的GPU上运行,所以这些应用程序需要在不同的profile(一个Cg profile是被特定图形硬件和API所支持的Cg语言子集)上运行。如果一个应用程序在编译时刻预编译了它的Cg程序,它必须为每个程序保存适用于每个profile的预编译版本。对于一个使用了很多Cg程序的应用程序来说十分累赘。更糟糕的是,这些Cg程序在运行时刻不能灵活更改。通过预编译的方法,一个应用程序牺牲了未来的编译器可能提供的优化功能。

B.2.2 去除依赖性的问题

如果你把已经已经编译的Cg程序和一个应用程序链接起来,应用程序就和编译结果绑定了,尤其是和编译器如何分配参数有关。应用程序不得不通过使用Cg编译器输出的硬件寄存器名来参照Cg程序输入参数。

1. 在Cg程序中,不查看编译器输出结果寄存器名字难以和Cg程序中的相关有意义的名字关联起来。

2. 每当Cg程序、Cg编译器或者编译的profile改变时寄存器分配都可能发生变化。这就意味着你不得不每次更新应用程序,这是很不方便的。

B.2.3 输入参数管理

Cg runtime也提供了管理Cg程序输入参数的功能。特别的,它使得处理比如数组和矩阵这样的数据类型更加容易。

这些传统的函数也包括了那些必要的3D API调用,它们能够使得代码更短,减少程序员犯错误的次数。


B.3 Cg Runtime是如何工作的?

下图显示了组成Cg runtime API的三个库:


(1)一个函数和结构的核心集封装了runtime的独立于3D API的功能。

(2)建立于核心集之上的面向OpenGL的函数集

(3)建立于核心集之上的面向Direct3D的函数集

为了使应用程序编写者更加方便的编写程序,OpenGL和Direct3D库都能够采用他们各自API的规范和数据结构类型。你只需要把你的应用程序所使用的3D API和针对特定3D API的Cg runtime库链接起来。这样,大多数应用程序能够使用OpenGL或者Direct3D Cg runtime库。

B.3.1 头文件

下面显示了如何在你的C或者C++程序中包含核心Cg runtime API:

#include <Cg/cg.h>

下面显示了如何包含针对OpenGL的Cg runtime API:

#include <Cg/cgGL.h>

B.3.2 创建上下文

一个上下文是用来存放Cg程序的容器。它存放你加载的Cg程序以及他们共享的数据。

下面显示了如何创建上下文:

CGcontext context = cgCreateContext();

B.3.3 编译程序

使用cgCreateProgram函数通过把它添加到一个上下文中来编译一个Cg程序:

CGprogram program = 
cgCreateProgramFromFile(
						context,
						CG_SOURCE,
						programString,
						profile,
						"main",
						args
						);

参数CG_SOURCE表明接下来的参数programString是包含了Cg源代码的字节,而不是预编译代码。Cg runtime也允许你通过使用CG_OBJECT而不是CG_SOURCE来从已经编译的代码(也称目标代码)创建一个程序。

profile指明了这个程序将要编译成的profile。

"main"指明了你的程序的入口函数名。

args是这样的一个字符串,它提供选项给编译器。

B.3.4 加载程序

 在你编译完程序之后,你需要将结果目标代码传递给你使用的3D API。这时,你需要调用Cg runtime的特定的3D API函数。

在OpenGL中,你可以这样加载一个程序:

cgGLLoadProgram(program);

B.3.5 更改程序参数

runtime允许你更改你的程序的参数值。第一步是获得参数的句柄。

CGparameter myParameter = cgGetNamedParameter(program,"myParameter");

myParameter是出现在程序源代码中的参数名。

第二步就是设置参数的值,所需使用的函数取决于参数类型。

比如OpenGL中的一个例子:

cgGLSetParameter4fv(myParameter,value);

这个函数把值value(一个包含4个浮点类型的数组)赋给参数myParameter。

B.3.6 执行程序

在你执行一个程序之前,你必须使其相关的profile可用。比如:

cgGLEnableProfile(CG_PROFILE_ARBFP1);

接下来。你必须把程序与当前3D API状态绑定起来。这就意味着它将要在接下来的每个顶点(假设是顶点程序)以及每个片段(假设是片段程序)的绘制调用中执行。

下面显示了在OpenGL中如何绑定一个程序:

cgGLBindProgram(program);

在同一时刻你只能为特定的profile绑定一个顶点及片段程序。因此,只有别的顶点程序没有被绑定时,这个顶点程序才会被执行。对于片段程序也是一样。

在OpenGL中,禁用profile使用以下调用:

cgGLDisableProfile(CG_PROFILE_ARBFP1);

B.3.7 释放资源

如果你的应用程序不再需要Cg程序,那么释放由Cg runtime为程序维护的资源是一个好的编程习惯。

释放为单一程序分配的资源,使用下列函数调用:

cgDestroyProgram(program);

释放为一个上下文所分配的所以资源,使用下列函数调用:

cgDestroyContext(context);

注意:destroy一个上下文意味着释放了它包含的所以程序。

B.3.8 错误捕获

核心Cg runtime通过设置一个包含错误码的全局变量来报告错误。你能够以下列方式查询它以及相关错误字符串:

CGerror error = cgGetError();
const char *errorString = cgGetErrorString(error);

每次一个错误发生时,核心库会可选的调用一个由应用程序提供的回调函数。这个回调函数经常会调用cgGetError:
调用特定3D API也可能会产生关于特定API的错误。对于OpenGL Cg runtime库,使用glGetError来检测。

以这种方式,很多有用的信息都会被输出到调试输出控制台上。

void MyErrorCallbask()
{
	const char* errorString = cgGetErrorString(cgGetError());
	printf(logfile,"Cg error:%s",errorString);
}
cgSetErrorCallback(MyErrorCallbask);

最后粘贴一个实例:

C3E1v_green.cg:

struct C3E1v_Output{
	float4 position:POSITION;
	float3 color:COLOR;
};

C3E1v_Output C3E1v_green(float2 position:POSITION,
											uniform float3 constantColor)
{
	C3E1v_Output OUT;
	OUT.position = float4(position,0,1);
	OUT.color = constantColor;
	return OUT;
}
C2E2f_passthru.cg:
struct C2E2f_Output{
	float4 color:COLOR;
};

C2E2f_Output C2E2f_passthru(float4 color:COLOR)
{
	C2E2f_Output OUT;
	OUT.color = color;
	return OUT;
}
02_vertex_and_fragment_program.cpp:
#pragma comment( lib, "cg.lib" )
#pragma comment( lib, "cgGL.lib" )

#include <iostream>
#include <stdlib.h>
#include <cmath>

#define GLUT_DISABLE_ATEXIT_HACK
#include <GL/glut.h>

#include <Cg/cg.h>
#include <Cg/cgGL.h>

using namespace std;

static CGcontext myCgContext;
static CGprofile myCgVertexProfile;
static CGprofile myCgFragmenrProfile;
static CGprogram myCgVertexProgram;
static CGprogram myCgfragmentProgram;
static const char *myProgramName = "02_vertex_and_fragment_program",
						*myVertexProgramFileName = "C3E1v_green.cg",
						*myVertexProgramName = "C3E1v_green",
						*myFragmenrProgramFileName = "C2E2f_passthru.cg",
						*myFragmentProgramName = "C2E2f_passthru";

static CGparameter myCgVertexParam_constantColor;

static void checkForError(const char* situation)
{
	CGerror error;
	const char *string = cgGetLastErrorString(&error);
	if (error != CG_NO_ERROR)
	{
		cout<<myProgramName<<":"<<situation<<":"<<string<<endl;
		if (error == CG_COMPILER_ERROR)
		{
			cout<<cgGetLastListing(myCgContext)<<endl;
		}
		exit(1);
	}
}

static void display();
static void keyboard(unsigned char c,int x,int y);

int main(int argc,char** argv)
{
	glutInitWindowSize(400,400);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInit(&argc,argv);

	glutCreateWindow(myProgramName);
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);

	glClearColor(0.1,0.3,0.6,0.0);

	myCgContext = cgCreateContext();
	checkForError("creating context");
	cgGLSetDebugMode(CG_FALSE);
	cgSetParameterSettingMode(myCgContext,CG_DEFERRED_PARAMETER_SETTING);

	myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(myCgVertexProfile);
	checkForError("selecting vertex profile");

	myCgVertexProgram = cgCreateProgramFromFile(
		myCgContext,
		CG_SOURCE,
		myVertexProgramFileName,
		myCgVertexProfile,
		myVertexProgramName,
		NULL
		);
	checkForError("creating vertex program from file");
	cgGLLoadProgram(myCgVertexProgram);
	checkForError("loading vertex program");

	myCgVertexParam_constantColor = cgGetNamedParameter(
		myCgVertexProgram,"constantColor");
	checkForError("could not get constantColor parameter");

	myCgFragmenrProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(myCgFragmenrProfile);
	checkForError("selecting fragment profile");

	myCgfragmentProgram = cgCreateProgramFromFile(
		myCgContext,
		CG_SOURCE,
		myFragmenrProgramFileName,
		myCgFragmenrProfile,
		myFragmentProgramName,
		NULL
		);
	checkForError("creating fragment program from file");
	cgGLLoadProgram(myCgfragmentProgram);
	checkForError("loading fragment program");

	glutMainLoop();
	return 0;
}

static void drawStar(float x, float y,
					 int starPoints, float R, float r)
{
	int i;
	double piOverStarPoints = 3.14159 / starPoints,
		angle = 0.0;

	cgUpdateProgramParameters(myCgVertexProgram);

	glBegin(GL_TRIANGLE_FAN);
	glVertex2f(x, y);  /* Center of star */
	/* Emit exterior vertices for star's points. */
	for (i=0; i<starPoints; i++) {
		glVertex2f(x + R*cos(angle), y + R*sin(angle));
		angle += piOverStarPoints;
		glVertex2f(x + r*cos(angle), y + r*sin(angle));
		angle += piOverStarPoints;
	}
	/* End by repeating first exterior vertex of star. */
	angle = 0;
	glVertex2f(x + R*cos(angle), y + R*sin(angle));
	glEnd();
}

static void drawStars(void)
{
	/*                     star    outer   inner  */
	/*        x      y     Points  radius  radius */
	/*       =====  =====  ======  ======  ====== */
	const float green[3] = {0.2,0.8,0.3};
	cgSetParameter3fv(myCgVertexParam_constantColor,green);

	drawStar(-0.1,   0,    5,      0.5,    0.2);
	drawStar(-0.84,  0.1,  5,      0.3,    0.12);
	drawStar( 0.92, -0.5,  5,      0.25,   0.11);

	cgSetParameter3f(myCgVertexParam_constantColor,
		0.7,0.1,0.1);

	drawStar( 0.3,   0.97, 5,      0.3,    0.1);
	drawStar( 0.94,  0.3,  5,      0.5,    0.2);
	drawStar(-0.97, -0.8,  5,      0.6,    0.2);
}

static void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	cgGLBindProgram(myCgVertexProgram);
	checkForError("Binding vertex program");

	cgGLEnableProfile(myCgVertexProfile);
	checkForError("enabling vertex profile");

	cgGLBindProgram(myCgfragmentProgram);
	checkForError("Binding fragment program");

	cgGLEnableProfile(myCgFragmenrProfile);
	checkForError("enabling fragment profile");

	drawStars();

	cgGLDisableProfile(myCgVertexProfile);
	checkForError("disabling vertex profile");

	cgGLDisableProfile(myCgFragmenrProfile);
	checkForError("disabling fragment profile");

	glutSwapBuffers();
}

static void keyboard(unsigned char c,int x,int y)
{
	switch(c)
	{
		case 27:
			cgDestroyProgram(myCgVertexProgram);
			cgDestroyProgram(myCgfragmentProgram);
			cgDestroyContext(myCgContext);
			exit(0);
			break;
	}
}
运行结果:

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值