1、在 windows 95 nt 下 用 opengl 编 程 科 学 计 算 可 视 化, 计 算 机 动 画 和 虚 拟 现 实 是 现 在 计 算 机 图 形学 的 三 个 热 点。 而 这 三 个 热 点 的 核 心 都 是 三 维 真 实 感 图 形 的绘 制。 由 于 opengl(open graphics library) 具 有 跨 平 台 性、 简 便、 高 效、功 能 完 善, 目 前 已 经 成 为 了 三 维 图 形 制 作 方 法 中 事 实 上 的 工业 标 准。 自 从 windows nt 3.51 在 微 机 平 台 上 支 持 opengl 以 后,现 在
2、微软 公 司 在 windows 95 osr2、windows nt 4.0 中 连 续 性 的 提 供opengl 开 发 环境。visual c+ 从 4.2 版 本 以 后 已 经 完 全 支 持 opengl api, 使 三 维 世 界的“ 平 民 化” 已 成 为 必 然。windows 操 作 系 统 对 opengl 的 支 持具 有 windows 编 程 经 验 的 人 都 知 道, 在 windows 下 用 gdi 作 图 必 须 通 过设 备 上 下 文(device context 简 写 dc) 调 用 相 应 的 函 数; 用opengl 作 图也 是 类 似
3、,opengl 函 数 是 通 过“ 渲 染 上 下 文”(rendering context 简 写rc) 完 成 三 维 图 形 的 绘 制。windows 下 的 窗 口 和 设 备 上 下 文 支 持“位 图 格 式”( pixelformat) 属 性, 和 有 着 位 图 结 构 上 的 一 致。只 要 在 创 建 时 与 一 个 建 立 联 系( 也 只 能 通 过 已 经建 立 了 位 图 格 式 的 来 创 建),opengl 的 函 数 就 可 以 通 过 对 应 的 画 到 相 应 的 显 示 设 备 上。 这 里 还 有 以 下 需 要 注 意 的方 面:1. 一 个
4、线 程 只 能 拥 有 一 个 渲 染 上 下 文( ), 也 就 是 说, 用 户如 果 在 一 个 线 程 内 对 不 同 设 备 作 图, 只 能 通 过 更 换 与 对 应的 来 完 成, 而 在 线 程 中 保 持 不 变。( 当 然, 删 除 旧 的 后 再 创 建 新 的 是 可 以 的) 与 此 对 应, 一 个 也 只 能 属 于 一个 线 程, 不 能 被 不 同 线 程 同 时 共 享。2. 设 定 dc 位 图 格 式 等 于 设 定 了 相 应 的 窗 口 的 位 图 格 式,并 且 和 窗 口 的 位 图 格 式 一 旦 确 定 就 不 能 再 改 变。 这 一 点
5、 只 能 期望 以 后 的 windows 版 本 改 进 了。3. 一 个 虽 然 可 以 更 换 dc, 在 任 何 时 刻 只 能 利 用 一 个 ( 这 个 dc 称 为 rc 的 当 前 dc), 但 由 于 一 个 窗 口 可 以 让 多 个 作图 从 而 可 以 让 多 个 线 程 利 用 多 个 在 该 窗 口 上 执 行 opengl 操作。4. 现 在 的 windows 下 的 opengl 版 本 对 opengl 和 gdi 在 同 一 个 dc 上 作 图 有一 定 的 限 制。 当 使 用 双 缓 存 用 opengl 产 生 动 画 时, 不 能 使 用 gdi
6、 函数 向 该 dc 作 图。5. 不 建 议 用 在 windows 下 编 写 opengl 程 序。 这 样 的 程序 虽 然 具 有 跨 平 台 的 可 移 植 性( 比 如 很 多 的 例 子 程 序),但 是 它 们 不 能 利 用 windows 操 作 系 统 的 很 多 特 性, 实 用 价 值 不 大。用 来 编 写 opengl 程 序经 过 上 面 的 分 析, 用 来 调 用 opengl 作 图 的 方 法 就 很 显 然 了。步 骤 如 下:1. 先 设 置 显 示 设 备 的 位 图 格 式(pixelformat) 属 性。 这 通 过填 充 一 个 pixe
7、lformatdescriptor 的 结 构 来 完 成( 关 于pixelformatdescriptor中 各 项 数 据 的 意 义, 请 参 照 的 帮 助 信 息), 该 结 构 决 定 了opengl 作 图 的 物 理 设 备 的 属 性, 比 如 该 结 构 中 的 数 据 项dwflags 中pfd_doublebuffer 位 如 果 没 有 设 置( 置 ), 通 过 该 设 备 的 上 作图 的 opengl 命 令 就 不 可 能 使 用 双 缓 冲 来 做 动 画。 有 一 些 位 图 格式(pixelformat) 是 dc 支 持 的, 而 有 一 些 dc
8、就 不 支 持 了。 所 以 程 序必 须 先 用 choosepixelformat 来 选 择 dc 所 支 持 的 与 指 定 位 图 格 式 最接 近 的 位 图 格 式, 然 后 用 setpixelformat 设 置 dc 的 位 图 格 式。2. 利 用 刚 才 的 设 备 建 立 渲 染 上 下 文 (wglcreatecontext),使 得 rc 与 dc 建 立 联 系(wglmakecurrent)。3. 调 用 opengl 函 数 作 图。 由 于 线 程 与 一 一 对 应,opengl 函 数 的参 数 中 都 不 指 明 本 线 程 的 句 柄(handle
9、)4. 作 图 完 毕 以 后, 先 通 过 置 当 前 线 程 的 为null(:wglmakecurrent(null, null);), 断 开 当 前 线 程 和 该 渲 染 上 下 文的 联 系, 由 此 断 开 与 的 联 系。 此 时 rc 句 柄 的 有 效 性 在 微 软自 己 的 文 档 中 也 没 有 讲 清 楚, 所 以 在 后 面 删 除 rc 的 时 候 要 先 判断 以 下 rc 句 柄 的 有 效 性(if (m_hrc) :wgldeletecontext(m_hrc);)。 再 根据 情 况 释 放(releasedc) 或 者 删 除(deletedc)
10、所 附 程 序 说 明所 附 的 程 序 用 完 成 了 一 个 简 单 的 opengl 作 图, 用opengl 的辅 助 库 画 了 一 个 有 光 照 的 实 心 圆 球。opengl 本 身 的 函 数 这 里 就不 解 释 了, 仅 对 用 mfc 编 opengl 时 需 要 注 意 的 内 容 做 一 个 简 要 的 说明:1. 一 旦 设 定 了 一 个 dc 的 位 图 格 式, 该 dc 所 联 系 的 窗 口 的 位 图 格式 随 之 设 定。 该 窗 口 若 含 有 子 窗 口 或 者 有 兄 弟 窗 口, 这 些 兄 弟/ 子 窗 口 的 位 图 格 式 没 有 设
11、 成 与 对 应 rc 一 致 的 格 式,opengl 在 它们 上 面 作 图 就 容 易 出 错。 故 而 opengl 作 图 的 窗 口 必 须 具 有ws_clipchildren 和 ws_clipsiblings 风 格, 程 序 中 在 主 框 窗 的 构 造 函 数中 用 loadframe(idr_mainframe,ws_overlappedwindow | ws_clipchildren |ws_clipsiblings,null,null ); 指 定 了 主 窗 口 的 风 格。2. 在 ansi c 的 opengl 编 程 中, 由 auxreshapefun
12、c 定 义 设 置 opengl 视 口 大 小和 作 图 尺 寸 的 回 调 函 数。 在 mfc 中 应 该 由 wm_siz 消 息 的 处 理 函 数来 完 成。 在 ansi c 的 opengl 编 程 中, 由 eauxmainloop 定 义 作 图 的 回 调 函数。 在 mfc 中 应 该 由 wm_paint 消 息 的 处 理 函 数 来 处 理。 相 应 的, 由opengl 定 义 的 键 盘、 鼠 标 处 理 函 数 都 应 该 由 相 应 的windows 处 理 函数 来 响 应。3. opengl 自 己 有 刷 新 背 景 的 函 数 glclear, 故
13、 而 应 禁 止windows 刷 新窗 口 背 景。 否 则, 当 窗 口 需 要 重 画 时,windows 会 自 动 先 发 送wm_erasebkgnd, 而 缺 省 的 处 理 函 数 使 用 白 色 的 背 景 刷。 当opengl 使用 的 背 景 颜 色 不 是 白 色 时, 作 图 时 有 一 帧 白 色 的 闪 烁。 这 种 现象 在 做 动 画 时 特 别 明 显。 程 序 中 只 需 要 在 wm_erasebkgnd 的 消 息 处理 函 数 中 禁 止 父 窗 口 类 的 消 息 处 理, 简 单 的 返 回 一 个true 即 可。4. 由 于 opengl 的
14、 跨 平 台 性, 它 必 须 用 操 作 系 统 的 调 色 板。所 以 如果 gl_index_mode 作 图 时, 必 须 用 vc 自 己 定 义 调 色 板。 不 过 一 般 情 况下, 用 gl_rgba_mode 模 式 比 较 方 便, 很 少 用 到 gl_index_mode 模 式。5. 在 opengl 作 图 期 间,rc 对 应 的 dc 不 能 删 除 或 者 释 放。6. 由 于 opengl 作 图 时 需 要 长 时 间 占 用 dc, 所 以 最 好 把 作 图 窗 口 类设 成 cs_owndc。mfc 缺 省 的 窗 口 类 风 格 中 没 有 设
15、这 一 属 性,程 序 中在 主 窗 口 c+ 类 的 precreatewindow 方 法 中 自 己 注 册 了 一 个 窗 口 类,除 了 设 定 了 cs_owndc 属 性 以 外, 还 设 定 了cs_hredraw、cs_vredraw 和cs_savebits。 设 定 cs_hredraw、cs_vredraw 是 为 了 让 窗 口 缩 放 时 产 生wm_paint 消 息, 修 正 opengl 视 口 和 作 图 尺 寸; 由 于 opengl 作 图 需 要 很多 计 算, 设 定 cs_savebits 是 为 了 在 opengl 窗 口 被 遮 盖 后 显
16、现 出 来 时,不 产 生 wm_paint 消 息, 用 内 存 存 储 的 图 象 来 填 充, 从 而 用 空 间 消耗 换 取 计 算 时 间。7. 本 程 序 中 没 有 对 opengl 函 数 的 出 错 情 况 作 出 处 理。opengl 出 错后 返 回 错 误 码, 不 会 抛 出 异 常; 而 且 在 某 一 个 函 数 出 错 以 后, 后继 函 数 也 一 般 不 会 出 现 异 常, 只 是 返 回 错 误 码, 一 不 小 心 就 可能 忽 略 某 些 错 误。 而 对 每 一 个 opengl 函 数 都 做 出 错 与 否 的 判 断 比较 麻 烦, 所 以
17、 编 程 序 时 对 opengl 的 函 数 应 当 非 常 小 心。附 程 序:程 序 运 行 时 必 须 确 定 opengl32.dll、glu.dll、glaux.dll 在windows 的 system目 录 下。 如 果 找 不 到 这 些 文 件, 可 以 从 windows 95 osr2 的 机 器 上 面将 这 些 文 件 拷 贝 过 来 即 可。 opengl 运 行 不 需 要 注 册 库 信 息。 在 vc的 studio 中 运 行 程 序 时, 工 程 文 件 中 必 须 加 入 opengl.h 、glu.h、glaux.h以 及 opengl.lib、gl
18、u.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
19、 “resource.h“/ openglwnd.h : header file/ copenglwnd frameclass copenglwnd : public cframewnddeclare_dyncreate(copenglwnd)public:copenglwnd();/ protected constructor used by dynamic creationprotected:hglrc m_hrc;cclientdc *m_pdc;/ attributespublic:/ operationspublic:/ overrides/ classwizard generate
20、d virtual function overrides/afx_virtual(copenglwnd)protected:virtual bool precreatewindow(createstruct/afx_virtual/ implementationpublic:virtual copenglwnd();/ generated message map functions/afx_msg(copenglwnd)afx_msg int oncreate(lpcreatestruct lpcreatestruct);afx_msg void onsize(uint ntype, int
21、cx, int cy);afx_msg void ondestroy();afx_msg bool onerasebkgnd(cdc* pdc);afx_msg void onpaint();/afx_msgdeclare_message_map();/afx_insert_location/ microsoft developer studio will insert additionaldeclarations immediately before the previous line.#endif / !defined(afx_openglwnd_h_3fb1ab28_0e70_11d2_
22、9aca_48543300e17d_included_)主 窗 口 类 的 实 现(openglwnd.cpp):/ openglwnd.cpp : implementation file/#include “stdafx.h“#include “openglwnd.h“#include “simpleglapp.h“#include “glglu.h“#include “glgl.h“#include “glglaux.h“#ifdef _debug#define new debug_new#undef this_filestatic char this_file = _file_;#end
23、if/ copenglwndimplement_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
24、_size()on_wm_destroy()on_wm_erasebkgnd()on_wm_paint()/afx_msg_mapend_message_map()bool copenglwnd:precreatewindow(createstructreturn cframewnd:precreatewindow(cs);int copenglwnd:oncreate(lpcreatestruct lpcreatestruct) if (cframewnd:oncreate(lpcreatestruct) = -1)return -1;int pixelformat;m_pdc = new
25、cclientdc(this);/ 在 客 户 区 作 图assert(m_pdc != null);static pixelformatdescriptor pfd =sizeof(pixelformatdescriptor), / 固 定 值1, / 固 定 值pfd_draw_to_window | / support windowpfd_support_opengl | / support openglpfd_type_rgba, / rgba 模 式, 不 用 调 色 板16, / 程 序 在 16 位 色 彩 下 运 行0, 0, 0, 0, 0, 0, / color bits
26、ignored0, / no alpha buffer0, / shift bit ignored0, / no accumulation buffer0, 0, 0, 0, / accum bits ignored32, / 32-bit z-buffer0, / no stencil buffer0, / no auxiliary bufferpfd_main_plane, / main layer0, / reserved0, 0, 0 / layer masks ignored;if ( (pixelformat = choosepixelformat(m_pdc-getsafehdc
27、(), return -1;if (setpixelformat(m_pdc-getsafehdc(), pixelformat, 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 窗 口 构
28、造 成 功void copenglwnd:onsize(uint ntype, int cx, int cy) cframewnd:onsize(ntype, cx, cy);/ todo: add your message handler code hereif(cy 0) glviewport(0, 0, cx, cy);glmatrixmode(gl_projection);glloadidentity();if (cx = 1000#pragma once#endif / _msc_ver = 1000s / simpleglapp.h : header file/#include #
29、include “openglwnd.h“#include “resource.h“/ csimpleglapp threadclass csimpleglapp : public cwinappdeclare_dyncreate(csimpleglapp)public:csimpleglapp(); / protected constructor used by dynamic creation/ attributespublic:/ operationspublic:/ overrides/ classwizard generated virtual function overrides/
30、afx_virtual(csimpleglapp)public:virtual bool initinstance();virtual int exitinstance();/afx_virtual/ implementationpublic:virtual csimpleglapp();/ generated message map functions/afx_msg(csimpleglapp)afx_msg void onappexit();/afx_msgdeclare_message_map();/afx_insert_location/ microsoft developer stu
31、dio 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“#i
32、fdef _debug#define new debug_new#undef this_filestatic char this_file = _file_;#endif/ csimpleglappimplement_dyncreate(csimpleglapp, cwinapp)csimpleglapp:csimpleglapp()csimpleglapp:csimpleglapp()bool csimpleglapp:initinstance()/ todo: perform and per-thread initialization herem_pmainwnd = new copeng
33、lwnd();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_mapend_message_map()/ csimpleglapp message handlersvoid csimpleglapp:onappexit() / todo: add your command handler code herecwinapp:onappexit();csimpleglapp simpleglapp;