一、前言

在学习OpenGL的过程中,发现很多函数都是全局的。前面几章中都是在一个窗口DC中画图,那么要在多个窗口画图,需要怎么处理呢?网上方法有多种,这里采用其中一种,利用wglMakeCurrent函数来切换不同窗口,以达到多窗口同时喧染的目的。

二、准备

每个窗口与OpenGL绑定时,都通过以下几个过程进行:

1.获取窗口句柄Handle/HWND(在TWinControl继承下来的类中,都可以通过TWinControl.Handle获得,MFC的窗口可以通过CWnd::GetSafeHwnd()获得)

2.通过句柄获取设备场景HDC(通过API GetDC()获取)

3.通过设备场景HDC获取OpenGL的HGLRC(通过OpenGL的wglCreateContext()获得)

当然,在程序销毁时记得释放相应的HGLRC与窗口DC。通过以上的对应关系,可以把这些信息存储在数组中。以四个窗口为例,我建了这样几个数组:

    FDC: array[..] of HDC;
FHRC: array[..] of HGLRC;
FHwnd: array[..] of THandle;

三、初始化

本例以多个TPanel为例代替多个窗口,以上三个数组进行初始化。新建了一个inidc()的方法,在窗口的OnCreate时调用,目的是初始化数组变量的值

procedure TForm1.FormCreate(Sender: TObject);
begin
Fbmpindex := ;
// 对多个窗口进行初始化
inidc(, Pnl1.Handle);
inidc(, Pnl2.Handle);
inidc(, Pnl3.Handle);
inidc(, Pnl4.Handle);
end;

下面的inidc的代码

procedure TForm1.inidc(i: Integer; hform: THandle);
var
 pfd:TPIXELFORMATDESCRIPTOR;
pixelFormat: Integer;
begin
With pfd do
begin
nSize := sizeof(TPIXELFORMATDESCRIPTOR); // size
nVersion := ; // version
dwFlags := PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER; // support double-buffering
iPixelType := PFD_TYPE_RGBA; // color type
cColorBits := ; // preferred color depth
cRedBits := ;
cRedShift := ; // color bits (ignored)
cGreenBits := ;
cGreenShift := ;
cBlueBits := ;
cBlueShift := ;
cAlphaBits := ;
cAlphaShift := ; // no alpha buffer
cAccumBits := ;
cAccumRedBits := ; // no accumulation buffer,
cAccumGreenBits := ; // accum bits (ignored)
cAccumBlueBits := ;
cAccumAlphaBits := ;
cDepthBits := ; // depth buffer
cStencilBits := ; // no stencil buffer
cAuxBuffers := ; // no auxiliary buffers
iLayerType := PFD_MAIN_PLANE; // main layer
bReserved := ;
dwLayerMask := ;
dwVisibleMask := ;
dwDamageMask := ;
end;
FDC[i] := GetDC(hform);
FHwnd[i] := hform;
pixelFormat := ChoosePixelFormat(FDC[i], @pfd);
if pixelFormat = then
Exit;
if not SetPixelFormat(FDC[i], pixelFormat, @pfd) then
Exit;
FHRC[i] := wglCreateContext(FDC[i]);
wglMakeCurrent(FDC[i], FHRC[i]); // 设置背景色为 黑色 参数为 RGBA
glClearColor(, , , );
end;

四、绘制

这里修改一下前面几章中的Draw函数,用于支持多窗口的绘制,代码如下:

procedure TForm1.Draw(i: Integer);
var
Bmp: TBitmap;
texture: GLuint;
l, t, w, h: Integer;
rc: TRect;
begin
// 重新设置显示区域
wglMakeCurrent(FDC[i], FHRC[i]);
// 重新计算并设置显示区域大小
GetWindowRect(fhwnd[i], rc);
w := rc.Right - rc.Left;
h := rc.Bottom - rc.Top;
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
glViewPort(, , w, h);
gluOrtho2D(, w, h, );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity; // 只是为了显示多个图片,不是必须的
inc(Fbmpindex);
Bmp := TBitmap.Create;
Bmp.LoadFromFile(ExtractFilePath(ParamStr()) + IntToStr(Fbmpindex mod ) + '.bmp');
// 创建纹理区域
glGenTextures(, @texture); // 这地方是错误的,应该用成员变量,不能一直创建纹理,否则内存会一直涨
// 绑定纹理区域
glBindTexture(GL_TEXTURE_2D, texture);
// 使用位图创建图像纹理
glTexImage2D(
GL_TEXTURE_2D, // 纹理是一个2D纹理 GL_TEXTURE_2D
, // 图像的详细程度 默认
, // 数据的成分数。因为图像是由红,绿,蓝三种组成 默认3
Bmp.Width, // 纹理的宽度
Bmp.Height, // 纹理的高度
, // 边框的值 默认
GL_BGR_EXT, // 数据格式 bmp使用 bgr
GL_UNSIGNED_BYTE, // 组成图像的数据是无符号字节类型的
Bmp.ScanLine[Bmp.Height - ] // DIB数据指针
);
// 下面两行是让opengl在放大原始的纹理大(GL_TEXTURE_MAG_FILTER)或缩小原始纹理(GL_TEXTURE_MIN_FILTER)时OpenGL采用的滤波方式。
// GL_LINEAR 使用线性滤波,可以把图片处理处平滑,但需要更多的内存与CPU
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 线形滤波
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 线形滤波 // 以下是绘图,利用一个四边形,绘制图片 // 启用纹理映射
if glIsEnabled(GL_TEXTURE_2D) = then
glEnable(GL_TEXTURE_2D);
// 清空缓冲区
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); l := ;
t := ;
w := + (i * ); // 放大为200*的图片 // 选择纹理 如果场景中使用多个纹理,不能在glBegin() 和 glEnd() 之间绑定纹理
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS);
// glTexCoord2f 的第一个参数是X坐标。
// 0.0是纹理的左侧。 0.5是纹理的中点, 1.0是纹理的右侧。
// glTexCoord2f 的第二个参数是Y坐标。
// 0.0是纹理的底部。 0.5是纹理的中点, 1.0是纹理的顶部。
glTexCoord2f(, );
glVertex2f(l, t);
glTexCoord2f(, );
glVertex2f(l + w, t);
glTexCoord2f(, );
glVertex2f(l + w, t + w);
glTexCoord2f(, );
glVertex2f(l, t + w);
glEnd(); Bmp.Free;
SwapBuffers(FDC[i]);
end;

上面的画图片的代码,如果觉得看起来复杂,可以用以下简单点的代码:

procedure TForm1.DrawSimple(i: Integer);
var
l, t, w, h: Integer;
rc: TRect;
begin
// 重新设置显示区域
wglMakeCurrent(FDC[i], FHRC[i]);
// 重新计算并设置显示区域大小
GetWindowRect(fhwnd[i], rc);
w := rc.Right - rc.Left;
h := rc.Bottom - rc.Top;
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
glViewPort(, , w, h);
gluOrtho2D(, w, h, );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity; // 清空缓冲区
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
// 设置四边形的颜色
glColor3f(, 0.5, ); // 绘制第一个多边形
l := ;
t := ;
w := + (i - ) * ;
glBegin(GL_QUADS);
glVertex2f(l, t);
glVertex2f(l + w, t);
glVertex2f(l + w, t + w);
glVertex2f(l, t + w);
glEnd();
SwapBuffers(FDC[i]);
end;

为了方便演示,这里用一个定时器TTimer代替以前在窗口的OnPaint画图,定时器下的代码如下:

procedure TForm1.tmr1Timer(Sender: TObject);
begin
Draw();
Draw();
DrawSimple();
DrawSimple();
end;

五、释放

最后记得释放资源,Delphi中基本上申请的资源都要回收,new->delete/dispose、get->release、create->destroy,简单的说,创建(create)的要销毁(destroy),借(get)的要还(release)。

procedure TForm1.FormDestroy(Sender: TObject);
var
i: Integer;
begin
for i := to - do
begin
wglMakeCurrent(FDC[i], FHRC[i]);
wglDeleteContext(FHRC[i]);
ReleaseDC(fhwnd[i], FDC[i]);
end;
end;

效果如下:

源码下载:OpenGL_06.zip

2014-07-17 by lin

