How to write a OpenGL program in VC++ environment by bagavathikumar


Introduction

OpenGL is an abbreviation of “Open Graphics Library”.

Writing an OpenGL Program

First of all, we have to satisfy the minimum requirement for setup the window to display the openGL graphics.  Windows GDI needs a Device Context (DC) to draw the graphics, but openGL uses Rendering Context (RC). we can use multiple rendering context to draw a single window, but only one RC is a current RC at any time. Since OpenGL uses the concept of a current Rendering Context.

Part 1: Windows Pixel Format:

The pixel format describes how the windows represent the graphics into memory. It is used to set the buffering, coloring and supporting interface. PIXELFORMATDESCRIPTOR structure is used to describe the pixel format.

Step 1:

  • Start the VC++ Project.
  • Go to File->New and select “MFC AppWizard (exe)”. Press ok.
  • Select the single document option.
  • Retain the all other default option and press finish button.

Step 2:

Now we have to include all necessary openGL files and libraries. First you should check whether these files are present in your PC. Normally these files are present in “Microsoft Visual StudioVC98IncludeGL” folder.

  • Go to Project -> settings -> link tab.
  • Select the “General” into category combo box.
  • Type the following into Object/Library modules text box.

“opengl32.lib glu32.lib”

  • Press ok.

Step 3:

Open the stdafx.h file and include the following header files.

#include <gl/gl.h>
#include <gl/glu.h>

Step 4:
Open the view file, go to PreCreateWindow function and insert the following code.

cs.style |= ( WS_CLIPCHILDREN | WS_CLIPSIBLINGS );

Step 5:

Next we have to define the windows pixel format. The pixel format describes how the graphics that the window displays are represented in memory. The PIXELFORMATDESCRIPTOR structure is used to describe the pixel format of the drawing surface. We choose the pixel format that supports OpenGL and double buffering with RGBA.

Now add the member variable to the CFirstGLView class.

int m_nPxlIndex;

Create a function called SetPixelFormat and write the coding like this:

BOOL CFirstGLView::SetPixelFormat(HDC hDC)
{
   PIXELFORMATDESCRIPTOR myPxlFormat;
   myPxlFormat.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Size Of thePixel Format Descriptor.
   myPxlFormat.nVersion = 1; // Set the Version.
   // Specify the properties of the pixel buffer.
   myPxlFormat.dwFlags = PFD_DRAW_TO_WINDOW | 
      PFD_DRAW_TO_BITMAP | 
      PFD_SUPPORT_OPENGL | 
      PFD_SUPPORT_GDI | 
      PFD_STEREO_DONTCARE;
   myPxlFormat.iPixelType = PFD_TYPE_RGBA; // Set the RGBA Format
   myPxlFormat.cColorBits = 32; // Color Depth Value
   // Set the Color bits.
   myPxlFormat.cRedBits = 8; 
   myPxlFormat.cRedShift = 16;
   myPxlFormat.cGreenBits = 8;
   myPxlFormat.cGreenShift = 8;
   myPxlFormat.cBlueBits = 8;
   myPxlFormat.cBlueShift = 0;
   // Specify the Alpha Value.
   myPxlFormat.cAlphaBits = 0;
   myPxlFormat.cAlphaShift = 0;
   myPxlFormat.cAccumBits = 64;   // Specify the Accumulation Buffer.
   // Specify the Accumulation Bits.
   myPxlFormat.cAccumRedBits = 16; 
   myPxlFormat.cAccumGreenBits = 16;
   myPxlFormat.cAccumBlueBits = 16;
   myPxlFormat.cAccumAlphaBits = 0;
   myPxlFormat.cDepthBits = 32; // Depth Buffer Bit.
   myPxlFormat.cStencilBits = 8; // Stencil Buffer Bit.
   myPxlFormat.cAuxBuffers = 0; // Auxiliary Buffer Bit.
   myPxlFormat.iLayerType = PFD_MAIN_PLANE;
   myPxlFormat.bReserved = 0;
   // Set the Mask Bits.
   myPxlFormat.dwLayerMask = 0;
   myPxlFormat.dwVisibleMask = 0;
   myPxlFormat.dwDamageMask = 0;
   m_nPxlIndex = ChoosePixelFormat( hDC, &myPxlFormat );
   if (m_nPxlIndex == 0 ) // Select the default index.
   {
      m_nPxlIndex = 1;    
      if ( DescribePixelFormat( hDC, m_nPxlIndex, 
      sizeof( PIXELFORMATDESCRIPTOR ), &myPxlFormat ) == 0 )
      {
         return FALSE;
      }
   }

   if ( SetPixelFormat( hDC, m_nPxlIndex, &myPxlFormat ) == FALSE )
   {
      return FALSE;
   }
   return TRUE;
}

