OpenGL中文本的显示 

OpenGL中文本的显示 
航天部二院 陈峰(cnfer@sina.com)

--------------------------------------------------------------------------------

来源:http://www2.ccw.com.cn/2000/0026/0026b10.asp

---- 本文详细讨论了在OpenGL中显示文本的几种方法。

----也许大多数程序员使用OpenGL更多的是将精力集中于动态三维图形应用,因此,OpenGL中的文本显示往往被忽视,使人有不见积薪之感。本文介绍了几种文本显示的方法,希望能对使用OpenGL的编程者有所帮助。

建立并修改程序
----建立一个MFC SDI Windows应用工程Text,除单文档属性外,使用其他的所有默认选择。在菜单Project打开Settings对话框,在Link属性页的 object/library modules编辑框中加入opengl32.lib glu32.lib glaux.lib三个GL库。我们利用这些库函数完成图形编辑工作。
----为使VC++的AppWizard产生的SDI应用程序能使用 OpenGL绘图,还需要作一些修改,说明如下。

----1.介绍PreCreateWindow函数

---- OpenGL窗口必须具有WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时剪裁子窗口所覆盖的区域)和WS_CLIPIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)两种风格。此外,窗口类属性不能包括CS_PARENTDC风格。具体程序实现如下:

BOOL  CTextView::PreCreateWindow
(CREATESTRUCT& cs)
{
    // TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs

//An OpenGL window must be created with the following flag
// and must not include CS_PARENTIDC for the class style.
  cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN;

    return CView::PreCreateWindow(cs);
}

----2.OnCreate函数中定义像素格式PIXELFORMAT和创建 RC

----要使窗口支持OpenGL绘图,必须对窗口进行初始化。其中包括定义像素格式PIXELFORMAT和创建RC,为OpenGL指定一个合适的像素格式,创建着色上下文并将它和窗口的设备上下文关联起来。着色上下文保存着当前着色环境的信息。可在OnCreate中调用一个自建视口成员函数SetupPixelFormat(),具体函数如下:

BOOL CTextView::SetupPixelFormat()
{
//Create a rendering context
CDC* m_pDC=GetDC();
if(m_pDC==NULL) //failure to get DC
{
 MessageBox(“Could't get a valid DC.");
 return FALSE;
}

//Default pixel format is a single-buffered,
//OpenGL support hardware-accelerated,RGBA mode format
PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),//Structure size.
   1,      
   // Structure version number.Property flags(特性标志):
  PFD_DRAW_TO_WINDOW | // support window
   PFD_SUPPORT_OPENGL | // support OpenGL
   PFD_DOUBLEBUFFER,     
   PFD_TYPE_RGBA, // RGBA type
   24,  // 32-bit color.
0, 0, 0, 0, 0, 0, // Not concerned with these:不涉及的属性
0,  // No alpha :无alpha缓存
0,  // Shift bit ignored:忽略转换位
0, 0, 0, 0, 0,// No accum buffer:没有累积缓存
   32, // 32-bit depth buffer.
0,  // No stencil:无模板缓存
0,  // No auxliliary buffers:无辅助缓存
PFD_MAIN_PLANE, // Main layer type.:主层类型
0,  // Reserved.:保留结构数
 0, 0, 0 // Unsupported.:不支持结构数
};
 int nPixelFormat=
ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd);
if( nPixelFormat ==0)
{
 MessageBox(“ChoosePixelFormat failed.");
 return FALSE; 
}

if(SetPixelFormat(m_pDC->GetSafeHdc(),
    nPixelFormat,&pfd)==0)
{
 MessageBox(“SetPixelFormat failed.");
 return FALSE; 
}

 if( (m_hRC=wglCreateContext(m_pDC->
      GetSafeHdc())) ==0)
 {
 MessageBox(“wglCreateContext failed.");
 return FALSE;
 }
 if( (wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC)) ==0)
 {
 MessageBox(“wglMakeCurrent failed.");
 return FALSE;
 }

 if(m_pDC) ReleaseDC(m_pDC);
 return TRUE;
}

----3.在OnCreate()函数中调用初始化背景函数 InitializeOpenGL()

void CTextView::InitializeOpenGL()
{
 glClearColor(0.2f,0.2f,0.2f,0.0f);
 glClearDepth(1.0);
 glDepthFunc(GL_LESS);
        glEnable(GL_DEPTH_TEST);
 glShadeModel(GL_SMOOTH);
}

----4.在OnCreate()中启动动画定时器

    SetTimer(0,40,NULL);

----5.在收到WM_SIZE消息时要重新计算场景尺寸,用OnSize 设置图形显示模式

----为了使物体能合适的显示,必须要经过投影和确定视口的工作。

void CTextView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
 
// TODO: Add your message handler code here
//Save the wide and height of the current window Client
GLsizei nWidth=(GLsizei)cx; 
GLsizei nHeight=(GLsizei)cy;
ratio=(double)cx/(double)cy;

//Coupute the aspect ratio
GLdouble dAspect=(GLdouble)nWidth/(GLdouble)nHeight;

glViewport(0,0,nWidth,nHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective
 (FOV,dAspect,NEARPLANE,FARPLANE);
  
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity();
}

----6.在OnDraw()中简单调用DrawScene()以执行OpenGL函数

void CTextView::OnDraw(CDC* pDC)
{
 CTextDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);

 // TODO: add draw code for native data here
 DrawScene();
 //Invalidate();
}

----7.撤销视窗时删除上下文并撤销定时器

void CTextView::OnDestroy()
{
 CView::OnDestroy();
 
     // TODO: Add your message handler code here
     //This call makes the current RC not current
 if(wglMakeCurrent(0,0)==FALSE)
 MessageBox(“wglMakeCurrent failed.");
 
 //delete the RC
 if(m_hRC && (wglDeleteContext(m_hRC)==FALSE))
 MessageBox(“wglDeleteContext fail."); 
 KillTimer(1); 
}

----8.当40ms定时器时间到时,简单地将整个场景的客户区置无效,使之重画

void CTextView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
Invalidate();
CView::OnTimer(nIDEvent);
}

修改界面
---- 删除菜单IDR_MAINFRAME中File下除去Exit菜单项外的所有选项,添加“显示GDI文字” “列表制作文字”“列表三维文字”三个菜单项,并给他们分配适当的ID。使用ClassWizard为这三个菜单项在视类中添加命令处理函数和界面更新函数,其中显示GDI文字的对应的函数如下:
void CTextView::OnGdiText()
{
    // TODO: Add your command handler code here
 m_iWhichText=0;
 Invalidate();
}

void CTextView::OnUpdateGdiText(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
if(m_iWhichText==0) pCmdUI->SetCheck();
else pCmdUI->SetCheck(0);
}

----增加Draw3DText()、DrawListText()和DrawGdiText()三个函数用于三种不同的文本绘制方法。增加DrawScene()函数,它被OnDraw函数调用,用于绘制场景。在DrawScene()函数中,当m_iWhichText为0、1、2时,分别调用 DrawGdiText()、DrawListText()和Draw3DText()显示文本。具体函数实现如下:

void CTextView::OnDraw(CDC* pDC)
{
 CTextDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);

     // TODO: add draw code for native data here
 DrawScene();
     //Invalidate();
}

void CTextView::DrawScene()
{
 glClear
(GL_COLOR_BUFFERBIT|GL_DEPTH_BUFFER_BIT);
 
 glPushMatrix();
        glTranslatef(0.0f,0.0f,-FARPLANE);
 //TextureMap();
 glPopMatrix(); 
 glPushMatrix();
 glTranslatef
 (0.0f,0.0f,-(FARPLANE+NEARPLANE)/2);

 if(m_iWhichText==1) DrawListText();
 if(m_iWhichText==2) Draw3DText();
  glPopMatrix();
 glFinish();
 SwapBuffers(wglGetCurrentDC());

 if(m_iWhichText==0) DrawGdiText();
}

GDI 显示文本
---- 调用wglGetCurrentDC()函数取得当前的设备上下文,使用TextOut函数显示文本,不过要注意在DoubleBuffer模式下,绘制函数要在glFinish()和 SwapBuffers(wglGetCurrentDC())函数之后调用,否则会产生闪烁,在绘制OpenGL结束之前使用GDI函数,要除去闪烁则只能使用SingleBuffer模式,具体函数如下:
void CTextView::DrawGdiText()
{
HDC  hdc=wglGetCurrentDC();
::SetBkMode( hdc, TRANSPARENT );
::SetTextColor( hdc, RGB(250,0,0) );
 
CString sState(“显示GDI文本。");
::TextOut(hdc,5,5,sState,sState.GetLength());
}

wglUseFontBitmaps
函数显示文字
----使用wglUseFontBitmaps()将ASCII字符装入显示列表,然后使用glCallLists()函数利用显示列表序列显示文本。wglUseFontBitmaps有四个参数,分别是当前使用的DC、从第几个ASCII字符起始装入列表、装入列表的ASCII字符数和起始的列表序号。glListBase()指定glCallLists执行的起始列表序列号。glCallLists()含有三个参数:执行列表序列的个数、列表值的类型和所要显示的文本。注意如果所要显示的文本是字符串,它所提供的信息是相对于起始装入ASCII字符的偏移量,因此最终所显示的ASCII字符是从glListBase()所指定的列表起始号在经过glCallLists()中偏移后的列表,因此wglUseFontBitmaps的从第几个ASCII字符起始装入列表参数、glListBase()指定的 glCallLists执行的起始列表序列号和glCallLists()中的所要显示的文本参数都可以影响最终显示结果。由于显示的是ASCII 字符,因此不能显示汉字。glRasterPos3f函数决定在 OpenGL视景体坐标系下的偏移。具体函数实现如下:
void CTextView::DrawListText()
{
wglUseFontBitmaps(wglGetCurrentDC(),0,256,1000);
glListBase(1000);
glRasterPos3f(-5.0f,0.0f,0.0f);
glCallLists(20,GL_UNSIGNED
 _BYTE,“Draw with List Text.");
}

wglUseFontOutlines
函数显示三维文字
----wglUseFontOutlines使得OpenGL可以显示三维文字。它的用法与wglUseFontBitmaps函数大致相同,但是多了内插计算参数、字体深度、显示方式和装载字模的缓存四个参数,且只能显示TrueType字体,显示前应该先选择字体类型。具体函数实现如下:
void CTextView::Draw3DText()
{
GLYPHMETRICSFLOAT agmf[256];
// create display lists for glyphs 0 through 255
// with 0.1 extrusion and default deviation.
//The display list numbering starts at 1000
  (it could be any number)
wglUseFontOutlines(wglGetCurrentDC(),
   0,255,1000,0.3f,0.8f, WGL_FONT_LINES ,agmf);
 
// Set up transformation to draw the string
 glTranslatef(-15.0f,0.0f,0.0f);
 glScalef(4.0f, 4.0f, 4.0f);
 // Display a string
 glListBase(1000);
// Indicates the start of display lists for the glyphs
// Draw the characters in a string
 
glCallLists(26, GL_UNSIGNED_BYTE,
 “Draw outline list 3D text."); 
}


 

在Qt使用OpenGL显示文字可以通过使用QOpenGLWidget和QOpenGLFunctions类实现。以下是一个简单的示例: 首先,在你的Qt项目添加一个QOpenGLWidget控件,并在其构造函数启用OpenGL功能: ``` MyGLWidget::MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { // 启用OpenGL功能 initializeOpenGLFunctions(); } ``` 接下来,你需要编写一个代码片段来生成一个纹理,该纹理包含所需的文本。你可以使用Qt的QPainter类将文本渲染到QImage,并使用QOpenGLFunctions类将其转换为OpenGL纹理。以下是一个示例: ``` void MyGLWidget::createTextTexture() { // 创建QImage并使用QPainter将文本渲染到其 QImage textImage(256, 256, QImage::Format_RGBA8888); textImage.fill(Qt::transparent); QPainter painter(&textImage); painter.setPen(Qt::white); painter.setFont(QFont("Arial", 24)); painter.drawText(textImage.rect(), Qt::AlignCenter, "Hello, world!"); // 将QImage转换为OpenGL纹理 glGenTextures(1, &m_textTexture); glBindTexture(GL_TEXTURE_2D, m_textTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textImage.width(), textImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, textImage.constBits()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } ``` 现在,你需要在OpenGL上下文绘制纹理。在QOpenGLWidget的paintGL()函数,你可以使用QOpenGLFunctions类启用纹理映射,并使用glBindTexture()将纹理绑定到当前的纹理单元。然后,你可以使用glBegin()和glEnd()函数绘制一个矩形,并将纹理坐标与顶点坐标一起传递给OpenGL。以下是一个示例: ``` void MyGLWidget::paintGL() { // 启用纹理映射 glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_textTexture); // 绘制矩形并绑定纹理坐标 glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); glEnd(); } ``` 现在,你已经可以在Qt使用OpenGL显示文字了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值