Skip navigation

Tag Archives: opengl

The weirdest shit. You have to ask OpenGL to normalize byte colors. So the correct call to glVertexAttribPointer would be:

glVertexAttribPointer( attribNo, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, squareColors);

I spent like 20 minutes looking for what the problem could be with my code when I tried to switch float colors to byte colors, all my colors were white. And it was just this `normalized` flag.

Man, what a …

I can’t believe it. I spent about an hour debugging this problem and it wasn’t even a problem..

OpenGL textures are addressed upside down from d3d ones.

Direct3d texture address mode, notice (0,0) in top left

OpenGL texture address mode. Notice (0,0) in bottom left

Fog as a cheap depth cue

I’m trying to build something cool here. not telling what is, but you can guess from the screens, I suppose ;).

So I needed to make sure my frustum cull was working right. I could never tell which objects were further away or closer in orthographic mode (naturally), so I wanted a solution for this.

Yesterday and the day before I discovered Bryce and I was following some really good tutorials on it. It was very painful to go through ALL THAT UI stuff in one day, but I did it.

So like there’s a depth cue in Bryce which is just simple fog. DUH!!!! Fog makes things that are further away appear grayer. (Well bryce actually fades it out so you can’t see it anymore).

I like to be able to see everything all the time, so I just left the background default corny-blue and made the fog color gray. The spheres are supposed to be black, frustum lines yellow.

fog_is_a_very_nice_depth_cue

It really helps!

Understanding vertex shaders

Absolutely minimal CG program for good fundamentals understanding

First, make sure you install CG!

// FILE:  AbsoluteMinimalCGProgram.cpp
//////////////////////////////////////////
// ABSOLUTE MINIMAL CG PROGRAM:         //
// BASIC DRAWING IN 2D USING CG         //
//                                      //
// This example discusses the absolute  //
// basics of using CG with OpenGL.      //
//                                      //
// As a prerequisite, you should        //
// already have experience with         //
// OpenGL.  If ya don't. . . I suggest  //
// you get some practice, before        //
// attempting to continue with this     //
// series.                              //
//                                      //
// You found this at bobobobo's weblog, //
// https://bobobobo.wordpress.com        //
//                                      //
// Creation date:  Jan 21/08            //
// Last modified:  Jan 23/08            //
//                                      //
//////////////////////////////////////////

// Loosely based on Chapter 2 of "The Cg Tutorial" by Kilgard and Fernando,
// and the associated code files.

// The original Cg Tutorial code files are freely available for download at
// http://developer.nvidia.com/object/cg_tutorial_home.html

////////////////////////////////////////////////////////////////////
// To use this, get and install the Cg Toolkit and make sure you  //
// get the examples that come with the Cg Toolkit to work first.  //
////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>

// If having problems compiling, ('fatal error') see
// https://bobobobo.wordpress.com/2008/01/22/setting-up-cg-environment-variables/
#include <Cg/cg.h>
#include <Cg/cgGL.h>

// These link the Cg libraries.
#pragma comment( lib, "cg.lib" )
#pragma comment( lib, "cgGL.lib" )


// Ok, this package has 2 files:
// AbsoluteMinimalCGProgram.CPP:  The C++ code file.
// vertexShader.CG:               The vertex shader file.
// Take a real quick glance at both, up and down.

//  ________   _____
// |___  ___| |  _  |
//    |  |    | | | |
//    |  |    | |_| |
//    |__|    |_____| do.
//
// 1)  One of the main ideas of this package is to compare
//     OpenGL program behavior when our CG shader is on, and
//     when there is no shader being used at all (classic OpenGL
//     rendering).

//     Go down to the #pragma region part that says
//     "this code has no effect when the shader is on".
//     Verify that claim is true.  What other types of OpenGL
//     function call appears to have no effect when the CG Shader is on?

//     Change the values that are going into gluPerspective and gluLookAt()
//     Change them to completely wild values.  Take the eye
//     really really far away so that the set of triangles is
//     a mere speck. . .  Then turn the shader on.

// Conclusion:  IF YOU HAVE YOUR VERTEX SHADER ON, THEN ALL YOUR
// CODE that uses the OpenGL matrix stakcs __DOES NOT WORK ANYMORE__.

// !!  HMM!!  This is an important point.  This should be very interesting to you.  How
// are we going to make it look 3D without gluPerspective???
// Trust me, we will. . .

