奇谭怪剑

“奇谭怪剑”————奇谈怪见是也!你想说什么就说什么;你想知道什么就能知道什么;你想要什么就有什么;来吧朋友————天剑这儿应有尽有!..........*^-^*..........

881 成员697 主题2,175 回复0 张照片

在Windows 9x/NT下用OpenGL编程

天
天 剑
2000-05-10 23:26


在Windows 9x/NT下用OpenGL编程



---- 科学计算可视化,计算机动画和虚拟现实是现在计算机图形学的三个热点。而这三个热点的核心都是三维真实感图形的绘制。由于OpenGL(Open Graphics Library)具有跨平台性、简便、高效、功能完善,目前已经成为了三维图形制作方法中事实上的工业标准。自从Windows NT 3.51 在微机平台上支持OpenGL以后,现在微软公司在Windows 95 OSR2、Windows NT 4.0 中连续性的提 供OpenGL开发环境。Visual C++从4.2 版本以后已经完全支持OpenGL API,使三维世界的“平民化”已成为必然。
Windows操作系统对OpenGL的支持
----具有Windows编程经验的人都知道,在Windows下用GDI作图必须通过设备上下 文(Device Context 简 写DC)调用相应的函数;用OpenGL作图也是类似,OpenGL 函数是通过“ 渲染上下文”(Rendering Context 简写RC)完成三维图形的绘制。 Windows下的窗口和设备上下文支持“位图格式”(PIXELFORMAT)属性,和 RC有着位图结构上的一致。只要在创建RC时与一个DC建立联系(RC也只能 通过已经建立了位图格式的DC来创建),OpenGL的函数就可以通过RC对应的 DC画到相应的显示设备上。这里还有以下需要注意的方面:
---- 1.一个线程只能拥有一个渲染上下文(RC),也就是说,用户如果在一个 线程内对不同设备作图,只能通过更换与RC对应的DC来完成,而RC在线程中 保持不变。(当然,删除旧的RC后再创建新的是可以的)与此对应,一个RC也 只能属于一个线程,不能被不同线程同时共享。
---- 2.设定C位图格式等于设定了相应的窗口 的位图格式,并且DC和窗口的位图格式一旦确定就不能再改变。这一点只能期望 以后的 Windows版本改进了。
---- 3.一个RC虽然可以更换DC,在任何时刻只能利用一个DC(这个DC称为RC的当前DC),但由于一个窗口可以让多个DC作图,从而可以让多个线程利用多个RC在该窗口上执行OpenGL操作。
---- 4.现在的 Windows 下的OpenGL版本对OpenGL和GDI在同一个DC上作图有一定的限制。当使用双缓存用OpenGL产生动画时,不能使用GDI函数向该DC作图。
---- 5.不建议用ANSI C在Windows下编写OpenGL程序。这样的程序虽然具有跨平台的可移植性(比如很多SGI的例子程序),但是它们不能利用Windows操作系统的很多特性,实用价值不大。

用VC来编写OpenGL程序
经过上面的分析,用VC来调用OpenGL作图的方法就很显然了。步骤如下:
---- 1.先设置显示设备DC的位图格式 (PIXELFORMAT)属性。这通过填充一个 PIXELFORMATDESCRIPTOR的结构来完成(关于PIXELFORMATDESCRIPTOR中各 项数据的意义,请参照VC的帮助信息),该结构决定了OpenGL作图的物 理设备的属性,比如该结构中的数据项dwFlags中PFD_DOUBLEBUFFER位如 果没有设置(置1),通过该设备的DC上作图的OpenGL命令就不可能使用双缓冲 来做动画。有一些位图格式(PIXELFORMAT)是DC支持的,而有一些DC就不支持了。 所以程序必须先用ChoosePixelFormat来选择DC所支持的与指定位图格式最接近的位 图格式,然后用SetPixelFormat设置DC的位图格式。
---- 2.利用刚才的设备DC建立渲 染上下文RC(wglCreateContext),使得RC与DC建立联系(wglMakeCurrent)。
---- 3.调用OpenGL函数作图。由于线程 与RC一一对应,OpenGL 函数的参数中都不指明本线程RC的句柄(handle)
---- 4.作图完毕以后,先通过置当前 线程的RC为NULL(::wglMakeCurrent(NULL,NULL);),断开当前线程 和该渲染上下文的联系,由此断开与DC的联系。此时RC句柄的有效性在微软自己 的文档中也没有讲清楚,所以在后面删除RC的时候要先判断以下RC句柄的有效性 (if (m_hrc) ::wglDeleteContext(m_hrc);)。再根据情况释放(ReleaseDC) 或者删除(DeleteDC)DC