ChoosePixelFormat returns an index, which is used to reference the pixel format. If it is return 0, we just set index to 1 and get the default pixel format using DescribePixelFormat.

Part 2: Create the Rendering Context

Step 6:

Now we have to create a rendering context and make it as current. So we need the protected member variable to store the rendering context. For that you need to add the member variable for CFirstGLView class.

HGLRC m_hRndContxt;

For create the rendering context you need to add the below method called CreateRenderingContext,

BOOL CFirstGLView::CreateRenderingContext(HDC hDC)
{
   m_hRndContxt = wglCreateContext (hDC);
   if (m_hRndContxt == NULL )
   {
      return FALSE;
   }
   if ( wglMakeCurrent ( hDC, m_hRndContxt ) == FALSE )
   {
      return FALSE;
   }
   return TRUE;
}

wglCreateContext is used to creates a new OpenGL rendering context, which is suitable for drawing on the device referenced by hDC. If the function succeeds, it returns the valid handle otherwise NULL.

Step 7:

Add the function OnCreate in response to the WM_CREATE message and edit the function like this:

int CFirstGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
   if (CView::OnCreate(lpCreateStruct) == -1)
      return -1;

   HWND hWnd = GetSafeHwnd();
   HDC hDC = ::GetDC(hWnd);
   // Set the Pixel Format. If it is success it will return true.
   if (SetPixelFormat(hDC)==FALSE)
      return 0; 

   // Form the Rendering Context.
   if (CreateRenderingContext(hDC)==FALSE)
      return 0;

   return 0;
}

Step 8:

Now add the function OnDestroy in response to the WM_DESTROY message, and edit the function like this:

void CFirstGLView::OnDestroy()
{
   if(wglGetCurrentContext()!=NULL)
   {
      // Clear the rendering context. That is to set the rendering context not current.
      wglMakeCurrent(NULL, NULL) ;
   }
   if (m_hRndContxt!=NULL)
   {
      // Delete the rendering context.
      wglDeleteContext(m_hGLContext);
      m_hRndContxt = NULL;
   }

    CView::OnDestroy();
}

Step 9:

Finally edit the constructor for CFirstGLView like this:

CFirstGLView:: CFirstGLView ()
{
   // To Initialize the rendering context and pixel format.
   m_hRndContxt = NULL;
   m_nPxlIndex = 0;
}

Now compile the program and run the program. The SDI window will display that is still look like a generic MFC program. But it is enabling OpenGL drawing.

Part 3: Explore your World

Step 10:

Now add the function OnSize in response to the WM_SIZE message and edit the function like this:

void CFirstGLView::OnSize(UINT nType, int cx, int cy) 
{
   CView::OnSize(nType, cx, cy);
   GLsizei width, height;
   GLdouble aspect;

   width = cx;
   height = cy;

   if (cy==0)
      aspect = (GLdouble)width;
   else
      aspect = (GLdouble)width/(GLdouble)height;

   glViewport(0, 0, width, height);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluOrtho2D(0.0, 500.0*aspect, 0.0, 500.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

OnSize is used to define the viewport and the viewing coordinates. The viewport is the area of the window that the OpenGL can draw.

OpenGL maintains three internal matrices to control various transformations. These matrices are name Projection, ModelView, and Texture.

  • Projection (GL_PROJECTION) – handles eye coordinates to clip coordinates.
  • ModelView (GL_MODELVIEW) – handles converts object coordinates to eye coordinates.
  • Texture (GL_TEXTURE) – converts textures from the coordinates handles.

Step 11:

Finally add the function OnPaint in response to the WM_PAINT message and edit the function like this:

void CFirstGLView::OnPaint() 
{
   // device context for painting 
   CPaintDC dc(this);

   glLoadIdentity();
   glClear(GL_COLOR_BUFFER_BIT);
   // Draw a polygon
   glBegin(GL_POLYGON);
      glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
      glVertex2f(100.0f, 50.0f);
      glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
      glVertex2f(450.0f, 400.0f);
      glColor4f(0.0f, 0.0f, 1.0f, 1.0f);    
      glVertex2f(450.0f, 50.0f);
      glVertex2f(100.0f, 400.0f);
   glEnd();

   glFlush();
}

Now compile and run the program. Now you will able to see the OpenGL Magic.

It is a high level API, which is used to communicate with graphics hardware. This interface consists of around 120 distinct commands, which you can use to specify the objects and operations needed to produce interactive three-dimensional applications. If you are interested in creating a high quality and attractive 2D or 3D graphics in windows environment, keep reading this article. This will show you how to create a openGL graphics in Visual C++.