// 2)  Play around with the drawing code a bit.  Draw your own shapes.
//     Get a feel for what shows up in the window, and what does not,
//     when the shader is switched on.
//     What are the max/min values of x,y and z that glVertex3f() can take?
//
#pragma region answer to 2
//     ANSWER:  x, y and z can only take values between
//              [-1, +1] each if they are to show up in the final
//              render when the shader is on.
#pragma endregion

//
// 3)  Figure this out:
//     What is the effect of z-value when
//     the shader is on???  When does Z
//     appear to have any affect on the final drawing?
//     (try very large AND very small values of z!)
#pragma region answer to 3
     // At this point, Z-value will appear to have NO EFFECT!
     // To make the red square go in the back, put code like
#pragma endregion

// 4)  Draw a huge red square BEHIND the triangles
//     that appear when the shader is on.
//     How can you make the red square appear behind all
//     the triangles?
#pragma region answer to 4

// Use code like
/// glBegin( GL_QUADS ); glVertex3f(1,1,0);glVertex3f(-1,1,0);
/// glVertex3f(-1,-1,0);glVertex3f(1,-1,0); glEnd();

// _BEFORE_ the glBegin(); for the other triangles.

#pragma endregion


////////////////////////
// GLOBALS:  Here we declare 3 global variables
//           for use by our CG program.  More detail
//           when they're actually used.
CGcontext   myCgContext;        // Like a huge container for all of our CG stuff
CGprofile   myCgVertexProfile;  // A CGprofile contains a summary of the
                                // capabilities of the hardware that this
                                // CG program is going to run on.
CGprogram   myCgVertexProgram;  // And finally this global variable will be
                                // a reference to the program itself, once
                                // it has been compiled.

bool shaderOn = false;          // This is a global var that we use to
                                // switch on and off the shader, so
                                // we can see the diff.  You press spacebar.

// Below is a diagram that kind of shows in a big picture
// sort of way how the CGcontext, CGprofile and CGprogram exist.
/*
           /----------------------------------\
          /                                    \
         /                                      \
        /                                        \
       /                                          \
       \   CGprogram  ----- USES ----> CGprofile  /
        \                                        /
         \                                      /
          \                                    /
           \----------------------------------/
                         CGcontext
   
   CGprogram USES CGprofile, when CGprogram is compiled.

   CGcontext contains both CGprogram AND CGprofile.

*/

// Function that checks to see if CG is crying
// about some error.  We run this function
// after every Cg command we execute from
// our C++ code.
void checkForCgError(const char *situation)
{
  CGerror error;
  const char *string = cgGetLastErrorString(&error);

  if (error != CG_NO_ERROR) {
    printf("%s: %s: %s\n",
           "ERROR", situation, string);
    if (error == CG_COMPILER_ERROR) {
        printf("%s\n", cgGetLastListing(myCgContext));
    }
    exit(1);
  }
}


////////////////////////
// display() function
// Once the line of code that says "glutMainLoop()" executes
// we'll be trapped cycling in this "display" function
// forever until someone hits the ESC key.
void display()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  if( shaderOn == true )
  {
    // !! Next 2 lines actually TURN ON THE VERTEX SHADER!!
    cgGLBindProgram(myCgVertexProgram);
    cgGLEnableProfile(myCgVertexProfile);
  }
  
  
  #pragma region this code has no effect when shader is on
  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();
  gluPerspective( 45, 1.0, 0.4, 1000 );

  glMatrixMode( GL_MODELVIEW );
  glLoadIdentity();
  gluLookAt( 6.5, 0.0, 5.0,
             0.0, 0.0, 0.0,
             0.0, 1.0, 0.0 );
  #pragma endregion
  

  // Notice how we're always picking
  // values between -1 and +1.
  // That's important!  Try drawing your own stuff.
  glBegin(GL_TRIANGLES);
  
    // First, a red triangle that goes from the middle to
    // the top right corner.
    glColor3d ( 1,     0,     0 );
    glVertex3d( 0,     0,     0 );
    glVertex3d( 0,     0.98,  0 );
    glVertex3d( 0.98,  0.98,  0 );

    // Second, a green triangle that goes from the middle to
    // the top left corner
    glColor3d ( 0,     1,     0 );
    glVertex3d( 0,     0,     0 );
    glVertex3d( 0,     0.98,  0 );
    glVertex3d(-0.98,  0.98,  0 );

    // Third, a blue triangle that goes from the middle to
    // the bottom left corner
    glColor3d ( 0,     0,     1 );
    glVertex3d( 0,     0,     0 );
    glVertex3d( 0,    -0.98,  0 );
    glVertex3d(-0.98, -0.98,  0 );

    // Finally, a white triangle in the bottom right corner
    glColor3d ( 1,     1,     1 );
    glVertex3d( 0,     0,     0 );
    glVertex3d( 0,    -0.98,  0 );
    glVertex3d( 0.98, -0.98,  0 );

  glEnd();

  if( shaderOn == true )
  {
    // Now that we are totally done drawing, we want to
    // actually TURN OFF the shader.
    cgGLDisableProfile(myCgVertexProfile);      // !! SHADER OFF
  }

  glutSwapBuffers();
}

