Drawing and Printing OpenGL Graphics Using Device-Independent Bitmaps
2014-07-11 15:12
483 查看
Overview
The OpenGL API has been available to PC programmers who wanted to display rendered 3D graphics since Windows NT 3.51 came out in the mid-1990s. It's a terrific platform for 3D visualization for two primary reasons: 1) it exposes some very advanced rendering features, and 2) it is very easy to use. You might be thinking: "Easy to use???" Well, it is, and this article is intended to be a short primer for someone new to OpenGL programming on the PC. Normally, a window receives the output from the OpenGL library, using a double-buffer approach. This article shows how to send the OpenGL graphics output directly to a DIB (device-independent bitmap) section. The decision on whether to draw to a DIB or a window depends on your application; however, drawing straight to a DIB has several convenient advantages: mouse selection of 3D objects is simplified, printing high resolution graphics becomes trivial, the DIB can be displayed on any window (dialog, view, and so forth), and it allows you to use image processing techniques after OpenGL renders the 3D image. Look and you will find an infinite number of articles and books on how to do 3D programming, with and without graphics libraries such as OpenGL, and how to optimize your 3D code for speed. This article takes a slightly different track and assumes you have some idea of how to do 3D programming and you want to learn to use the OpenGL API effectively.The following steps allow you to render 3D graphics to a DIB:Create a DIB section. It can't be a regular bitmap; OpenGL requires that it be a DIB Section.Initialize the OpenGL library and connect the library with the new DIB.
Execute the drawing code.
Render the DIB to the screen or to the printer.
That's it. You're drawing 3D graphics directly into an image!
Creating a DIB Section
A simple call to CreateDIBSection sets up the image for drawing. In the sample project, the DIB section is encapsulated within the DIBSection class to make things a little easier.Initialize the OpenGL library and connect the library with the new DIB
The OpenGL API is a C-style interface that operates like a state engine. Think of it like a car. You start it up, and then you put it in gear and give it commands. When you issue commands to the OpenGL engine, its state determines how those commands are implemented. Using the car analogy, what happens when you step on the gas depends very much on whether you are in first gear or reverse. For initialization, call the following OpenGL functions: SetPixelFormat, wglCreateContext, wglMakeCurrent. The key for drawing directly to a DIB is the SetPixelFormat call: for the library to play nice with a DIB section you have to set the pixel descriptor flags to the following: PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL |PFD_SUPPORT_GDI Next, make the following calls to set up how you want your 3D model to look: glViewport, glMatrixMode, glPolygonMode, and glShadeModel. Now OpenGL should be ready to go.Execute the drawing code:
This section is intended to give you a quick idea of how to use the drawing calls. Complex drawings are typically built from lots of "primitive" drawing commands. For example, if you want to draw the surface of a rectangular grid, you may want to break the surface in to (flat) triangles. To accomplish this, you have to first tell the library that you are sending it triangle vertices, and when you are finished you call glEnd to tell the library you are no longer sending vertices:glBegin(GL_TRIANGLES); glDouble v1[3]; glDouble v2[3]; glDouble v3[3]; glDouble x, y, z; for (int row = 0; row < (GRID_ROWS-1); row++){ for (int col = 0; col < (GRID_COLUMNS-1); col++){ // get some x, y, and z values x = (glDouble)row; y = (glDouble)col; z = (glDouble)((row%2) + (col%3)); v1[0] = x; v1[2] = y; v1[1] = z; glVertex3dv(v1); z = (glDouble)((row%3) + (col%2)); v2[0] = x; v2[2] = y + 1; v2[1] = z; glVertex3dv(v2); z = (glDouble)((row%4) + (col%3)); v3[0] = x+1; v3[2] = y+1; v3[1] = z; glVertex3dv(v3); z = (glDouble)((row%3) + (col%2)); v1[0] = x; v1[2] = y; v1[1] = z; glVertex3dv(v1); z = (glDouble)((row%3) + (col%4)); v2[0] = x + 1; v2[2] = y; v2[1] = z; glVertex3dv(v2); z = (glDouble)((row%2) + (col%3)); v3[0] = x+1; v3[2] = y+1; v3[1] = z; glVertex3dv(v3); } } glEnd();The same approach is used for drawing lines, for example:
Also, the API uses a naming convention for its function calls that helps programmers keep the parameters straight. For example, the call to glVertex3dv actually has many variations:glVertex3fv—passing in 3 glFloat typesglBegin(GL_LINES); // send the API pairs of vertices by calling glVertex3dv(v) // as many times as necessary glEnd();A slightly different approach is necessary for drawing polygons because there is no way for the API to know ahead of time how many sides the polygons are going to have. So, you have to nest the glBegin(GL_POLYGON) and glEnd() within the polygon loop.
glVertex3di—passing in 3 glInt types
glVertex3dv—passing in 3 glDouble types...and so on
Render the DIB to the screen or to the printer:
Create a CClientDC (MFC) for the window you wish to show the graphics in and transfer the image to the window using bitblt. In the sample project, this is accomplished by using the DIBSection object. The following code snippet (from the sample project) shows some of the implementation of the OpenGL view class.GLView3D::GLView3D(void) { InitImage(); } GLView3D::~GLView3D(void) { } void GLView3D::CreateImage(UINT32 width, UINT32 height) { if ((width > 0) && (height > 0)) { m_dib.Create(width,height,32); if (!IsInitialized()) { InitializeOpenGL(); if (CreateView()) { m_IsInitialized = 1; } } } } void GLView3D::SizeImage(UINT32 width, UINT32 height) { if ((width > 0) && (height > 0)) { if (!IsInitialized()) { CreateImage(width,height); } else { if (!m_dib.IsCreated()) { m_dib.Create(width,height,32); if (SetImagePixelFormat(*m_dib.GetDC())) { CreateImageGLContext(*m_dib.GetDC()); } } else if ((width != m_dib.Width()) || (height != m_dib.Height())) { m_dib.Create(width,height,32); CreateView(); } } glViewport(0,0,width,height); double aspect; aspect = (height == 0) ? (double)width : (double)width/(double)height; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45,aspect,0.01,25.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } } void GLView3D::InitializeOpenGL(void) { m_hGLContext = NULL; m_GLPixelIndex = 0; InitGeometry(); } void GLView3D::InitGeometry(void) { m_model_matrix.SetRotation(45.0,0.0,0.0); m_model_matrix.SetScale(1.0,1.0,1.0); m_model_matrix.SetPosition(0.0,0.0,-5.0); } // Lights, camera, action... int GLView3D::CreateView(void) { int result = 0; // do the standard OpenGL initialization if(SetImagePixelFormat(*m_dib.GetDC()) && CreateImageGLContext(*m_dib.GetDC())) result = 1; else return result; glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); glShadeModel(GL_SMOOTH); glEnable(GL_NORMALIZE); glEnable(GL_COLOR_MATERIAL); // Lights, material properties GLfloat ambientProperties[] = {0.3f, 0.3f, 0.3f, 0.7f}; GLfloat diffuseProperties[] = {0.9f, 0.9f, 0.9f, 1.0f}; GLfloat specularProperties[] = {0.7f, 0.7f, 0.7f, 1.0f}; GLfloat mat_amb_diff[] = { 0.7f, 0.7f, 0.7f, 1.0f}; GLfloat shine[] = { 128.0f}; GLfloat emissionProperties[] = {0.2f, 0.2f, 0.2f, 1.0f}; glLightfv( GL_LIGHT0, GL_AMBIENT, ambientProperties); glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseProperties); glLightfv( GL_LIGHT0, GL_SPECULAR, specularProperties); glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.0); // lighting glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); return result; } BOOL GLView3D::SetImagePixelFormat(HDC hDC) { PIXELFORMATDESCRIPTOR pixelDesc; pixelDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR); pixelDesc.nVersion = 1; pixelDesc.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_SUPPORT_GDI; pixelDesc.iPixelType = PFD_TYPE_RGBA; pixelDesc.cColorBits = 32; pixelDesc.cRedBits = 0; pixelDesc.cRedShift = 0; pixelDesc.cGreenBits = 0; pixelDesc.cGreenShift = 0; pixelDesc.cBlueBits = 0; pixelDesc.cBlueShift = 0; pixelDesc.cAlphaBits = 0; pixelDesc.cAlphaShift = 0; pixelDesc.cAccumBits = 0; pixelDesc.cAccumRedBits = 0; pixelDesc.cAccumGreenBits = 0; pixelDesc.cAccumBlueBits = 0; pixelDesc.cAccumAlphaBits = 0; pixelDesc.cDepthBits = 32; pixelDesc.cStencilBits = 0; pixelDesc.cAuxBuffers = 0; pixelDesc.iLayerType = PFD_MAIN_PLANE; pixelDesc.bReserved = 0; pixelDesc.dwLayerMask = 0; pixelDesc.dwVisibleMask = 0; pixelDesc.dwDamageMask = 0; int pix_index = ChoosePixelFormat(hDC,&pixelDesc); if(!SetPixelFormat(hDC,pix_index,&pixelDesc)) { DWORD code = GetLastError(); return FALSE; } return TRUE; } BOOL GLView3D::CreateImageGLContext(HDC hDC) { m_hGLContext = wglCreateContext(hDC); if(m_hGLContext==NULL) return FALSE; if(wglMakeCurrent(hDC,m_hGLContext)==FALSE) return FALSE; return TRUE; } void GLView3D::RenderImage(void) { if (m_IsInitialized && m_dib.IsCreated()) { glClearColor((float)1.00,(float)1.00, (float)1.00,1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // put a working copy on the matrix stack glPushMatrix(); // Position / translation / scale glTranslated(m_model_matrix.X(), m_model_matrix.Y(), m_model_matrix.Z()); glRotated(m_model_matrix.XRot(), 1.0, 0.0, 0.0); glRotated(m_model_matrix.YRot(), 0.0, 1.0, 0.0); glRotated(m_model_matrix.ZRot(), 0.0, 0.0, 1.0); glScaled(m_model_matrix.XScale(), m_model_matrix.YScale(), m_model_matrix.ZScale()); // build the scene...if necessary RenderPrimaryImage(); glFlush(); // pop the working copy of the matrix stack glPopMatrix(); } } void GLView3D::RenderPrimaryImage(void) { glColor3ub(128,128,128); GLUquadricObj * q = gluNewQuadric(); gluQuadricDrawStyle(q, GLU_LINE); gluQuadricNormals(q, GLU_SMOOTH); gluSphere(q,1.0,16,16); gluDeleteQuadric(q); GLdouble v1[3], v2[3]; glBegin(GL_LINES); v1[0] = v1[1] = v1[2] = 0.0; v2[0] = 1.0; v2[1] = v2[2] = 0.0; glColor3ub(224,0,0); glVertex3dv(v1); glVertex3dv(v2); v2[0] = 0.0; v2[1] = 1.0; v2[2] = 0.0; glColor3ub(0,224,0); glVertex3dv(v1); glVertex3dv(v2); v2[0] = 0.0; v2[1] = 0.0; v2[2] = 1.0; glColor3ub(0,0,224); glVertex3dv(v1); glVertex3dv(v2); glEnd(); } void GLView3D::DrawImage(CWnd * wnd) { CClientDC dc(wnd); m_dib.Draw(&dc,0,0); }
Some Information about the Sample Project
This article is NOT intended to be the final word on OpenGL programming. You could probably spend a career on OpenGL and 3D programming and still not know everything. The sample project has printing code within the view class's "OnDraw" method.Enjoy.相关文章推荐
- GDI Graphics In Delphi(Drawing Bitmaps, the TBitmap object )
- Unix Sed Tutorial: Printing File Lines using Address and Patterns
- OpenGL学习脚印: 光照中材质和lighting maps使用(material and lighting maps)
- Build fat static library (device + simulator) using Xcode and SDK 4+
- algorithm@ Divide two integers without using multiplication, division and mod operator. (Bit Operation)
- OpenGL学习脚印:立方体纹理和天空包围盒(Cubemaps And Skybox)
- Creating Mobile Games: Using Java ME Platform to Put the Fun into Your Mobile Device and Cell Phone
- Chimera HD Graphics Bootflags: IGPEnabler, IGPlatformID, and IGPDeviceID
- under ubuntu, using usb and adb connect device
- Test OpenGL and OpenVG performance; Game using OpenGL
- how to share register and bit field definitions between a device driver and the FPGA it controls
- WPF: drawing a video using MediaPlayer, VideoDrawing and DrawingBrush
- OpenGL: Rotation vector sensor of Android and Device motion of iOS
- Android bitmaps and out of memory errors
- Web Development: Tutorial on Printing and Traversing DOM Using JavaScript
- NeHe OpenGL Lesson09 - Moving Bitmaps in 3D Space
- Flicker free drawing using GDI+ and C#
- IT English Collection(31)of About Drawing and Printing in iOS
- Chapter 7. Advanced DirectDraw and Bitmapped Graphics
- vulakn教程--Drawing a Triangle--Set up--Physical Device and Queue Family