Graphics Programming Using Opengl and MFC
Total Page:16
File Type:pdf, Size:1020Kb
Graphics Programming Using OpenGL and MFC
CALIFORNIA SOFTWARE LABS
R E A L I Z E Y O U R I D E A S
California Software Labs 6800 Koll Center Parkway, Suite 100 Pleasanton CA 94566, USA. Phone (925) 249 3000 Fax (925) 426 2556 [email protected] http://www.cswl.com www.cswl.com
Graphics Programming Using OpenGL and MFC
A Technical Report
Technical Expertise Level : Intermediate Requires knowledge of : MFC
INDEX
INTRODUCTION...... 3 OPENGL SUPPORT ON WINDOWS NT...... 3 WHAT CAN OPENGL DO FOR ME...... 4 A GRAPHICS PRIMER...... 5 OPENGL PROCESS PIPELINE...... 9 OPENGL COMMAND SYNTAX...... 11 OPENGL PRIMITIVES...... 11 OPENGL STATE VARIABLES...... 12 OPENGL APPLICATION LAYOUT...... 13 LETS WRITE A SIMPLE OPENGL APPLICATION...... 15 BUILDING THE APPLICATION...... 20 PERFORMANCE TIPS...... 38 GET LITTLE HELP FROM OPENGL UTILITY LIBRARY...... 40 CONCLUSION...... 41 APPENDIX A...... 41
In this article, I have tried to familiarise the reader, the most commonly used concepts and techniques in OpenGL Programming. Eventhough the focus is on
CSWL Inc, Pleasanton, California - 2 - www.cswl.com
building simple 3D graphic programmes, many of the techniques presented here are also applicable to advanced applications.
INTRODUCTION
OpenGL is a library of graphics routines available on a wide variety of hardware platforms and operating systems, including Windows 95,Windows NT, OS/2, Digital's Open VMS, and X Windows.
OpenGL was developed by Silicon Graphics Incorporated in the year 1992, and was eventually accepted as an industrial standard for hardcore 3D graphics. The OpenGL Architecture Review Board maintains the library ensuring that OpenGL is implemented properly on various platforms. This makes porting the programs across the platforms much easier. OpenGL routines are well structured, highly stable, intuitive, scalable from PCs to Super Computers and guarantied to produce consistent visual displays across various platforms.
This article intents to introduce a VC++ programmer, to the concepts and usage of OpenGL library on Windows NT 3.5 (or higher) platform.
OPENGL SUPPORT ON WINDOWS NT
Following libraries are packed with Windows NT to support OpenGL.
1. The main OpenGL library Contains the core APIs that are guarantied to be implemented on any platform that supports OpenGL. APIs in this library has a prefix ' gl '. These APIs are used to draw shapes, perform transformations, produce lighting effects, map textures etc. 2. OpenGL Utility Library Contains higher-level helper functions that internally use the core APIs to work. APIs in this library has a prefix ' glu ' and helps the programmer in polygon tessellation, texture scaling etc. These
CSWL Inc, Pleasanton, California - 3 - www.cswl.com
APIs are also guarantied to be implemented on all platforms that supports OpenGL. 3. OpenGL Auxiliary Library Contains some special APIs that are platform dependent and may not be available on all platforms. 4. Apart from the APIs in the above libraries, there are some APIs that are unique to Windows NT implementation of OpenGL. These APIs have a prefix ' wgl ' and acts as an interface between Windows and OpenGL.
To use these APIs in your application, you have to set links to OPENGL32.LIB (core APIs), GLU32.LIB (utilities) and GLAUX.LIB (auxiliary library functions). The header files to be included are gl\gl.h (core APIs), gl\glu.h (utility library APIs) and gl\glaux.h (auxiliary library functions).
WHAT CAN OPENGL DO FOR ME
Well... Almost everything in 3D graphics !!!
OpenGL functionality ranges from drawing simple shapes like points, lines and polygons to creating graphic marvels like 'Jurassic Park' with near photographic quality. OpenGL APIs relieves you from the complex mathematics involved in lighting, shading, blending, antialiasing, texture mapping and animation. OpenGL also supports features like Fog, Motion blur, Gauraud shading, non-uniform rational B-spline (NURBS) curves and surfaces etc. We shall see more about them later in the article.
However, OpenGL is more of a renderer than a modeler. Even though you can draw points, lines and polygons using OpenGL, combining these basic shapes to build the required 3D image is the applications responsibility. The OpenGL Utility Library packed with Windows NT can assist with modeling tasks, but for all practical purposes modeling is the application's responsibility.
CSWL Inc, Pleasanton, California - 4 - www.cswl.com
A GRAPHICS PRIMER
Before starting to use OpenGL APIs, let us look into some of the relevant concepts you may not be familiar with. This is just a random listing without any order. A brief understanding of these terms will surely be helpful to you while reading any article on OpenGL.
Color systems and Logical Palettes
Let us take a look at how pixel colors are represented in various color systems.
1. Monochrome Here, 1 bit represents a pixel. The pixel is turned on or off depending on the value of the bit. 2. 16 Color Here, 4 bits represents a pixel. Each nibble holds an index of a color in a 16-color palette. 3. 256 Color Here each pixel is represented by a Byte, which holds an index of a color in a 256-color palette. 4. 16 Million Color In this system, 24 bits are used to represent a pixel. Each byte in the 24 bits holds the actual Red, Green, and Blue components of the color of the pixel.
Among the above mentioned color schemes, we can see that 16 color and 256 color systems use indexes to find the color of a pixel from a palette. There are two types of palettes, System palette and logical palette. System palette is provided by the operating system and most of the applications use this palette for displaying purposes. But there is a pitfall here. The system palette used by Windows NT on a
CSWL Inc, Pleasanton, California - 5 - www.cswl.com
256-color system has only 20 colors. OpenGL cannot accurately render 3D objects with just 20 colors. The solution is either to upgrade the system to support more colors or to set up a logical palette that contains a reasonable number of colors and map it into Windows system palette.
Device Dependent and Independent Bitmaps
Device Dependent bitmaps use system palette to convert the color indexes to the pixel colors. Since system palettes may vary for different devices, these bitmaps will be displayed differently on each device. Device Independent bitmaps (DIBs) carry the color table along. Hence they can give consistent output with any device. Any bitmap that you use as an OpenGL primitive should be a DIB.
Front and Back Buffers
OpenGL supports front and back video buffers. The front buffer holds the current display information. In animations, we may have to draw many frames on the screen one after the other. If you don't want the user to see a frame as it is being drawn, you can send the drawing commands to a hidden buffer (called back buffer) and swap the buffers when the frame is finished.
Depth Buffer
The Z-Depth buffer is used by OpenGL to keep track of the proximity of the viewer's object. It is also crucial for hidden surface removal.
Stencil Buffer
Stencil Planes are used by OpenGL to restrict the drawing to certain portions of the screen. Useful for constructive solid geometry, interference, mirror reflection, and shadow algorithms
CSWL Inc, Pleasanton, California - 6 - www.cswl.com
Accumulation Buffer
Useful for image post-processing, creating motion blur, and depth-of-field effects
Alpha Buffer
Useful for Alpha blending which described later in this section.
Pixel Formats
Pixel format contains the attributes for a drawing device. OpenGL gives us almost total control over the pixel format. These attributes include color depth, double buffering, whether the drawing surface uses RGBA or Indexed color mode, number of bits used in depth or stencil buffers, whether to draw to a window or to a bitmap etc.
Rendering Context
Just like the normal Windows programs need to create a GDI Device Context before drawing to a window, OpenGL programs need to create a Rendering Context and be made current before any drawing. The Rendering context holds the OpenGL state variables (See OpenGL State Variables). You can have many rendering contexts in your program with different pixel formats but only one rendering context can be made current at any specific time. All the OpenGL drawing commands are routed to the current rendering context. If your application is multithreaded, you can have only one rendering context current per thread at any specific time.
Gouraud shading
CSWL Inc, Pleasanton, California - 7 - www.cswl.com
Gouraud shading is a technique used to apply smooth shading to a 3D object by interpolating color differences between the vertices across its surfaces.
Texture Mapping
OpenGL allows you to 'paste' an image onto a surface. This feature if effectively used can make your 3D object look like natural objects. For example, you can map a DIB representing wood grain onto the faces of a cube to make it look like a wooden box.
Anti-Aliasing
Lines drawn on a computer display have jagged edges. This effect is predominant when lines are drawn at low resolution. Anti-aliasing is a common computer graphics technique that modifies the color and intensity of the pixels near the line in order to produce smooth lines. OpenGL supports antialiasing.
Alpha Blending
Alpha blending uses the Alpha value of the RGBA code, allowing one to combine the color of the fragment (see OpenGL Process Pipeline) being processed with that of the pixel already stored in the frame buffer. Imagine, for example, drawing a transparent light blue window in front of a red box. Alpha blending allows simulating the transparency of the window object so the box seen through the glass will appear with a magenta tone.
Fog
In OpenGL, Fog is a term that really describes an algorithm that simulates the effect of air, making the farther points of an object less sharp and more realistic.
CSWL Inc, Pleasanton, California - 8 - www.cswl.com
Display Lists
A display list is a group of OpenGL commands that have been stored for later execution. When a display list is invoked, the commands in it are executed in the order in which they were issued. One common use is creating a display list for an object that is to be drawn more than once. If the vertices of the object must be calculated, then the calculations need only be performed rather than each time the object is drawn.
Transformation Matrices
You will already know that Matrices are used extensively in graphics for transformation purposes. OpenGL provides the You with Three stacks of 4*4 Matrices, which can be used for performing any desired transformations (scaling, translation and rotation) on the OpenGL primitives. They are,
1. MODELVIEW Matrix Stack Use these matrices to define transformations for individual 3D objects 2. PROJECTION Matrix Stack Use these matrices to define the clipping volumes, perspective ratio etc 3. TEXTURE Matrix Stack Use these matrices to define transformations for the texture coordinates
The usage of these matrices is discussed later.
CSWL Inc, Pleasanton, California - 9 - www.cswl.com
OPENGL PROCESS PIPELINE
The series of processes performed by OpenGL for rendering a 3D scene on the screen are listed below in their order of execution.
1. The user defined vertices are sent to a vertex buffer 2. When vertices for an entire OpenGL primitive are buffered, the Model- View matrix transformations are applied to the Vertices and to the current Normal. 3. Texture Matrix transformations are applied to texture coordinates 4. Lighting and fog factors are calculated for the vertices using the current color settings 5. The combined information from the above processes are used to assemble a Primitive 6. The Projection-View matrix transformations are applied to the primitive 7. Clipping and culling of the primitive is performed 8. Light intensities at vertices of the objects are evaluated 9. Sub-pixel accurate slopes and offsets are generated for color, depth, alpha, fog, and texture coordinates 10. Edge walking and span interpolations are performed for surface primitives. The resulting interpolated data is referred to as Fragments 11. The Scissor test is done to eliminate the fragments outside the Scissor rectangle on the screen specified by the user 12. The alpha test (performed only in RGBA mode) compares the fragment's Alpha value with a reference value set by the user and discards or accepts the fragment according to the comparison mode set by the user 13. Stencil test compares the fragment's Stencil value with a reference value set by the user and discards or accepts the fragment according to the comparison mode set by the user
CSWL Inc, Pleasanton, California - 10 - www.cswl.com
14. Depth test compares the fragment's Z-value value with a reference value set by the user and discards or accepts the fragment according to the comparison mode set by the user 15. A fragment produced by rasterization modifies the corresponding pixel in the frame buffer only if it passes the above four tests 16. Alpha Blending is done where, Alpha value (diffuse-material value) of the RGBA code, is used for combining the color of the fragment being processed with that of the pixel already stored in the frame buffer to produce an effects like transparency. 17. Dithering applied to the fragment's color or color-index value 18. User defined logical operation can be applied between the fragment and the value stored at the corresponding location in the frame buffer; the result replaces the current frame buffer value. This is done only in the color index mode.
OPENGL COMMAND SYNTAX
OpenGL commands use the prefix 'gl'. Some OpenGL command names have a number at the end that indicates the number of values that must be presented to the command. The character that follows the number indicates the specific type of the arguments like signed short integer (s), signed integer (i), character (b), single- precision floating-point (f) or double precision floating-point (d). If the final character is 'v', indicating that the command takes a pointer to an array (a vector) of values. OpenGL defined constants begin with GL_, use all capital letters, and use underscores to separate words.
CSWL Inc, Pleasanton, California - 11 - www.cswl.com
OPENGL PRIMITIVES
OpenGL supports the following primitives
Points Lines Line Loops Polygons Triangles Triangle Fans Triangle Strips Quads and Quad Strips
The syntax for defining an n vertex primitive is as given below
glBegin (primitive type); glVertex3f (x1, y1, z1); glVertex3f (x2, y2, z2); ..... glVertex3f(xn, yn, zn); glEnd();
OPENGL STATE VARIABLES
As a geometric primitive is drawn, each of its vertices is affected by OpenGL "state" variables associated with the current RC. These state variables specify information such as line width, line stipple pattern, color, shading method, fog, polygon culling, etc . . . They can be broadly categorized as follows.
CSWL Inc, Pleasanton, California - 12 - www.cswl.com
1. Some variables hold the on-off states of OpenGL capabilities that can be turned on or off. Examples of such capabilities are Lighting, Alpha blending, Depth test, etc. You can turn these capabilities On and Off using the commands glEnable() and glDisable(). For example,
glEnable(GL_LIGHTING); // enables lighting
2. Some state variables specify the modes for certain OpenGL functionality that can operate in various modes. Examples are Shading Model, Lighting Model etc. Mode state variables require commands specific to the state variable being accessed in order to change its value. For example,
glShadeModel(GL_SMOOTH); // Sets the shade mode to Gouraud // Shading
3. Some state variables hold the information regarding the current color, current normal, line width etc. Value state variables require commands specific to the state variable being accessed in order to change it value. For example,
glColor3f(1.0f, 0.0f, 0.0f); //Sets the current drawing color to Red
All OpenGL state variables have a default value.
CSWL Inc, Pleasanton, California - 13 - www.cswl.com
OPENGL APPLICATION LAYOUT
The 'View' class of a MFC program that uses OpenGL APIs to render will have the following layout.
PreCreateWindow
Set up the correct window style
WM_CREATE message
Create a device context Choose a pixel format Set up palette if needed Create an OpenGL Rendering context (RC)
Make the Rendering Context current Set the OpenGL state variables Make the Rendering context not current
WM_SIZE message
Set up Aspect ratio Set up perspective viewing volume
WM_ERASEBACKGROUND
Do not allow windows to erase the background (which may cause flicker)
WM_PAINT message
CSWL Inc, Pleasanton, California - 14 - www.cswl.com
Make the Rendering Context current Draw something Make the Rendering context not current
WM_DESTROY message
Make the Rendering context not current Destroy the Rendering Context
LETS WRITE A SIMPLE OPENGL APPLICATION
Let's write a simple OpenGL application to display two cubes rotating in diffrent directions and illuminated by a spotlight. I have carefully selected this example to familiarize you the OpenGL transformations. Before getting into the code, lets see the major parts of the above layout in little more detail
1. Setting up a proper Window style
In the PreCreateWindow member function, modify the default window style so that WS_CLIPSIBLINGS and WS_CLIPCHILDREN bits are set. This is to prevent OpenGL from displaying output in child windows.
2. Choosing a pixel format
The PIXELFORMATDESCRIPTOR structure describes a pixel format used by a DC. Since this is the most error prone area for a beginner, I have listed the members of the PIXELFORMATDESCRIPTOR structure and their typical values for a beginners application in Appendix A. The reader is advised to refer it before further reading.
CSWL Inc, Pleasanton, California - 15 - www.cswl.com
For choosing a Pixel Format, you have to fill in a PIXELFORMATDESCRIPTOR structure with the desired format and passing it to the system using the ChoosePixelFormat() API function. The quality of the output depends on the values you choose here. The system gives a close match depending on the resources available on that particular system. You can examine the granted pixel format using the API function DescribePixelFormat(). You have to loop through this cycle until you get the most appropriate pixel format for your application on that system. Set the pixel format using the API SetPixelFormat().
3. Setting up a logical palette if needed
Examine the dwFlags member of the PIXELFORMATDESCRIPTOR and check if the PFD_NEED_PALETTE flag is set. If so, set up a logical palette with a reasonable number of colors.
4. Creating the RC and making it current
The API function wglCreateContext() takes the DC (for which we have already set the pixelformat) as the argument, creates the RC and returns the handle. You can use wglMakeCurrent() to make the RC current and not current.
5. Setting the state variables
Since our application is very simple and straightforward, default values will do for most of the state variables. I have set the shading model to be Smooth, enabled Lighting, Vertex winding direction of the faces of the cube to be Counter Clockwise, drawing color, etc in this application.
6. Setting the aspect ratio and viewing frustum
CSWL Inc, Pleasanton, California - 16 - www.cswl.com
While the user changes the size of the window, the aspect ratio also should be changed dynamically to avoid distortion of the displayed elements. Viewing frustum can be defined by defining the View-Angle, distance to the front-clipping plane, dimensions of the front clipping plane and the distance to the back-clipping plane. This also implicitly defines the perspective ratio. Any object or the parts of object that goes out of this viewing frustum is clipped. As I mentioned earlier, the PROJECTION Matrix stack is used to hold these transformations. These transformations can be applied to the PROJECTION Matrix using the APIs gluPerspective() and glViewPort().
7. Drawing Something
Drawing a 3D Object.
OpenGL can draw only its primitives. It is application's responsibility to split the target entities into OpenGL primitives. For example, in our application, target entity is a Cube. Since cube is not a primitive, we have to split it into the six Faces. Face is a primitive (Polygon) and can be defined as mentioned earlier (see OpenGL primitives). While defining vertices, keep in mind that OpenGL's default grid is a cube of side 2 units and centered at origin. This means that X, Y, Z coordinates can range from 1.0 to -1.0.
Applying Transformations
As I have mentioned earlier, MODELVIEW Matrix is used to transform the primitives. A general layout for applying transformations to primitives is given below.
Set MODELVIEW Matrix as the current matrix Initialize MODELVIEW Matrix
CSWL Inc, Pleasanton, California - 17 - www.cswl.com
Define Transformation T1 Define Transformation T2 .... Define Transformation Tn
Define Primitive P1 Define Primitive P2 ... Define Primitive Pn
The thumb rule is, any primitive is affected by the transformations defined above its definition and below the initialization of the matrix. Hence in the above case, all the transformations are applied to all the primitives. Suppose we want to apply separate transformations for the two cubes in our application, the layout will be as shown below.
Set MODELVIEW Matrix as the current
Initialize MODELVIEW Matrix
Define Transformation Ta Define Transformation Tb ... Define Transformation Tn
Define Cube1 Primitives
Initialize MODELVIEW Matrix
Define Transformation TA Define Transformation TB
CSWL Inc, Pleasanton, California - 18 - www.cswl.com
... Define Transformation TN
Define Cube2 Primitives
Now, consider the case where some transformations are common and some are separate for the cubes. In that case, we have to retain the matrix that holds common transformations. Fortunately, we can use the matrix stack. The layout becomes
Set MODELVIEW Matrix as the current Initialize MODELVIEW Matrix
Define CommonTransformation Tc1 Define CommonTransformation Tc2 ... Define CommonTransformation Tcn
Push Matrix Define Transformation Ta Define Transformation Tb .... Define Transformation Tn
Define Cube1 Primitives Pop Matrix Push Matrix Define Transformation TA Define Transformation TB .... Define Transformation TN
CSWL Inc, Pleasanton, California - 19 - www.cswl.com
Define Cube2 Primitives Pop Matrix
Wondering what is happening? The idea is simple. When you push a matrix, you are actually putting a copy of the current matrix on the top of the matrix stack, leaving you with two identical matrices on the top. When you pop, OpenGL removes one copy from the stack, leaving the original one on the top.
Applying Lighting
In OpenGL, Light has ambient, diffusion and specular properties. You can set RGB values of each component. You can also specify the position, direction, cut-off angle and attenuation factors for the light. These property vectors are passed to OpenGL using the API glLightfv().
Apart from specifying light properties, OpenGL allows you to specify the properties of the surface materials of the Objects you are drawing. These properties are Ambient light reflection, Diffusion light reflection, Specular light reflection, Emission, and Shininess. These property vectors are passed to OpenGL using the API glMaterialfv().
The appearance of an object depends on the properties of light falling on it and the reflective properties of the surfaces.
In order for OpenGL's lighting model to work, you should also specify the Normals for each vertex of the object using the API glNormal3f(). Note that one RC can support only upto 8 lights.
CSWL Inc, Pleasanton, California - 20 - www.cswl.com
BUILDING THE APPLICATION
Now its time to see the above concepts in action. Crank up your MFC AppWizard. Create a simple SDI application. Add Links to OPENGL32.LIB, GLU32.LIB and GLAUX.LIB
In the View class,
Include gl/gl.h, gl/glu.h and gl/glaux.h
Override PrecreateWindow()
Add handlers for WM_CREATE, WM_DESTROY, WM_SIZE, WM_ERASEBACKGROUND, And WM_TIMER
Add the following member variables
HGLRC m_hRC; GLfloat m_Angle; GLfloat ambmat1 [4]; GLfloat specmat1 [4]; GLfloat ambmat2 [4]; GLfloat specmat2 [4]; GLfloat amblight1 [4]; GLfloat difflight1 [4]; GLfloat speclight1 [4]; GLfloat poslight1 [4]; GLfloat dirlight1[4];
CSWL Inc, Pleasanton, California - 21 - www.cswl.com
Add the following member functions
void DrawNextFrame() void CalcNormal ( double*, double* , double* ,double* ) void DrawCube() void SetupLogicalPalette()
Now, modify the View.cpp file to look like the one given below
// OGLAnimationView.cpp: // implementation of the COGLAnimationView class //
#include "stdafx.h" #include "OGLAnimation.h" #include "OGLAnimationDoc.h" #include "OGLAnimationView.h"
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
///////////////////////////// // COGLAnimationView
CSWL Inc, Pleasanton, California - 22 - www.cswl.com
IMPLEMENT_DYNCREATE(COGLAnimationView, CView)
BEGIN_MESSAGE_MAP(COGLAnimationView, CView) //{{AFX_MSG_MAP(COGLAnimationView) ON_WM_CREATE() ON_WM_DESTROY() ON_WM_SIZE() ON_WM_TIMER() ON_WM_ERASEBKGND() //}}AFX_MSG_MAP END_MESSAGE_MAP()
///////////////////////////////////// // COGLAnimationView construction/destruction COGLAnimationView::COGLAnimationView() {
m_Angle=0.0f;
// Ambient, diffusion, and specular // light reflective properties of the // surfaces of the cube1 (Green) ambmat1 [0]=0.0f; ambmat1 [1]=0.3f; ambmat1 [2]=0.0f; ambmat1 [3]=1.0f;
specmat1 [0]=0.0f; specmat1 [1]=0.8f; specmat1 [2]=0.0f;
CSWL Inc, Pleasanton, California - 23 - www.cswl.com
specmat1 [3]=1.0f;
// Ambient, diffusion, and specular // light reflective properties of the // surfaces of the cube2 (Red)
ambmat2 [0]=0.3f; ambmat2 [1]=0.0f; ambmat2 [2]=0.0f; ambmat2 [3]=1.0f;
specmat2 [0]=0.8f; specmat2 [1]=0.0f; specmat2 [2]=0.0f; specmat2 [3]=1.0f;
// Ambient, diffusion, specular, position, // and direction properties of the light1
amblight1 [0]=0.8f; amblight1 [1]=0.8f; amblight1 [2]=0.8f; amblight1 [3]=1.0f;
difflight1 [0]=0.2f; difflight1 [1]=0.2f; difflight1 [2]=0.2f; difflight1 [3]=1.0f;
speclight1 [0]=0.1f;
CSWL Inc, Pleasanton, California - 24 - www.cswl.com
speclight1 [1]=0.1f; speclight1 [2]=0.1f; speclight1 [3]=1.0f;
dirlight1 [0]=0.0f; dirlight1 [1]=0.0f; dirlight1 [2]=0.0f; dirlight1 [3]=1.0f;
poslight1 [0]=0.0f; poslight1 [1]=0.5f; poslight1 [2]=0.0f; poslight1 [3]=1.0f;
}
COGLAnimationView::~COGLAnimationView() { }
BOOL COGLAnimationView::PreCreateWindow(CREATESTRUCT& cs) { // set window style // cs.style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS ;
return CView::PreCreateWindow(cs); }
/////////////////////////////////////////
CSWL Inc, Pleasanton, California - 25 - www.cswl.com
// COGLAnimationView drawing
void COGLAnimationView::OnDraw(CDC* pDC) { COGLAnimationDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);
DrawNextFrame();
}
////////////////////////////////////////////////////////// // COGLAnimationView diagnostics
#ifdef _DEBUG void COGLAnimationView::AssertValid() const { CView::AssertValid(); }
void COGLAnimationView::Dump(CDumpContext& dc) const { CView::Dump(dc); } COGLAnimationDoc* COGLAnimationView::GetDocument() { ASSERT(m_pDocument-> IsKindOf(RUNTIME_CLASS(COGLAnimationDoc))); return (COGLAnimationDoc*)m_pDocument; } #endif //_DEBUG
CSWL Inc, Pleasanton, California - 26 - www.cswl.com
////////////////////////////////////////////////////////// // COGLAnimationView message handlers
int COGLAnimationView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1;
//Create an appropriate Rendering Context which is needed by OGL //to draw anything.
PIXELFORMATDESCRIPTOR pfd= { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, //color depth 0,0,0,0,0,0, 0,0,0,0,0,0,0, 32, //number of Depth buffer bits per pixel 0,0, PFD_MAIN_PLANE, 0, 0,0,0 };
CClientDC clientDC(this);
CSWL Inc, Pleasanton, California - 27 - www.cswl.com
int pixelformat=ChoosePixelFormat(clientDC.m_hDC,&pfd); SetPixelFormat(clientDC.m_hDC,pixelformat,&pfd);
// Cross-check the pixel format //Here, I'm checking only whether palette is needed
DescribePixelFormat(clientDC.m_hDC,pixelformat,sizeof(pfd),&pfd); if(pfd.dwFlags & PFD_NEED_PALETTE) SetupLogicalPalette();
// Create RC m_hRC= wglCreateContext(clientDC.m_hDC);
//Set the state variables
wglMakeCurrent(clientDC.m_hDC,m_hRC); //Make RC current
glEnable(GL_DEPTH_TEST); //Enable Depth Test
glEnable(GL_LIGHTING); //Enable Lighting
glShadeModel(GL_SMOOTH); //Gauraud shading
glClearColor(0.0f,0.0f,0.0f,0.0f); //The color used to //clear the screen is black
glFrontFace(GL_CCW); //Vertices of the primitives are wound //in counter clock direction
wglMakeCurrent(clientDC.m_hDC,NULL);
CSWL Inc, Pleasanton, California - 28 - www.cswl.com
//Make the RC not-current
//Initialize Timer for animation SetTimer(1,200,0);
return 0;
}
void COGLAnimationView::OnDestroy() { CView::OnDestroy();
KillTimer(1); wglDeleteContext(m_hRC);
}
void COGLAnimationView::OnSize(UINT nType, int cx, int cy) {
CView::OnSize(nType, cx, cy);
GLdouble ar; // aspect ratio
// ReInitialize Projection and
CSWL Inc, Pleasanton, California - 29 - www.cswl.com
// ModelView Matrices to adapt to // new window size.
CClientDC cdc(this); wglMakeCurrent(cdc.m_hDC,m_hRC); glMatrixMode(GL_PROJECTION); glLoadIdentity(); //initialize the PROJECTION matrix
if(cy!=0)ar=(GLdouble)cx / (GLdouble)cy; gluPerspective(40.0, //View Angle ar, //aspect ratio 1.0, //distance to front clipping plane
40); //distance to back clipping plane
glViewport(0,0,cx,cy); //Set the view port to be entire screen
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //initialize the MODELVIEW matrix wglMakeCurrent(NULL,NULL);
}
void COGLAnimationView::OnTimer(UINT nIDEvent) {
CSWL Inc, Pleasanton, California - 30 - www.cswl.com
// Change orientation angle of objects
m_Angle += 10; if(m_Angle>355)m_Angle=0.0f;
// Redraw the Frame DrawNextFrame();
CView::OnTimer(nIDEvent); }
void COGLAnimationView::CalcNormal (double * p1, double * p2, double * p3, double * n) {
//Find the cross product of the vectors p1p2 and p1p3
double a[3],b[3];
a[0]=p2[0]-p1[0]; a[1]=p2[1]-p1[1]; a[2]=p2[2]-p1[2];
b[0]=p3[0]-p1[0]; b[1]=p3[1]-p1[1]; b[2]=p3[2]-p1[2];
n[0]=a[1]*b[2]-a[2]*b[1]; n[1]=a[2]*b[0]-a[0]*b[2]; n[2]=a[0]*b[1]-a[1]*b[0];
CSWL Inc, Pleasanton, California - 31 - www.cswl.com
//make it a unit vector
double len=sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]); if(len==0)return;
n[0]=n[0]/len; n[1]=n[1]/len; n[2]=n[2]/len; }
BOOL COGLAnimationView::OnEraseBkgnd(CDC* pDC) { // Do not erase the background, just return
//return CView::OnEraseBkgnd(pDC); return TRUE; }
void COGLAnimationView::SetupLogicalPalette() { struct { WORD VersionNumber; WORD NumberOfEntries; PALETTEENTRY PalEntryArr[256]; } MyPalette = { 0x300, 256}
// Code for setting RGB values in // each of the 256 PALETTEENTRY structure of // MyPalette goes here. I have not included
CSWL Inc, Pleasanton, California - 32 - www.cswl.com
// it for it is too lengthy and doesn't // convey much. But If you get the following // message box, you will have to add it here.
AfxMessageBox(" Add colors to the Logical palette");
m_hPalette = CreatePalette ((LOGPALETTE*) &MyPalette);
}
void COGLAnimationView::DrawCube() {
//Since cube is not a primitive, we draw each face using OpenGL //Note that vertices are wound in Counter ClockWise Direction and //The Direction of the normal is out ward
double v1[3],v2[3],v3[3],n[3];
//front face
v1[0]=-0.5f;v1[1]=0.5f;v1[2]=0.5f; v2[0]=-0.5f;v2[1]=-0.5f;v2[2]=0.5f; v3[0]=0.5f;v3[1]=-0.5f;v3[2]=0.5f; CalcNormal(v1,v2,v3,n); glBegin(GL_POLYGON); glNormal3f((GLfloat)n[0],(GLfloat)n[1],(GLfloat)n[2]); glVertex3f(-0.5f,0.5f,0.5f); glVertex3f(-0.5f,-0.5f,0.5f); glVertex3f(0.5f,-0.5f,0.5f); glVertex3f(0.5f,0.5f,0.5f);
CSWL Inc, Pleasanton, California - 33 - www.cswl.com
glEnd();
//right face
v1[0]=0.5f;v1[1]=0.5f;v1[2]=0.5f; v2[0]=0.5f;v2[1]=-0.5f;v2[2]=0.5f; v3[0]=0.5f;v3[1]=-0.5f;v3[2]=-0.5f; CalcNormal(v1,v2,v3,n); glBegin(GL_POLYGON); glNormal3f((GLfloat)n[0],(GLfloat)n[1],(GLfloat)n[2]); glVertex3f(0.5f,0.5f,0.5f); glVertex3f(0.5f,-0.5f,0.5f); glVertex3f(0.5f,-0.5f,-0.5f); glVertex3f(0.5f,0.5f,-0.5f);
glEnd();
//back face
v1[0]=0.5f;v1[1]=0.5f;v1[2]=-0.5f; v2[0]=0.5f;v2[1]=-0.5f;v2[2]=-0.5f; v3[0]=-0.5f;v3[1]=-0.5f;v3[2]=-0.5f; CalcNormal(v1,v2,v3,n); glBegin(GL_POLYGON); glNormal3f((GLfloat)n[0],(GLfloat)n[1],(GLfloat)n[2]); glVertex3f(0.5f,0.5f,-0.5f); glVertex3f(0.5f,-0.5f,-0.5f); glVertex3f(-0.5f,-0.5f,-0.5f); glVertex3f(-0.5f,0.5f,-0.5f);
CSWL Inc, Pleasanton, California - 34 - www.cswl.com
glEnd();
//left face
v1[0]=-0.5f;v1[1]=0.5f;v1[2]=-0.5f; v2[0]=-0.5f;v2[1]=-0.5f;v2[2]=-0.5f; v3[0]=-0.5f;v3[1]=-0.5f;v3[2]=0.5f; CalcNormal(v1,v2,v3,n); glBegin(GL_POLYGON); glNormal3f((GLfloat)n[0],(GLfloat)n[1],(GLfloat)n[2]); glVertex3f(-0.5f,0.5f,-0.5f); glVertex3f(-0.5f,-0.5f,-0.5f); glVertex3f(-0.5f,-0.5f,0.5f); glVertex3f(-0.5f,0.5f,0.5f); glEnd();
//bottom face
v1[0]=-0.5f;v1[1]=-0.5f;v1[2]=0.5f; v2[0]=-0.5f;v2[1]=-0.5f;v2[2]=-0.5f; v3[0]=0.5f;v3[1]=-0.5f;v3[2]=-0.5f; CalcNormal(v1,v2,v3,n); glBegin(GL_POLYGON); glNormal3f((GLfloat)n[0],(GLfloat)n[1],(GLfloat)n[2]); glVertex3f(-0.5f,-0.5f,0.5f); glVertex3f(-0.5f,-0.5f,-0.5f); glVertex3f(0.5f,-0.5f,-0.5f); glVertex3f(0.5f,-0.5f,0.5f);
glEnd();
CSWL Inc, Pleasanton, California - 35 - www.cswl.com
//top face
v1[0]=-0.5f;v1[1]=0.5f;v1[2]=-0.5f; v2[0]=-0.5f;v2[1]=0.5f;v2[2]=0.5f; v3[0]=0.5f;v3[1]=0.5f;v3[2]=0.5f; CalcNormal(v1,v2,v3,n); glBegin(GL_POLYGON); glNormal3f((GLfloat)n[0],(GLfloat)n[1],(GLfloat)n[2]); glVertex3f(-0.5f,0.5f,-0.5f); glVertex3f(-0.5f,0.5f,0.5f); glVertex3f(0.5f,0.5f,0.5f); glVertex3f(0.5f,0.5f,-0.5f); glEnd(); }
void COGLAnimationView::DrawNextFrame() {
m_Angle += 10.0f; // increment the angle of rotations of the cubes CClientDC cdc(this); wglMakeCurrent(cdc.m_hDC,m_hRC); //Make the RC current
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear the Color and the Depth buffers glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); //Initialize matrix glTranslatef(0.0, 0.0,-4.0); //translate both cubes and the light into
CSWL Inc, Pleasanton, California - 36 - www.cswl.com
//Viewing frustum
glPushMatrix(); //Draw the Green Cube
// Set current material properties glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,ambmat1); glMaterialfv(GL_FRONT,GL_SPECULAR,specmat1);
// Apply transformations specific to this cube
glTranslatef(-2.0, 0.0,0.0); //translate to left glRotatef(m_Angle,0.0,1.0,0.0); //rotate about local Y axis
// Define the primitives forming the cube
DrawCube();
glPopMatrix();
glPushMatrix(); //Draw the Red Sphere
// Set current material properties glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,ambmat2); glMaterialfv(GL_FRONT,GL_SPECULAR,specmat2);
// Apply transformations specific to this cube
glTranslatef(0.0f,0.0f ,-6.0f);
CSWL Inc, Pleasanton, California - 37 - www.cswl.com
//the light is above the Red Sphere and directed downward float co=160.0; //Cut Off Angle
//Pass on the light properties to OpenGL
glLightfv(GL_LIGHT1,GL_AMBIENT,amblight1); glLightfv(GL_LIGHT1,GL_DIFFUSE,difflight1); glLightfv(GL_LIGHT1,GL_SPECULAR,speclight1); glLightfv(GL_LIGHT1,GL_POSITION,poslight1); glLightfv(GL_LIGHT1,GL_SPOT_CUTOFF,&co); glLightfv(GL_LIGHT1,GL_SPOT_DIRECTION,dirlight1);
glEnable(GL_LIGHT1); // Turn On the Light
glRotatef(m_Angle,1.0,0.0,0.0); //rotate the cube about // local X axis DrawCube();
glPopMatrix();
glFlush(); // Make sure all the commands //are executed SwapBuffers(cdc.m_hDC); //Swap the buffers wglMakeCurrent(NULL,NULL); //Make the RC not-current
}
CSWL Inc, Pleasanton, California - 38 - www.cswl.com
PERFORMANCE TIPS
Here are some of the general tips that can improve the performance of your OpenGL applications. I have not applied them to the above code to keep it simple and readable.
1. Always use triangles, triangle strips and perhaps triangle fans as your primitives. Other primitives such as polygons and quads are decomposed into triangles before rasterization. So if the same model is to be drawn multiple times, considerable time may be lost decomposing the primitives each time it is drawn.
2. Rather than drawing independent lines and triangles with distinct glBegin()- glEnd() pairs, use the GL_LINES, GL_TRIANGLES, and GL_QUADS tokens to specify as many vertices as possible between glBegin()-glEnd() pairs.
3. For any 3D object, we can see that some edges and vertices are common for two or more faces. In OpenGL, while constructing the Object with its faces as primitives, there is no guarantee that the shared edge will share the same pixels since the two edges may be rasterized differently. This may result in formation of narrow cracks along the edges while animating the object. In order to avoid the problem, shared edges should share the same vertex coordinates so that the edge equations are the same.
4. Similarly, all the coplanar Faces of the object should share the same Normal for proper lighting effect.
5. If a surface is intended to be closed, it should share the same vertex coordinates where the surface specification starts and ends.
6. Use unit-length Normals instead of using GL_NORMALIZE.
CSWL Inc, Pleasanton, California - 39 - www.cswl.com
7. Performance can be improved by avoiding unnecessary state changes. Avoid redundantly re-specify the color or material for each line and polygon, if it has not changed since the last line or polygon.
8. Beware of invalid data like Zero-length vectors and NaN (Not a Number) colors and coordinates which can slow down your application considerably.
9. Use Flat shading wherever smooth shading isn't required.
10. Use display lists to encapsulate the rendering of objects that will be drawn repeatedly.
11. Use specific matrix calls such as glLoadIdentity() glRotate, glTranslate() and glScale(), rather than composing your own rotation, translation, and scale matrices and calling glMultMatrix().
GET LITTLE HELP FROM OPENGL UTILITY LIBRARY
The OpenGL core API is dedicated to the basic rendering functionality. The Utility Library assists you in performing many tasks that are common in graphics. The prefix for Utility library functions is "glu". Utility Library helps you in
Transforming coordinates Performing Selection and feedback from an OpenGL screen Tessellating polygons OpenGL renders only convex polygons whereas your polygon may be concave and may even contain holes. You can use the Utility functions to tessellate the polygon into convex polygons. Manipulating images for texture applications
CSWL Inc, Pleasanton, California - 40 - www.cswl.com
The dimensions of the images used for texture mapping in OpenGL should be a power of two. If your texture has some arbitrary dimensions, you can use Utility functions to scale the image to a nearest power of two. Rendering canonical shapes like spheres, cylinders and disks, which you will need extensively. Support for Non-uniform rational B-spline (NURBS) curves and surfaces Error reporting
Clearly knowing how to use the Utility Library is as important as learning OpenGL.
CONCLUSION
As you may have guessed, there is still a reasonably large learning curve to take full advantage of OpenGL. Even though 'Texture mapping' is commonly used and a handy feature of OpenGL, lack of time and space didn't allow me to include it in this article. A lot of experimentation on the sample code given above can give you a better insight of OpenGL transformations.Exhaustive helps are available at www.OpenGL.org and www.sgi.com.
APPENDIX A
PIXELFORMATDESCRIPTOR structure
The members, their possible values and explanations are given below
Member Possible Values Description Size of the PIXELFORMATDESCRIPTOR Nsize sizeof( PIXELFORMATDESCRIPTOR) Structure Version number of the NVersion 1 structureCurrently, 1 DwFlags PFD_DRAW_TO_WINDOW Set this flag if you want to draw the output to a window
CSWL Inc, Pleasanton, California - 41 - www.cswl.com
Set this flag if you want to draw PFD_DRAW_TO_BITMAP the output to memory bitmap.
Enables double buffering. Set this flag if your output is going to be PFD_DOUBLEBUFFER animated. These two flags can be used to identify the underlying OpenGL driver model implemented in the system. If both the flags are not set, then it's a fully hardware accelerated card running an ICD PFD_GENERIC_FORMAT (Installable Client Driver). This is the fastest implementation. If both the flags are set, then it's a partially accelerated card running a MCD (Mini Client Driver). This is the second fastest and most common implementation. If PFD_GENERIC_FORMAT alone is set, then it's a generic software PFD_GENERIC_ACCELERATED driver.
PFD_NEED_PALETTE If set, a logical palette is needed
If set, the system uses OpenGL hardware that supports only one PFD_NEED_SYSTEM_PALETTE hardware palette Enables buffers for holding the left and right eye parallaxes of the same image. Not supported PFD_STEREO under NT.
Set this flag if you want to use the familiar GDI drawing functions. Cannot be used when PFD_SUPPORT_GDI double buffering is enabled.
If set, the buffer supports PFD_SUPPORT_OPENGL OpenGL drawing functions IpixelType PFD_TYPE_RGBA Color coding in RGBA (Red- Green-Blue-Alpha). The Alpha values are used in blending. Running in this mode allows OpenGL to render the output more realistically.
CSWL Inc, Pleasanton, California - 42 - www.cswl.com
Color coding in the familiar color index-palette mode. Use this mode only if you are running in less than 15-bit color and you are not using blending, lighting, PFD_TYPE_INDEX texture mapping etc.
Number of bits used to represent CcolorBits 8, 16, 24 etc a color (color depth).
Number of red bits in the RGBA CredBits 0 will do for normal applications buffer. Valid only in RGBA mode
The shift count for red bits in the RGBA color buffer Valid only in CredShift 0 will do for normal applications RGBA mode
Number of Green bits in the RGBA buffer Valid only in RGBA CgreenBits 0 will do for normal applications mode
The shift count for Green bits in the RGBA color buffer Valid only CgreenShift 0 will do for normal applications in RGBA mode
Number of Blue bits in the RGBA CblueBits 0 will do for normal applications buffer Valid only in RGBA mode
The shift count for Blue bits in the RGBA color buffer Valid only in CblueShift 0 will do for normal applications RGBA mode
Number of Alpha bits in the RGBA CalphaBits 0 will do for normal applications buffer. Valid only in RGBA mode
The shift count for Alpha bits in the RGBA color buffer Valid only CalphaShift 0 will do for normal applications in RGBA mode
Number of bits per pixel in the CaccumBits 0 will do for normal applications accumulation buffer
Number of Greenbits per pixel in CaccumGreenBits 0 will do for normal applications the accumulation buffer
Number of Blue bits per pixel in CaccumBlueBits 0 will do for normal applications the accumulation buffer
Number of Alpha bits per pixel in CaccumAlphaBits 0 will do for normal applications the accumulation buffer
Number of bits per pixel in the CdepthBits 16, 32 etc Depth buffer
CSWL Inc, Pleasanton, California - 43 - www.cswl.com
Number of bits per pixel in the CstencilBits 0 will do for normal applications Stencil buffer
Number of bits per pixel in the Auxiliary buffer. Not supported CauxBuffers 0 under Windows NT
Specifies the layer type. Only PFD_MAIN_PLAIN is supported IlayerType PFD_MAIN_PLAIN under NT
Breserved 0 Not supported under Windows NT
DwLayerMask 0 Not supported under Windows NT
DwVisibleMask 0 Not supported under Windows NT
DwDamageMask 0 Not supported under Windows NT
------Copyright Notice:
2002 California Software Labs. All rights Reserved. The contents on the document are not to be reproduced or duplicated in any form or kind, either in part or full, without the written permission of California Software labs. Product and company names mentioned here in are the trademarks of their respective companies.
CSWL Inc, Pleasanton, California - 44 -