void keyboard(unsigned char c, int x, int y)
{
  switch (c) {
  case 27:  /* Esc key */
    /* Demonstrate proper deallocation of Cg runtime data structures.
       Not strictly necessary if we are simply going to exit. */
    cgDestroyProgram(myCgVertexProgram);
    cgDestroyContext(myCgContext);
    exit(0);
    break;
  case ' ': // spacebar
    {
        shaderOn = !shaderOn;   // flip

        char t[100];
        sprintf( t, "First Cg program!  Shader is %s", shaderOn ? "ON!" : "off." );
        glutSetWindowTitle( t );
        
        glutPostRedisplay();    // redraw screen
    }
    break;
  }
} 

int main(int argc, char **argv)
{
  // 0.  Initialize GLUT.
  // Thanks Kilgard!  You da bomb.  :D.
  #pragma region GLUT INIT
  // If we weren't using GLUT, we'd have much 
  // more than 6 lines of code to write to get
  // a window up in Windows.

  glutInitWindowSize(600, 600);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
  glutInit(&argc, argv);

  glutCreateWindow("First Cg program!  Press spacebar to turn shader ON!");
  glutDisplayFunc(display);
  glutKeyboardFunc(keyboard);
  #pragma endregion

  glClearColor(0.1, 0.12, 0.3, 0.0);  // dark background

  #pragma region CG INIT
  //////////////////////////////////////////////
  //
  // 1.  CREATE the Cg CONTEXT.
  //////////////////////////////////////////////
  #pragma region create Cg context
  // What's a "context"?
  
  // When you're programming, the word comes up a lot.
  // In Windows programming, you hear a lot about "device context."
  // With OpenGL, you talk about the "rendering context."

  // In plain English, a "context" is like a "circumstance."
  // You usually need to know the "context" of a sentence
  // to totally understand what it means.

  // "Sorry, I should have held that."

  // You'd need a context for that sentence for it
  // to make any sense.

  // In computing though, a context is a little more than
  // just a situation.

  // A "CONTEXT" in computing specifically is
  // a COLLECTION of variables and ALL the data that represents
  // the overall STATE of a program.

  // One of the main jobs of your operating system is to
  // handle "CONTEXT SWITCHES" -- ie to swap out
  // a program and all of its associated variables from
  // the CPU's registers, so another program can have
  // a chance to run for microsecond or two.

  // Read this:  Context switch on Wikipedia

  // So for Cg, a context will be a COLLECTION OF ALL
  // THE ASSOCIATED INFORMATION THAT HAS TO DO WITH
  // THIS CG SHADER.
  #pragma endregion

  // And so, now we shall create the context.
  myCgContext = cgCreateContext();
  checkForCgError("creating context");

  ////////////////////////////////////////
  // 
  // 2.  PICK A CG PROFILE TO USE
  ////////////////////////////////////////
  #pragma region select a CG profile
  // What's a CG PROFILE?
  
  // So what's a PROFILE in CG?  Its where


  // CG programs go to show off the number
  // of friends they have . . . :)

  // But really, a CG PROFILE is kind of like
  // a personality profile, only it tells the
  // CG Compiler what assembly level instructions
  // the present graphics hardware is capable of
  // processing.

  // The information about the PROFILE is used
  // WHEN THE CG PROGRAM IS COMPILED.

  // 'case ya didn't know, CG Programs are COMPILED
  // AT RUN TIME.  The CG CODE is normally __NOT__ compiled
  // along with the rest of your C program
  // when you first press F5 in Visual Studio.

  // That's called Dynamic Compilation and it is
  // a Good Thing.

  // You CAN, however, statically compile your
  // Cg program if you really want to.
  
  // Different people running your CG program
  // will be using different graphics cards to
  // run it, each with different capabilities.

  // My good ol' FX 5200, for example, has a much
  // different set of capabilities than Nathan's
  // 8800 card that he got for Christmas.

  // Should the instructions generated by the Cg
  // program to my old FX 5200 be exactly the same
  // as the instructions generated to Nathan's 8800?
  
  // No way!!

  // 

  // As such, since Nathan's 8800 card CAN DO MORE
  // THINGS than my FX 5200 can, the CG compiler
  // should have a much different _attitude_ when its 
  // generating assembly code for my old FX 5200
  // than it does for Nathan's 8800.

  // THE PROFILE YOU USE DEPENDS ON:
     // 1)  The hardware you have available
     // 2)  The graphics API you are using (OpenGL or Direct3D).

  #pragma endregion

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


  ////////////////////////////////////////
  // 
  // 3.  CREATE THE VERTEX PROGRAM, ASSOCIATING IT
  //     WITH YOUR CGcontext _and_ YOUR VERTEX PROFILE
  ////////////////////////////////////////
  myCgVertexProgram =
    cgCreateProgramFromFile(
      myCgContext,              /* Cg runtime context */
      CG_SOURCE,                /* Program in human-readable form */
      "vertexShader.cg",        /* Name of file containing program */
      myCgVertexProfile,        /* Profile: OpenGL ARB vertex program */
      "vertexShaderFunction",   /* Entry function name of vertex shader program */
      NULL);                    /* No extra compiler options */

  checkForCgError("creating vertex program from file");

  ////////////////////////////////////////
  // 
  // 4.  LOAD THE VERTEX PROGRAM IN TO THE GPU MEMORY
  ////////////////////////////////////////
  cgGLLoadProgram(myCgVertexProgram);
  checkForCgError("loading vertex program");
  #pragma endregion

  glutMainLoop();  // GOTO the display() function next
  return 0;
}