所附程序说明:
所附的程序用MFC完成了一个简单的OpenGL作图,用OpenGL的辅助库画了一个有 光照的实心圆球。OpenGL本身的函数这里就不解释了,仅对用MFC编OpenGL时需要 注意的内容做一个简要的说明:
---- 1.一旦设定了一个DC的位图格 式,该DC所联系的窗口的位图格式随之设定。该窗口 若含有子窗口或者有兄弟窗口,这些兄弟/子窗口的 位图格式没有设成与对应RC一致的格式,OpenGL在它们上 面作图就容易出错。故而OpenGL作图的窗口必须具有WS_CLIPCHILDREN 和 WS_CLIPSIBLINGS 风格,程序中在主框窗的构造函数中用
LoadFrame(IDR_MAINFRAME,
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,NULL,NULL );
指定了主窗口的风格。
---- 2.在ANSI C 的OpenGL编程中,由auxReshapeFunc定 义设置OpenGL视口大小和作图尺寸的回调函数。在MFC中应该由WM_SIZ消息的处理函 数来完成。在ANSI C 的OpenGL编程中,由EauxMainLoop定义作图的回调函数。 在MFC中应该由WM_PAINT消息的处理函数来处理。相应的,由OpenGL定义的键盘、 鼠标处理函数都应该由相应的Windows处理函数来响应。
---- 3. OpenGL 自己有刷新背景的函数glClear, 故而应禁止Windows刷新窗口背景。否则,当窗口需要重画时,Windows 会自动先 发送WM_ERASEBKGND,而缺省的处理函数使用白色的背景刷。当OpenGL使用的背景 颜色不是白色时,作图时有一帧白色的闪烁。这种现象在做动画时特别明显。程序 中只需要在WM_ERASEBKGND的消息处理函数中禁止父窗口类的消息处理,简单的返回 一个TRUE即 可。
---- 4.由于OpenGL的跨平台性,它必须 用操作系统的调色板。所以如果GL_INDEX_MODE作图时,必须 用VC自己定义调色板。不过一般情况下,用GL_RGBA_MODE模 式比较方便,很少用到GL_INDEX_MODE模式。
---- 5.在OpenGL作图期间,RC对应的DC不 能删除或者释放。
---- 6.由于OpenGL作图时需要长时间占 用DC,所以最好把作图窗口类设成CS_OWNDC。MFC缺省的窗口 类风格中没有设这一属性,程序中在主窗口C++类的PreCreateWindow 方法中自己注册了一个窗口类,除了设定了CS_OWNDC属性 以外,还设定了CS_HREDRAW、CS_VREDRAW和CS_SAVEBITS。设定CS_HREDRAW、CS_VREDRAW 是为了让窗口缩放时产生WM_PAINT消息,修正OpenGL视口和 作图尺寸;由于OpenGL作图需要很多计算,设定CS_SAVEBITS是 为了在OpenGL窗口被遮盖后显现出来时,不产生WM_PAINT消 息,用内存存储的图象来填充,从而用空间消耗换取 计算时间。
---- 7.本程序中没有对OpenGL函数的出 错情况作出处理。OpenGL出错后返回错误码,不会抛出 异常;而且在某一个函数出错以后,后继函数也一般 不会出现异常,只是返回错误码,一不小心就可能忽 略某些错误。而对每一个OpenGL函数都做出错与否的判 断比较麻烦,所以编程序时对OpenGL的函数应当非常小心。
附程序:

---- 程序运行时必须确定OpenGL32.dll、glu.dll、glaux.dll 在Windows 的System目录下。如果 找不到这些文件,可以从Windows 95 OSR2 的机器上面将这些文件拷贝过来即可。OpenGL运行不需 要注册库信息。在VC的STUDIO中运行程序时,工程文件中必须加入OpenGL.h 、glu.h、glaux.h 以 及OpenGL.lib、glu.lib、glaux.lib,这些文件由VC自带。

主窗口类定义(OpenGLWnd.h):
#if !defined(AFX_OPENGLWND_H__3FB1AB28
_0E70_11D2_9ACA_48543300E17D__INCLUDED_)
#define AFX_OPENGLWND_H__3FB1AB28_0E70_11D2
_9ACA_48543300E17D__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#include
#include "SimpleGLApp.h"
#include "resource.h"
// OpenGLWnd.h : header file
//
/////////////////////////////////////////////////////////////////////////////
// COpenGLWnd frame

class COpenGLWnd : public CFrameWnd
{
DECLARE_DYNCREATE(COpenGLWnd)
public:
COpenGLWnd();// protected constructor used by dynamic creation
protected:
HGLRC m_hrc;
CClientDC *m_pDC;
// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(COpenGLWnd)
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~COpenGLWnd();

// Generated message map functions
//{{AFX_MSG(COpenGLWnd)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnDestroy();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional
declarations immediately before the previous line.

#endif // !defined(AFX_OPENGLWND_H__3FB1AB28
_0E70_11D2_9ACA_48543300E17D__INCLUDED_)
主 窗 口 类 的 实 现(OpenGLWnd.cpp):
// OpenGLWnd.cpp : implementation file
//

#include "stdafx.h"
#include "OpenGLWnd.h"
#include "SimpleGLApp.h"
#include "gl\glu.h"
#include "gl\gl.h"
#include "gl\glaux.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// COpenGLWnd

IMPLEMENT_DYNCREATE(COpenGLWnd, CFrameWnd)

COpenGLWnd::COpenGLWnd()
{
m_pDC = NULL;
m_hrc = 0;
LoadFrame(IDR_MAINFRAME,WS
_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS
,NULL,NULL );
}

COpenGLWnd::~COpenGLWnd()
{
}


BEGIN_MESSAGE_MAP(COpenGLWnd, CFrameWnd)
//{{AFX_MSG_MAP(COpenGLWnd)
ON_WM_CREATE()
ON_WM_SIZE()
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()



BOOL COpenGLWnd::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Add your specialized code here and/or call the base class

cs.lpszClass = AfxRegisterWndClass( CS_DBLCLKS |
CS_HREDRAW |
CS_VREDRAW |
CS_SAVEBITS |
CS_NOCLOSE |
CS_OWNDC
, AfxGetApp( )->LoadStandardCursor(IDC_ARROW), 0 ,
AfxGetApp( )
- >LoadStandardIcon(IDI_APPLICATION));
return CFrameWnd::PreCreateWindow(cs);
}


int COpenGLWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;

int pixelformat;

m_pDC = new CClientDC(this);// 在 客 户 区 作 图
ASSERT(m_pDC != NULL);

static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // 固定值
1, // 固定值
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_TYPE_RGBA, // RGBA 模式,不用调色板
16, // 程序在16位色彩下运行
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};


if ( (pixelformat = ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) == 0 )
{
MessageBox("在该DC上找不到与PFD接近的位图结构");
return -1;
}

if (SetPixelFormat(m_pDC->GetSafeHdc(), pixelformat, &pfd) == FALSE)
{
MessageBox("无法在该DC上设置位图结构");
return -1;
}
m_hrc = wglCreateContext(m_pDC->GetSafeHdc());
wglMakeCurrent(m_pDC->GetSafeHdc(), m_hrc);

glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);


glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);

return 0;//OpenGL 窗口构造成功
}

void COpenGLWnd::OnSize(UINT nType, int cx, int cy)
{
CFrameWnd::OnSize(nType, cx, cy);

// TODO: Add your message handler code here
if(cy > 0)
{
glViewport(0, 0, cx, cy);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (cx <= cy) glOrtho(-3.0,3.0,-3.0 * (GLfloat)cx/(GLfloat)cy, 3.0 * (GLfloat)cx/(GLfloat)cy,-3.0,3.0); else glOrtho(-3.0,3.0,-3.0 * (GLfloat)cy/(GLfloat)cx, 3.0 * (GLfloat)cy/(GLfloat)cx,-3.0,3.0); glMatrixMode(GL_MODELVIEW); } } void COpenGLWnd::OnDestroy() { CFrameWnd::OnDestroy(); ::wglMakeCurrent(NULL, NULL); if (m_hrc) ::wglDeleteContext(m_hrc); if (m_pDC) delete m_pDC; // TODO: Add your message handler code here } BOOL COpenGLWnd::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default return TRUE; //return CFrameWnd::OnEraseBkgnd(pDC); } void COpenGLWnd::OnPaint() { CPaintDC dc(this); // device context for painting GLfloat light_position[]="{2.0f,0.0f,4.0f,0.0f};" // TODO: Add your message handler code here glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(0.0f, 0.0f, 2.0f); glLightfv(GL_LIGHT0,GL_POSITION,light_position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); auxSolidSphere(1.0); glPopMatrix(); glFinish(); // Do not call CFrameWnd::OnPaint() for painting messages } 应 用 程 序 类 的 定 义(SimpleGLApp.h): #if !defined(AFX_SIMPLEGLAPP_H__3FB1AB29 _0E70_11D2_9ACA_48543300E17D__INCLUDED_) #define AFX_SIMPLEGLAPP_H__3FB1AB29_0E70_11D2 _9ACA_48543300E17D__INCLUDED_ #if _MSC_VER>= 1000
#pragma once
#endif // _MSC_VER >= 1000
// SimpleGLApp.h : header file
//
#include
#include "OpenGLWnd.h"
#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
// CSimpleGLApp thread

class CSimpleGLApp : public CWinApp
{
DECLARE_DYNCREATE(CSimpleGLApp)
public:
CSimpleGLApp(); // protected constructor used by dynamic creation

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSimpleGLApp)
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CSimpleGLApp();

// Generated message map functions
//{{AFX_MSG(CSimpleGLApp)
afx_msg void OnAppExit();
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional
declarations immediately before the previous line.

#endif // !defined(AFX_SIMPLEGLAPP_H__3FB1AB29_0E70_11D2_9ACA_48543300E17D__INCLUDED_)
应用程序类的实现(SimpleGLApp.cpp):
// SimpleGLApp.cpp : implementation file
//

#include "stdafx.h"
#include "SimpleGLApp.h"
#include "OpenGLWnd.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSimpleGLApp

IMPLEMENT_DYNCREATE(CSimpleGLApp, CWinApp)

CSimpleGLApp::CSimpleGLApp()
{
}

CSimpleGLApp::~CSimpleGLApp()
{
}

BOOL CSimpleGLApp::InitInstance()
{
// TODO: perform and per-thread initialization here
m_pMainWnd = new COpenGLWnd();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}

int CSimpleGLApp::ExitInstance()
{
return CWinApp::ExitInstance();
}

BEGIN_MESSAGE_MAP(CSimpleGLApp, CWinApp)
//{{AFX_MSG_MAP(CSimpleGLApp)
ON_COMMAND(ID_APP_EXIT, OnAppExit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSimpleGLApp message handlers
void CSimpleGLApp::OnAppExit()
{
// TODO: Add your command handler code here
CWinApp::OnAppExit();
}

CSimpleGLApp SimpleGLApp;

1 / 1

登录后回复。