Delphi下OpenGL2d绘图(06)-画图(多窗口、多视图、多个DC)的更多相关文章

  1. Delphi下OpenGL2d绘图(04)-画四边形

    一.前言 画四边形基本上与前几遍文字代码是相同.区别在于glBegin()的参数“GL_QUADS”.绘制的框架代码可以使用 Delphi下OpenGL2d绘图(01)-初始化 中的代码.修改的部份为 ...

  2. Delphi下OpenGL2d绘图(03)-画线

    一.前言 画线与画点基本上代码是相同.区别在于glBegin()的参数.绘制的框架代码可以使用 Delphi下OpenGL2d绘图(01)-初始化 中的代码.修改的部份为 Draw 函数的内容. 二. ...

  3. Delphi下OpenGL2d绘图(02)-画点

    一.前言 图形的绘制可以使用glBegin().glEnd()之间完成,绘制的框架代码可以使用 Delphi下OpenGL2d绘图(01)-初始化 中的代码.修改的部份为 Draw 函数的内容. 二. ...

  4. Delphi下OpenGL2d绘图(05)-画图片Bmp

    一.前言 找了不少资料,要画图片要先处理一下,需要引用别的单元,Delphi中没带,需要另外下载Gl.pas.看网上说是自带的OpenGl单元封装的是1.0版的,有此函数未声明.网上可以找到Gl.pa ...

  5. Delphi下OpenGL2d绘图(01)-初始化

    一.前言: Delphi默认支持OpenGl,可以uses OpenGL单元进行引用,便可以使用OpenGL的函数.OpenGl是跨平台的,而且Windows很早就支持并集成在系统中,存在于syste ...

  6. DELPHI下API简述(1800个API)

    DELPHI下API简述 http://zero.cnbct.org/show.asp?id=144 auxGetDevCaps API 获取附属设备容量 auxGetNumDevs API 返回附属 ...

  7. DELPHI下的SOCK编程(转)

    DELPHI下的SOCK编程      本文是写给公司新来的程序员的,算是一点培训的教材.本文不会涉及太多的编程细节,只是简单讲解在DELPHI下进行Winsock编程最好了解的知识. 题外话:我认为 ...

  8. 终于可以发布Delphi下收点OSGI精髓皮毛而设计的插件框架WisdomPluginFramework

    这是一个Delphi实现的插件框架,我耗费了相当相当相当多的精力来设计她,虽然仅闪着点我微薄智慧的光芒,但我还是决定用Wisdom来命名她,也因它是我绝无仅有的,在完成1年多后回头来看仍能相当满意的作 ...

  9. Delphi下的RTTI函数大全

    http://ljz9425.blog.163.com/blog/static/369148572008111635253858/ Delphi下的RTTI(下) 2008-12-16 15:52:5 ...

随机推荐

  1. asp.net 添加错误日志

    在开发程序中,错误日志很有必要.今天就把使用到的添加错误日志,记录下来,方便以后查看 利用的asp.net错误处理机制 Application_Error 贴出代码 protected void Ap ...

  2. C# SM加密

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using Org.Boun ...

  3. C语言/C++编程学习:和QT零距离接触的意义

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  4. nginx反向代理后abp的webapi host如何获取客户端ip?

    dotnet core 跨平台是微软伟大的创举,脱离iis后服务器成本都降低了. 问题 这不,采用abp搞了个小项目,部署到centos后发现审计日志里面的ip信息不对. 解决 这个问题在.net 4 ...

  5. 如何实现一个IOS网络监控组件

    此文由作者朱志强授权网易云社区发布. Mobile Application Monitor IOS组件设计技术分享 背景 应用程序性能管理Application Performance Managem ...

  6. JAVA-WEB开发环境和搭建

    JAVA Web开发环境与搭建 一.下载安装JDK 1.配置jdk开发环境 JAVA_HOME 2.path 二.下载安装eclipse javaEE版本 三.安装部署tomcat 3.1.安装: 直 ...

  7. GO学习笔记 - 包内首字母大写的名称是被导出的,才能被其它包代码调用!

    在GO语言的任意包内,如果名称的首字母是大写的,意味着这个名称被导出,在其它包中可以使用“包名.名称”方式来调用,如果名称首字母不是大写,那么只能在这个包内部使用!这个概念还真是和以往接触的编程语言的 ...

  8. Stacking方法详解

    集成学习方法主要分成三种:bagging,boosting 和 Stacking.这里主要介绍Stacking. stacking严格来说并不是一种算法,而是精美而又复杂的,对模型集成的一种策略. 首 ...

  9. Java50道经典习题-程序17 猴子吃桃问题

    题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个.以后每天早上都吃了前一天剩下的一半零一个.到第10天早上想再吃时,见只 ...

  10. 【OCP-12c】2019年CUUG OCP 071考试题库(76题)

    76.View the exhibit and examine the description of the DEPARTMENTSand EMPLOYEEStables. The retrieve ...