/* 
     ____   __   __      __   __  ___
    / _  \ /  / /  /    /  /  \ \/  /
   / _/ / /  / /  /    /  /    \   /
  / _/ \ /  / /  /__  /  /__   /  /
 /_____//__/ /______//______/ /__/

*/








// FILE:  vertexShader.cg
// This is our vertex shader program.  It is a single function.

// vertexShaderFunction takes in vertex position and color,
// whose values originate in our OpenGL code.

// It outputs a new vertex position and color, which will be 
// used to draw the final shapes to the screen.

/*

------------    -----------------------------    -------------
| INCOMING | => | vertexShaderFunction      | => | OUTGOING  |
|  VERTEX  |    |                           |    |  VERTEX   |
| with own | => | Like processing plant     | => | final     |
| position |    | takes incoming position   |    | position  |
| and color| => | and color values for each | => | and color |
------------    | vertex and does some      |    | of vertex |
                | math on them.             |    -------------
                -----------------------------

*/


void vertexShaderFunction (
            float3 incomingPosition : POSITION,  // : POSITION is a SEMANTIC (see note below)
            float3 incomingColor    : COLOR,

        out float4 outgoingPosition : POSITION,
        out float4 outgoingColor    : COLOR      // : COLOR is a SEMANTIC (see note below)
   )
{
    // compute outgoingPosition using the incomingPosition
    // In this example, we'll just pass it through unmodified.
    outgoingPosition.x = incomingPosition.x;
    outgoingPosition.y = incomingPosition.y;
    outgoingPosition.z = incomingPosition.z;
    outgoingPosition.w = 1; // don't worry what w is YET, we'll get to that later.

    
    // Here's a more advanced operation, to make color look cool.
    float3 transformedColor = saturate( ( cosh( incomingColor ) ) * cos( incomingPosition.yxy ) );


    outgoingColor = float4( transformedColor, 1 );
}

//////////////// WHAT IS THIS??? ////////////////////
// vertexShaderFunction is like a PROCESSING PLANT:
  // This vertex shader takes IN two things:
    // 1.  It TAKES IN the raw vertex xyz position in space,
    //        as handed to it by OUR OpenGL code (incomingPosition)
    //
    // 2.  It TAKES IN the raw vertex color, as handed to it
    //        by OUR OpenGL code (incomingColor)

  // This vertex shader SPEWS OUT two things:
    // 1.  It SPITS OUT a NEW, FINAL POSITION for the
    //        vertex (variable outgoingPosition)
    //
    // 2.  It SPITS OUT a NEW, FINAL COLOR for the
    //        vertex (in variable outgoingColor)

// This vertex shader will process EVERY SINGLE VERTEX
// IN OUR PROGRAM as it travels down the graphics pipeline.

