NVIDIA Cg programs with Xcode on Mac
Problem Description
While trying to follow along with the NVIDIA CG Tutorial, I was not able to find a way to compile and run the files that were provided along with the CG Toolkit, which is available for download at NVIDIA’s website. This made the tutorial difficult to follow along with. I’m aware that Cg has been discontinued by NVIDIA, but Unity Game Engine is still using it for it’s shader programming. So I wanted to learn more about Cg in order write shaders for Unity.
Problem Source
The source of the problem is that neither the NVIDIA CG Tutorial nor the web describes properly how to install and setup CG toolkit so that one can compile and run CG programs using Xcode.
Problem Solution
In this blog, I will give a step by step description of the following:
- Installing CG Toolkit on the Mac.
- Finding the framework and tutorial example files installed together with CG Toolkit.
- Creating and setting up a Xcode project for CG programs.
- Compiling and running the CG Tutorial examples using Xcode.
Installing CG Toolkit on Mac
To install the CG Toolkit just follow this link. Download the latest release. Once the .pkg file is downloaded, launch it and follow the instructions to run the install script.
Location of Framework and Tutorial example files
Once the installation is complete you will need to remember two important locations containing CG files that you will need in order to follow along with the CG tutorials (it is kind of frustrating that the download doesn’t contain any information regarding where the important files are installed). The two locations are given below for the Mac:
- Location of CG Framework: /Library/Frameworks
- Location of Example files to follow along with the NVIDIA CG Tutorials: /Developer/NVIDIA/Cg/examples/OpenGL/basic
Creating and Setting up Xcode project
- Start Xcode and create a Command Line Tool Project.
- Choose C as the Language, an arbitary location with an arbitary project name.
- Delete the main.c file from the project. Now the project should be empty.
- Next, select project in the Project Navigation window and choose the single available target.
- Go to Build phases and add Cocao Framework and Cg Framework under Link Binary with Libraries. The Cg Framework can be found at the location provided in the section above.
- Next, navigate to the location of the example files (the location of the example files is provided in the section above). I chose to import the 06_vertex_twisting project files because I only need to import 3 files. One file is the C OpenGL code, the other two are vertex and fragment functions written in .cg files (If you choose to import other projects, double check that you know which are all the required files). Select all the required files and drag them to the Project Navigation window of Xcode.
- Once the required files have been dragged to the Xcode Project Navigation window, you will be presented with a dialog. Check the Copy items if needed checkbox and make sure that the checkbox for Add to targets is checked.
- Once the files have been imported your Project Navigator window should look something like the screenshot below.
- Now select the .c file so that the code is revealed. Try now to build the program. The first error you will get is regarding GL/glew.h. For Mac, you need to make some changes to the code, in order to be able to compile it for Mac. The necessary changes are marked with a comment starting with CHANGE (in capital letters) in the listing below of the 06_vertex_twisting.c file.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300/* 06_vertex_twisting.c - OpenGL-based example using a Cgvertex and a Cg fragment programs from Chapter 3 of "The Cg Tutorial"(Addison-Wesley, ISBN 0321194969). *//* Requires the OpenGL Utility Toolkit (GLUT) and Cg runtime (version1.0 or higher). */#include <stdio.h> /* for printf and NULL */#include <stdlib.h> /* for exit */#include <math.h> /* for sin and cos *//* CHANGED: Added a new #ifdef */#ifdef __APPLE__#include <OpenGL/gl3.h>#else#include <GL/glew.h>#endif/* END OF CHANGED: Added a new #ifdef */#ifdef __APPLE__#include <GLUT/glut.h>#else#include <GL/glut.h>#endif#ifdef _WIN32#include <GL/wglew.h>#else#ifdef __APPLE__#include <OpenGL/OpenGL.h>#else#include <GL/glxew.h>#endif#endif#include <Cg/cg.h> /* Can't include this? Is Cg Toolkit installed! */#include <Cg/cgGL.h>static CGcontext myCgContext;static CGprofile myCgVertexProfile,myCgFragmentProfile;static CGprogram myCgVertexProgram,myCgFragmentProgram;static CGparameter myCgVertexParam_twisting;static const char *myProgramName = "06_vertex_twisting",*myVertexProgramFileName = "C3E4v_twist.cg",/* Page 79 */ *myVertexProgramName = "C3E4v_twist",*myFragmentProgramFileName = "C2E2f_passthru.cg",/* Page 53 */ *myFragmentProgramName = "C2E2f_passthru";static float myTwisting = 2.9, /* Twisting angle in radians. */myTwistDirection = 0.1; /* Animation delta for twist. */static void checkForCgError(const char *situation){CGerror error;const char *string = cgGetLastErrorString(&error);if (error != CG_NO_ERROR) {printf("%s: %s: %s\n",myProgramName, situation, string);if (error == CG_COMPILER_ERROR) {printf("%s\n", cgGetLastListing(myCgContext));}exit(1);}}/* Forward declared GLUT callbacks registered by main. */static void display(void);static void keyboard(unsigned char c, int x, int y);static void menu(int item);static void requestSynchronizedSwapBuffers(void);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);/* CHANGED: Commented out the code */// /* Initialize OpenGL entry points. */// if (glewInit()!=GLEW_OK || !GLEW_VERSION_1_1) {// fprintf(stderr, "%s: failed to initialize GLEW, OpenGL 1.1 required.\n", myProgramName);// exit(1);// }/* END OF CHANGED: Commented out the code */requestSynchronizedSwapBuffers();glClearColor(1, 1, 1, 1); /* White background */myCgContext = cgCreateContext();checkForCgError("creating context");cgGLSetDebugMode(CG_FALSE);cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);cgGLSetOptimalOptions(myCgVertexProfile);checkForCgError("selecting vertex profile");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 */checkForCgError("creating vertex program from file");cgGLLoadProgram(myCgVertexProgram);checkForCgError("loading vertex program");myCgVertexParam_twisting =cgGetNamedParameter(myCgVertexProgram, "twisting");checkForCgError("could not get twisting parameter");myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);cgGLSetOptimalOptions(myCgFragmentProfile);checkForCgError("selecting fragment profile");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 */checkForCgError("creating fragment program from file");cgGLLoadProgram(myCgFragmentProgram);checkForCgError("loading fragment program");/* No uniform fragment program parameters expected. */glutCreateMenu(menu);glutAddMenuEntry("[ ] Animate", ' ');glutAddMenuEntry("[w] Wireframe", 'w');glutAttachMenu(GLUT_RIGHT_BUTTON);glutMainLoop();return 0;}/* Apply an inefficient but simple-to-implement subdivision scheme for a triangle. */static void triangleDivide(int depth,const float a[2], const float b[2], const float c[2],const float ca[3], const float cb[3], const float cc[3]){if (depth == 0) {glColor3fv(ca);glVertex2fv(a);glColor3fv(cb);glVertex2fv(b);glColor3fv(cc);glVertex2fv(c);} else {const float d[2] = { (a[0]+b[0])/2, (a[1]+b[1])/2 },e[2] = { (b[0]+c[0])/2, (b[1]+c[1])/2 },f[2] = { (c[0]+a[0])/2, (c[1]+a[1])/2 };const float cd[3] = { (ca[0]+cb[0])/2, (ca[1]+cb[1])/2, (ca[2]+cb[2])/2 },ce[3] = { (cb[0]+cc[0])/2, (cb[1]+cc[1])/2, (cb[2]+cc[2])/2 },cf[3] = { (cc[0]+ca[0])/2, (cc[1]+ca[1])/2, (cc[2]+ca[2])/2 };depth -= 1;triangleDivide(depth, a, d, f, ca, cd, cf);triangleDivide(depth, d, b, e, cd, cb, ce);triangleDivide(depth, f, e, c, cf, ce, cc);triangleDivide(depth, d, e, f, cd, ce, cf);}}/* Large vertex displacements such as are possible with C3E4v_twistrequire a high degree of tessellation. This routine draws atriangle recursively subdivided to provide sufficient tessellation. */static void drawSubDividedTriangle(int subdivisions){const float a[2] = { -0.8, 0.8 },b[2] = { 0.8, 0.8 },c[2] = { 0.0, -0.8 },ca[3] = { 0, 0, 1 },cb[3] = { 0, 0, 1 },cc[3] = { 0.7, 0.7, 1 };glBegin(GL_TRIANGLES);triangleDivide(subdivisions, a, b, c, ca, cb, cc);glEnd();}static void display(void){glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);cgSetParameter1f(myCgVertexParam_twisting, myTwisting);cgGLBindProgram(myCgVertexProgram);checkForCgError("binding vertex program");cgGLEnableProfile(myCgVertexProfile);checkForCgError("enabling vertex profile");cgGLBindProgram(myCgFragmentProgram);checkForCgError("binding fragment program");cgGLEnableProfile(myCgFragmentProfile);checkForCgError("enabling fragment profile");drawSubDividedTriangle(5);cgGLDisableProfile(myCgVertexProfile);checkForCgError("disabling vertex profile");cgGLDisableProfile(myCgFragmentProfile);checkForCgError("disabling fragment profile");glutSwapBuffers();}static void idle(void){if (myTwisting > 3) {myTwistDirection = -0.05;} else if (myTwisting < -3) {myTwistDirection = 0.05;}myTwisting += myTwistDirection;glutPostRedisplay();}static void keyboard(unsigned char c, int x, int y){static int animating = 0,wireframe = 0;switch (c) {case ' ':animating = !animating; /* Toggle */if (animating) {glutIdleFunc(idle);} else {glutIdleFunc(NULL);}break;case 'w':case 'W':wireframe = !wireframe;if (wireframe) {glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);} else {glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);}glutPostRedisplay();break;case 27: /* Esc key *//* Demonstrate proper deallocation of Cg runtime data structures.Not strictly necessary if we are simply going to exit. */cgDestroyProgram(myCgVertexProgram);cgDestroyProgram(myCgFragmentProgram);cgDestroyContext(myCgContext);exit(0);break;}}static void menu(int item){/* Pass menu item character code to keyboard callback. */keyboard((unsigned char)item, 0, 0);}/* Platform-specific code to request synchronized buffer swaps. */static void requestSynchronizedSwapBuffers(void){#if defined(__APPLE__)#ifdef CGL_VERSION_1_2const GLint sync = 1;#elseconst long sync = 1;#endifCGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &sync);#elif defined(_WIN32)if (wglSwapIntervalEXT) {wglSwapIntervalEXT(1);}#elseif (glXSwapIntervalSGI) {glXSwapIntervalSGI(1);}#endif}
- After making the necessary changes as described in the previous step. You can now try to build the project again. This time, the project builds successfully with only some warnings about glut functions being deprecated. You can safely ignore these warnings, if the purpose is only to be able to run the example projects. Of course if you are an expert in OpenGL programming you can most probably make the warnings go away. I’m not at all accustomed to OpenGL, so I just chose to ignore the warnings.
- Before running the program, you need to take one more step. If you run the program now, you will get an error message in the console window of Xcode saying, The file could not be read. This error occurs because the c program is not able to find the .cg files. If you have a look at lines 49 and 51 of the source code provided above, you will notice that we are providing the program with cg file names. Either you need to give the absolute path to the files on line 49 and 51 or you make use of a better solution i.e. to let Xcode bundle .cg files with the executable. We go back to our notorious Build Phases again. Under Build Phases, you will find a section called Copy Files. Add the .Cg files to this section and set it up according to the screenshot below.
- Now you are ready to rebuild your program and run it. Voila! Now you should be presented with a window similar to the one near the heading of this post.
Note that if you still get an error,”The file could not be read”. Then make sure that you rebuild your program before running it.
Please leave a comment and share the post if you found it useful! Thanks!