在 MFC 中的 OpenGL 上下文上方绘制文本

2022-01-12 00:00:00 opengl mfc

我在一个包含 OpenGL 上下文的 MFC 应用程序上工作.我是 MFC 的新手,这就是我问它的原因.OpenGL 工作正常,但是当我想在 WindowProc 中使用此代码在 3D 窗口上方绘制文本时:

I work on an MFC app containing OpenGL context.I am new to MFC that is why I am asking it.OpenGL works fine ,but when I want to draw a text above the 3D window using this code inside WindowProc:

        case WM_PAINT:

      hDC=BeginPaint(window,&paintStr);
      GetClientRect(window,&aRect);
      SetBkMode(hDC,TRANSPARENT);
      DrawText(hDC,L"He He I am a text on top of OpenGL",-1,&aRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
      EndPaint(window,&paintStr);

      return 0;

它显示在 OpenGL 上下文下方.我只能在 OpenGL 渲染暂停时调整窗口大小时看到它.

it is shown beneath the OpenGL context.I can see it only when resizing the window as the OpenGL rendering pauses than.

推荐答案

你正在做的事情是错误的,而且比在 OpenGL 中做这一切更难.要解决向 OpenGL 绘制的窗口添加文本的问题,最好只让 OpenGL 绘制文本.您甚至可以通过在处理 WM_CREATE 时创建一个 CFont 实例、将字体选择到 DC 中并调用 wglUseFontBitmaps 来使用您在 MFC 中使用的完全相同的字体,这将生成一系列可以与 glCallLists 一起使用的光栅化位图.(在此过程中,调用 GetCharABCWidths 和 GetTextMetrics 来分别确定每个字形的宽度和高度.)

What you're doing is wrong and also harder than doing it all in OpenGL. To solve the problem of adding text to an OpenGL-drawn window, it's better to just make OpenGL draw the text. You can even use the exact same font you were using in MFC by creating a CFont instance when you handle WM_CREATE, selecting the font into the DC, and calling wglUseFontBitmaps, which will make a series of rasterized bitmaps that you can use with glCallLists. (While you're at it, call GetCharABCWidths and GetTextMetrics to determine the width and height of each glyph, respectively.)

<代码>

ABC glyphInfo[256]; // for font widths
TEXTMETRIC tm;  // for font heights

// create a bitmap font
CFont myFont;
myFont.CreateFont(
    16,                        // nHeight
    0,                         // nWidth
    0,                         // nEscapement
    0,                         // nOrientation
    FW_NORMAL,                 // nWeight
    FALSE,                     // bItalic
    FALSE,                     // bUnderline
    0,                         // cStrikeOut
    ANSI_CHARSET,              // nCharSet
    OUT_DEFAULT_PRECIS,        // nOutPrecision
    CLIP_DEFAULT_PRECIS,       // nClipPrecision
    DEFAULT_QUALITY,           // nQuality
    DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
    _T("Arial")                // lpszFacename
);

// change the current font in the DC
CDC* pDC = CDC::FromHandle(hdc);

// make the system font the device context's selected font  
CFont *pOldFont = (CFont *)pDC->SelectObject (&myFont); 

// the display list numbering starts at 1000, an arbitrary choice  
wglUseFontBitmaps (hdc, 0, 255, 1000); 
VERIFY( GetCharABCWidths (hdc, 0, 255, &glyphInfo[0]) );
pDC->GetTextMetrics(&tm);

if(pOldFont)
    pDC->SelectObject(pOldFont);

myFont.DeleteObject(); 

然后,当您处理 WM_PAINT 时,重置您的矩阵并使用 glRasterPos2d 将文本放在您需要的位置.如果您希望字符串水平居中,我建议使用类似于下面的代码来计算字符串的确切宽度.<代码>

Then when you handle WM_PAINT, reset your matrices and use glRasterPos2d to put the text where you need it to go. I suggest calculating the exact width of your string using code similar to the one below if you want it to be horizontally centered.

// indicate start of glyph display lists  
glListBase (1000); 

CRect r;
GetWindowRect(r);

glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, r.Width(), 0, r.Height());

glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

CString formattedString;
formattedString.Format("Pi is about %1.2f", 3.1415);

int stringWidth=0; // pixels
for(int j=0; j < formattedString.GetLength(); ++j)
    stringWidth += glyphInfo[ formattedString.GetAt(j) ].abcA + glyphInfo[ formattedString.GetAt(j) ].abcB + glyphInfo[ formattedString.GetAt(j) ].abcC;

double textXPosition, textYPosition;
textXPosition = r.Width()/2-stringWidth/2; // horizontally centered
textYPosition = r.Height()/2-tm.tmHeight/2; // vertically centered

glRasterPos2d(textXPosition,textYPosition);

// this is what actually draws the text (as a series of rasterized bitmaps)
glCallLists (formattedString.GetLength(), GL_UNSIGNED_BYTE, (LPCSTR)formattedString);

glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();

虽然设置很烦人,但您只需执行一次,我认为这比处理 GDI 更令人沮丧.混合 GDI 和 OpenGL 确实是自找麻烦,而 OpenGL 在显示文本方面做得非常好――您可以免费获得亚像素精度,以及其他好处.

While the setup is annoying, you only have to do it once, and I think it's less frustrating than dealing with GDI. Mixing GDI and OpenGL is really asking for trouble, and OpenGL does a very good job of displaying text -- you get sub-pixel accuracy for free, among other benefits.

为了响应您包含 GUI 元素的请求,我假设您的意思是您希望同时拥有 OpenGL 绘制的窗口和标准的 Windows 控件(编辑框、复选框、按钮、列表控件等)在同一个父窗口内.我还将假设您打算让 OpenGL 仅绘制窗口的一部分,而不是窗口的背景.

In response to your request for including GUI elements, I will assume that you meant that you want to have both OpenGL-drawn windows and also standard Windows controls (edit boxes, check boxes, buttons, list controls, etc.) inside the same parent window. I will also assume that you intend OpenGL to draw only part of the window, not the background of the window.

既然您说您使用的是 MFC,我建议您创建一个对话框窗口,将所有标准 Windows 控件添加到其中,然后添加处理 WM_PAINT 的 CWnd 派生类.使用资源编辑器将控件移动到您想要的位置.实际上,您正在制作一个由 OpenGL 进行绘图的自绘自定义控件.因此 OpenGL 将绘制该窗口,而标准 MFC 类(CEdit、CButton 等)将自行绘制.根据我的经验,这很有效,它与 GDI 在所有者绘制控件中所做的并没有太大区别.

Since you said you're using MFC, I suggest that you create a dialog window, add all of your standard Windows controls to it, and then add in a CWnd-derived class where you handle WM_PAINT. Use the resource editor to move the control to where you want it. Effectively, you're making an owner-draw custom control where OpenGL is doing the drawing. So OpenGL will draw that window, and the standard MFC classes (CEdit, CButton, etc.) will draw themselves. This works well in my experience, and it's really not much different from what GDI does in an owner-draw control.

如果您希望 OpenGL 绘制窗口的背景,并且希望标准的 Windows 控件出现在它上面怎么办?我认为这不是一个好主意,但是您可以为 CDialog 派生类处理 WM_PAINT 和 WM_ERASE.在 WM_ERASE 中,调用 OpenGL 来绘制您的 3D 内容,当调用 WM_PAINT 时,这些内容将被标准 Windows 控件覆盖.或者,在 WM_PAINT 中,您可以在调用 CDialog::OnDraw 之前调用 OpenGL,这将是类似的.

What if instead you want OpenGL to draw the background of the window, and you want standard Windows controls to appear on top of it? I don't think this is a great idea, but you can handle WM_PAINT and WM_ERASE for your CDialog-derived class. In WM_ERASE, call OpenGL to draw your 3D content, which will be overwritten by the standard Windows controls when WM_PAINT is called. Alternatively in WM_PAINT you could call OpenGL before calling CDialog::OnDraw, which would be similar.

如果你想让我写更多,请澄清你的陈述我想添加一些 2s 图形叠加(如标签、gui 元素)".

Please clarify your statement "I want to add some 2s graphics overlay (like labels ,gui elements)" if you want me to write more.

相关文章