// Something very important to realize about writing your
// own vertex shaders is, when you activate your vertex
// shader, YOU COMPLETELY BYPASS the regular OpenGL
// transformation and project matrices.

// So if you go to the C++ code and glRotatef, glTranslatef,
// and gluLookAt() to your heart's content, IT WILL HAVE
// ABSOLUTELY NO EFFECT on the final result you see here.

// That is because by writing and enabling your vertex 
// shader program, you have effectively __TAKEN OVER__
// the graphics pipeline.

// It is now YOUR responsibility to take in x, y and z
// coordinates that the C++ program will feed in here,
// and TRANSFORM THEM TO GENERATE THE 3D LOOKING IMAGE.

// So if you're used to programming with OpenGL purely
// without using shaders, and you're used to using
// gluLookAt() and such things to get your scene to look
// the way you want, and gluPerspective() to get that
// sweet perspective view, you'll need to kiss those glMatrix
// functions goodbye for now.

// K-I-S-S ;).

// Ok, now that seems very complicated!!!  How will we ever
// do that???

// Don't worry, we will.

// In order to make this digestable, we'll first start
// by totally ignoring the Z-component.  We'll pretend
// that z doesn't exist and we'll totally draw in 2D.


// You're used to programming so that you write all this code
// to get the vertices into correct position BEFORE calling
// glVertex3f.  Then the reality of it was, gluPerspective()
// and gluLookAt() took care of all the other stuff for you.

// Now we're doing things differently.

// The vertex shader we're writing makes changes to
// vertex position _AFTER_ the call of glVertex3f.

// is that the vertex shader will make changes to the
// vertices that you specified __AFTER__ the glVertex3f calls.

//
// This is totally NOT what you're used to, if you've never
// programmed shaders before.
// Also other points (that we'll explain in much more detail
// later) are that
//    - when the shader is on, you TOTALLY BYPASS the regular
//      OpenGL transformation matrices (glRotatef, glTranslatef
//      HAVE NO EFFECT WHEN YOUR SHADER IS ON).
//    - The reason the glRotatef and glTranslatef functions have
//      no effect is, once you've activated your shader, YOU
//      ARE IN FULL CONTROL OF THE GPU.  When your shader is on,
//      you become responsible to feed the rasterizer a set of 
//      points that have x values between [-1, +1] and
//      y-values between [-1, +1].  Seems strange, but get used to the idea.
//


///////////////////

// COVERED HERE:
// 1.  vector data types: float3 and float4
// 2.  SEMANTICS
// 3.  Keywords IN and OUT
// 

// BEFORE YOU READ THIS
// Make sure you know EXACTLY WHAT A "function parameter" is.
// If you don't, you need to brush up on your C++ programming,
// specifically "functions, parameters and arguments"
// before you attempt to understand this, or it won't make
// any sense.

///////////////////
// 1.  Vector data types: float3 and float4
//
// One of the absolute coolest things about working
// with CG is its treatment of vector values as
// first class, primitive types.

// The next tutorial will deal with vector data
// types and the operations that are defined for
// them in much more detail, but for now, just
// recognize that its just like working with
// real vectors.

///////////////////
// 2.  SEMANTICS:
// 
// Look at the first parameter to the vertexShaderFunction.

//             float3 incomingPosition : POSITION

// The last word there in all caps ("POSITION") is what
// we call a SEMANTIC.
//
// What's a semantic?  In plain English,
// 'semantic' just means the 'meaning' of something.
//
// We attach a SEMANTIC ("meaning") to each one
// of vertexShaderFunction()'s parameters 
// precisely so that CG KNOWS WHAT EACH FUNCTION PARAMETER IS
// __TO BE USED FOR__.
//
// FIRST PARAM:  incomingPosition
// The first parameter to "vertexShaderFunction"
// (the "incomingPosition" parameter) is given the
// POSITION semantic.  This lets CG know that CG should
// pass in the vertex position as the first argument
// to this function.

// CG WILL AUTOMATICALLY FEED IN POSITION ___FOR YOU___.  You don't
// have to do anything to pass the vertex position to the
// vertex shader OTHER THAN SPECIFY THE VERTICES IN THE OPENGL CODE.
// This takes a tad bit of getting used to, but its really natural.

// SECOND PARAM:  incomingColor
// The second parameter ("incomingColor") to vertexShaderFunction() is given the
// COLOR semantic.  This lets CG know that CG should
// pass in the vertex's COLOR as the second argument
// to this function.  Again, this happens AUTOMATICALLY
// and there's nothing special you have to do to get it
// to happen other than specify a color in OpenGL (using
// glColor3f or something).

// So attaching the POSITION semantic to a function parameter
// identifies it to CG.  Variable name doesn't matter.

///////////////////////////////
// 3.  KEYWORD OUT:
//
// Take a look at the third parameter "outgoingPosition."
// In front, it has the special word 'OUT'.
// At the end, it also uses the semantic "POSITION."
// Combining that information, that tells CG that the
// variable "outgoingPosition" will be the FINAL OUTPUTTED
// POSITION of the vertex.

// Same goes for outgoingColor, except of course,
// outgoingColor will contain the final outputted color.

// Keyword OUT tells CG that those two parameters
// ARE the output values of this function.

// You can only have ONE PARAMETER that has the combination
// of OUT and POSITION applied to it, and ONE PARAMETER
// that has the combination of OUT and COLOR applied to it,
// since each vertex can only have ONE FINAL position and
// ONE FINAL color.

// Also it might take some getting used to the fact that
// this shader program acts as a processing plant that
// sort of takes in two FULL containers (incomingPosition
// and incomingColor) and it ALSO TAKES +IN+ two EMPTY
// containers (outgoingPosition and outgoingColor).
// The JOB of the vertexShaderFunction is to use the
// stuff provided in the incomingPosition and incomingColor
// containers and to FILL the outgoingPosition and outgoingColor
// containers appropriately.

// Programmatically, you've seen interfaces like this before
// if you've ever used the strcpy() function in C
//    strcpy( char * dest, char * src );
// if we were to write this like a shader function is written,
// it would be:

//    strcpy( out char * dest,
//                char * src );

// ALSO as a final note, there is a keyword IN that you CAN use
//void vertexShaderFunction (
//         IN float3 incomingPosition : POSITION,  // keyword IN optional,
//         IN float3 incomingColor    : COLOR,     // designate as inputs
//
//        out float4 outgoingPosition : POSITION,
//        out float4 outgoingColor    : COLOR
//   )

// AND finally, note that YOU COULD ALSO do this:
//void vertexShaderFunction (
//        inout float4 pos  : POSITION,   // this var is both in and output
//        inout float4 color : COLOR,     // both in and output
//   )
// { /* code */ }

// And that's all there is to the basics of a vertex shader.
// If you got most of that, you're ready for the next one! :).



////////////////////
// REVIEW POINTS:
// Its not the NAMING of the parameters to the function
// "incomingColor" and "outgoingColor" that tell CG what
// each variable does.  Its the SEMANTIC and whether or
// not the variable uses the word OUT in front that tells
// CG what the variable is for.  FOr all Cg cares, you could
// write

//void vertexShaderFunction(
//                float3 hamburger   : POSITION,
//                float3 Fries       : COLOR,

//            out float4 milkshake   : POSITION,
//            out float4 coke        : COLOR
//          )

// and still the program would work the same, provided you
// make the appropriate changes to the function body as well.


// ONLY the ENTRY FUNCTION can use SEMANTICS.
// So if you write an additional helper function
// in the CG code file here, that's fine and dandy,
// but if the helper function has use of the POSITION
// or COLOR semantic, it will be to no effect.  You'd
// have to pass those values manually yourself, if
// you wanted access to the in your helper function.

// Final note:  The strangest thing you have to get
// used to is even though "vertexShaderFunction" is 
// just a function, YOU never get a chance to CALL
// IT YOURSELF.

// Semantics are the ONLY WAY you tell CG what
// variables do what.  YOU DO NOT get a chance
// to write a line of code like:
//
//     vertexShaderFunction( v1, c1, v2, c2 );

// As such, you must provide instructions to CG:
// "What parameter does what?" before you even
// compile and run the program.


// Additional:  What's an ENTRY FUNCTION?

// vertexShaderFunction (name of the shader function
// waaaaaaaaaay up at the top of this file) IS an entry function.
// Because vertexShaderFunction is where the program starts,
// vertexShaderFunction is called the ENTRY FUNCTION.

// Ok I'm done.  Send any questions, comments,
// +/- fdback, or appreciations! to billy.baloop@gmail.com.

/* 
     ____   __   __      __   __  ___
    / _  \ /  / /  /    /  /  \ \/  /
   / _/ / /  / /  /    /  /    \   /
  / _/ \ /  / /  /__  /  /__   /  /
 /_____//__/ /______//______/ /__/

*/


As always, code package available on esnips (thanks esnips!)