在纹理上写文本

本文介绍了一种在三维舰船模型上标记文本的方法,通过转换纹理贴图格式并在其上绘制舷号等文本信息,实现不同船只的个性化标识。
部署运行你感兴趣的模型镜像

        本人在开发三维视景类项目时遇到了这样的问题,需要在舰船模型上标明它的舷号,不同的船只有不同的号,而在加载模型时相同类型的船是按照同一方式加载的,于是笔者自然想到在舰艇舷号附近的纹理贴图上直接标上文本,这样舷号自然会显示出来。由于目前使用的是RGB格式的纹理,所以需要将它转成BMP格式,然后绘制文本,再转成RGB格式。
源代码如下:
EditImage.h
#include "dib.h"
/////////////////////////////////////////////////////////////////////////////
class CEditImage : public CEdit
{
// Construction
public:
 CEditImage();

// Attributes
public:

// Operations
public:

// Overrides
 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CEditImage)
 //}}AFX_VIRTUAL

// Implementation
public:
 virtual ~CEditImage();

 // Generated message map functions
protected:
 //{{AFX_MSG(CEditImage)
 afx_msg void OnPaint();
 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 afx_msg void OnSize(UINT nType, int cx, int cy);
 //}}AFX_MSG

 DECLARE_MESSAGE_MAP()
public:
 CDib* m_pDib;
 CRect m_rect;
 COLORREF m_clrText;
 BOOL m_bShowImage;
 BOOL SetImage(CString strName);

 BOOL SaveBMPByDC(CString strFile);
private:
 CDC* m_pDC;
 CDC* m_pMemDC;
 CBitmap* m_pBitmap;

 void Convert(CString sFileName);
 void RGBtoBMP(BYTE* pRGB,int nType,int nNum);
 BYTE* m_pRGB;
 CString m_sNote;
 BOOL    m_bBlend;
public:
 BOOL WriteToRGB(CString sOriginName,CString sDestName,COLORREF clr,CRect rect,CString sNote);
};
EditImage.cpp

CEditImage::CEditImage()
{
 m_bShowImage=false;
 m_rect.left=100;
 m_rect.top=10;
 m_rect.right=200;
 m_rect.bottom=200;
 m_clrText=RGB(255,0,0);

 m_pBitmap=NULL;
 m_pDC=NULL;
 m_pMemDC=NULL;
 m_pRGB = NULL;

 m_sNote="Hello";
 m_pDib=NULL;
}

CEditImage::~CEditImage()
{

 if(m_pMemDC)
 {
  delete m_pMemDC;
 }
 if(m_pDC)
  delete m_pDC;
 if(m_pBitmap)
 {
  m_pBitmap->DeleteObject();
  delete m_pBitmap;
 }
 if(m_pRGB)
  delete m_pRGB;

 if(m_pDib)
  delete m_pDib;
}


BEGIN_MESSAGE_MAP(CEditImage, CEdit)
 //{{AFX_MSG_MAP(CEditImage)
 ON_WM_PAINT()
 ON_WM_CREATE()
 ON_WM_SIZE()
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEditImage message handlers

void CEditImage::OnPaint()
{
 CPaintDC dc(this); // device context for painting
 
 // TODO: Add your message handler code here
 if(m_bShowImage)
 {
  m_pMemDC->SelectObject(m_pBitmap);
  CSize cs;
  cs.cx=m_pDib->bmSize.cx;
  cs.cy=m_pDib->bmSize.cy;
  m_pDib->Stretch(m_pMemDC,CPoint(0,0),cs);
  m_pMemDC->SetTextColor(m_clrText);
  CBrush* oldBrush=(CBrush*)m_pMemDC->SelectStockObject(NULL_BRUSH);
  m_pMemDC->SetBkMode(TRANSPARENT);
  LOGFONT lfFont;
  CFont fontNew, *fontOld;
  ZeroMemory(&lfFont, sizeof(lfFont));
  lstrcpy(lfFont.lfFaceName, "Arial");
  lfFont.lfHeight = m_rect.Height();
  lfFont.lfWidth = m_rect.Width();
  fontNew.CreateFontIndirect(&lfFont);
  fontOld=m_pMemDC->SelectObject(&fontNew);
  m_pMemDC->TextOut(m_rect.left,m_rect.top,m_sNote);
  m_pDC->BitBlt(0,0,cs.cx,cs.cy,m_pMemDC,0,0,SRCCOPY);

  m_pMemDC->SelectObject(oldBrush);
  m_pMemDC->SelectObject(fontOld);
 }
}
BOOL CEditImage::SetImage(CString strName)
{
 if(m_pDib)
  delete m_pDib;
 m_pDib=new CDib();
 m_bShowImage=false;
 if(m_pDib->ReadFile(strName))
 { 
  CSize cs;
  cs.cx=m_pDib->bmSize.cx;
  cs.cy=m_pDib->bmSize.cy;
  MoveWindow(0,0,cs.cx,cs.cy,true);
  m_bShowImage=true;
  Invalidate(false);
 } 
 else
  m_bShowImage=false;

 return m_bShowImage;
}

BOOL CEditImage::SaveBMPByDC(CString strFile)
{
 
 BITMAP btm;
 m_pBitmap->GetBitmap(&btm);
 DWORD size=btm.bmWidthBytes*btm.bmHeight;
 LPSTR lpData=(LPSTR)GlobalAllocPtr(GPTR,size);
/////////////////////////////////////////////
 BITMAPINFOHEADER bih;
 bih.biBitCount=btm.bmBitsPixel;
 bih.biClrImportant=0;
 bih.biClrUsed=0;
 bih.biCompression=0;
 bih.biHeight=btm.bmHeight;
 bih.biPlanes=1;
 bih.biSize=sizeof(BITMAPINFOHEADER);
 bih.biSizeImage=size;
 bih.biWidth=btm.bmWidth;
 bih.biXPelsPerMeter=0;
 bih.biYPelsPerMeter=0;
///////////////////////////////////
 HBITMAP bm;
 bm=(HBITMAP)m_pBitmap->GetSafeHandle();
 GetDIBits(m_pMemDC->GetSafeHdc(),bm,0,bih.biHeight,lpData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
// bm.GetBitmapBits(size,lpData); //此函数在处理5-5-5模式的16位色下会出现颜色混乱
//////////////////////////////
 if(bih.biBitCount<24)
  return FALSE;
 BITMAPFILEHEADER bfh;
 bfh.bfReserved1=bfh.bfReserved2=0;
 bfh.bfType=((WORD)('M'<< 8)|'B');
 bfh.bfSize=54+size;
 bfh.bfOffBits=54;
 ///////rgb/////////
 
 CFile bfr;
 if(bfr.Open(strFile,CFile::modeCreate|CFile::modeWrite))
 {
  int nWidth=bih.biWidth;
  int nHeight=bih.biHeight;
  byte* tmpByte;
  if(!m_bBlend)
  {  
   tmpByte=new byte[nWidth*nHeight*3];
   for(int i=0;i<nWidth*nHeight;i++)
   {
    tmpByte[i*3+0] = lpData[i*4+2];
    tmpByte[i*3+1] = lpData[i*4+1];
    tmpByte[i*3+2] = lpData[i*4+0];
   }
  }
  else
  {
   tmpByte=new byte[nWidth*nHeight*4];
   for(int i=0;i<nWidth*nHeight;i++)
   {
    tmpByte[i*4+0] = lpData[i*4+2];
    tmpByte[i*4+1] = lpData[i*4+1];
    tmpByte[i*4+2] = lpData[i*4+0];
    if( tmpByte[i*4+0] == 0x00 && tmpByte[i*4+1] == 0x00 && tmpByte[i*4+2] == 0x00 )
     tmpByte[i*4+3]=0;
    else
     tmpByte[i*4+3]=255;
   }
  }
  
  bfr.Write(&nWidth,sizeof(int));
  bfr.Write(&nHeight,sizeof(int));
  bfr.Write(&m_bBlend,sizeof(BOOL));
  if(m_bBlend)
   bfr.Write(tmpByte,nWidth*nHeight*4);
  else
   bfr.Write(tmpByte,nWidth*nHeight*3);
  bfr.Close();
  delete tmpByte;

 }
 /////////////////////
 GlobalFreePtr(lpData);
 return TRUE;
}

int CEditImage::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CEdit::OnCreate(lpCreateStruct) == -1)
  return -1;
 
 // TODO: Add your specialized creation code here
 
 return 0;
}

void CEditImage::OnSize(UINT nType, int cx, int cy)
{
 CEdit::OnSize(nType, cx, cy);
 
 // TODO: Add your message handler code here
 if(m_pDC)
  delete m_pDC;
 m_pDC=new CClientDC(this);
 if(m_pMemDC)
  delete m_pMemDC;
 m_pMemDC=new CDC;
 m_pMemDC->CreateCompatibleDC(m_pDC);
 
 if(m_pBitmap)
 {
  m_pBitmap->DeleteObject();
  delete m_pBitmap;
  m_pBitmap=NULL;
 }
 m_pBitmap=new CBitmap;
 m_pBitmap->CreateCompatibleBitmap(m_pDC,cx,cy);
}
BOOL CEditImage::WriteToRGB(CString sOriginName,CString sDestName,COLORREF clr,CRect rect,CString sNote)
{
 m_clrText=clr;
 m_rect.CopyRect(&rect);
 m_sNote=sNote;
 Convert(sOriginName);
 return SaveBMPByDC(sDestName);
}
void CEditImage::Convert(CString sFileName)
{
 CString strName = sFileName;
 strName = strName.Left(strName.Find('.'));
 //读RGB文件
 CFile fl;
 if(!fl.Open(strName+".RGB",CFile::modeRead))return;

 int iWidth,iHeight;
 
 fl.Read(&iWidth,sizeof(int));
 fl.Read(&iHeight,sizeof(int));
 fl.Read(&m_bBlend,sizeof(BOOL));
 
 if(m_pRGB) delete m_pRGB;

 if(m_bBlend)
 {
  m_pRGB = new BYTE[iWidth*iHeight*4];
  fl.Read(m_pRGB,iWidth*iHeight*4);
  RGBtoBMP(m_pRGB,4,iWidth*iHeight);
 }
 else
 {
  m_pRGB = new BYTE[iWidth*iHeight*3];
  fl.Read(m_pRGB,iWidth*iHeight*3);
  RGBtoBMP(m_pRGB,3,iWidth*iHeight);
 }
 fl.Close();
 //写BMP文件

 BITMAPFILEHEADER pFileHeader;
    pFileHeader.bfType = 0x4d42;
    pFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
    pFileHeader.bfReserved1 = 0;
    pFileHeader.bfReserved2 = 0;
    pFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
 //
 BITMAPINFOHEADER bmi;
 bmi.biSize = sizeof(BITMAPINFOHEADER);
 bmi.biWidth = iWidth;
 bmi.biHeight = iHeight;
 bmi.biPlanes = 1;
 bmi.biBitCount = 24;
 bmi.biCompression = BI_RGB;
 bmi.biSizeImage = 0;
 bmi.biXPelsPerMeter = 0;
 bmi.biYPelsPerMeter = 0;
 bmi.biClrUsed = 0;
 bmi.biClrImportant = 0;

 fl.Open(strName+".BMP",CFile::modeCreate | CFile::modeWrite);
 fl.Write(&pFileHeader,sizeof(BITMAPFILEHEADER));
 fl.Write(&bmi,sizeof(BITMAPINFOHEADER));
 fl.Write(m_pRGB,iWidth*iHeight*3);
 fl.Close();
 
 SetImage(strName+".BMP");
 OnPaint();
}
void CEditImage::RGBtoBMP(BYTE* pRGB,int nType,int nNum)
{
    BYTE* pBmp = new BYTE[nNum*3];

 for(int i = 0;i < nNum;i++)
 {
  pBmp[i*3+2] = pRGB[i*nType+0];
  pBmp[i*3+1] = pRGB[i*nType+1];
  pBmp[i*3+0] = pRGB[i*nType+2];
 }
 
 memcpy(pRGB,pBmp,nNum*3*sizeof(BYTE));
 delete pBmp;
}
说明:
此类的基类是一个CEdit,为的是利用它的窗口句柄生成位图并显示保存。
调用方法:
////////////初始化RGB写编号/////////////////
 m_pTexManage->m_ctrlEditImage->Create(WS_CHILD|WS_VISIBLE,CRect(0,0,10,10),this,ID_EDIT_WRITERGB);
 m_pTexManage->m_ctrlEditImage->ShowWindow(SW_HIDE);
 ////////////////////////////////////////////

m_pTexManage->m_ctrlEditImage->WriteToRGB(Origin.RGB",sDest,RGB(r,g,b),CRect(0,0,100,50),”Hello”);
参数说明:源文件,目标文件,字体颜色,字体左上角位置,字体宽度,高度,文本 

您可能感兴趣的与本文相关的镜像

EmotiVoice

EmotiVoice

AI应用

EmotiVoice是由网易有道AI算法团队开源的一块国产TTS语音合成引擎,支持中英文双语,包含2000多种不同的音色,以及特色的情感合成功能,支持合成包含快乐、兴奋、悲伤、愤怒等广泛情感的语音。

<think>我们正在讨论使用Qt的QOpenGLExtraFunctions通过纹理图集实现文本绘制。纹理图集(TextureAtlas)是一种将多个小纹理(比如字符图像)打包到一个大纹理中的技术,这样可以减少纹理切换次数,提高绘制效率。步骤概述:1.创建纹理图集:将需要绘制的字符(通常是一个字体集)预先渲染到一个大的纹理中,并记录每个字符在图集中的位置(纹理坐标)。2.初始化OpenGL:使用QOpenGLWidget或QOpenGLWindow作为渲染上下文,并初始化QOpenGLExtraFunctions来访问OpenGLES3.0+的功能。3.设置着色器:编顶点和片段着色器,用于渲染带纹理的四边形(每个字符对应一个四边形)。4.创建顶点缓冲区:为每个字符准备一个四边形(两个三角形),并设置纹理坐标。5.渲染文本:对于要绘制的每个字符,根据纹理图集中的位置计算纹理坐标,然后绘制对应的四边形。详细步骤:1.创建纹理图集:我们可以使用Qt的QFont和QPainter将字符绘制到一个QImage上,然后将该图像上传为OpenGL纹理。例如,我们可以选择ASCII字符集(32-126),并为每个字符分配一个固定大小的区域(比如64x64像素)。然后,将整个图像作为纹理加载。2.初始化OpenGL环境:在Qt中,我们可以继承QOpenGLWidget并重initializeGL、resizeGL和paintGL方法。在initializeGL中,初始化QOpenGLExtraFunctions对象,并加载纹理图集。3.着色器程序:我们需要一个简单的着色器程序来渲染纹理四边形。顶点着色器将位置和纹理坐标作为输入,并输出到片段着色器。片段着色器采样纹理并输出颜色。4.顶点数据:每个字符由一个四边形(两个三角形)表示,每个顶点包含位置和纹理坐标。我们可以使用一个VBO(顶点缓冲区对象)来存储所有字符的顶点数据,或者动态生成。5.渲染:在paintGL中,对于要绘制的字符串,遍历每个字符,根据字符在图集中的位置计算纹理坐标,然后绘制对应的四边形。考虑到性能,我们可以将整个字符串的顶点数据一次性上传到GPU(使用动态VBO)并一次绘制。具体代码结构示例:在初始化阶段:-创建纹理图集(生成QImage,然后转换为OpenGL纹理)。-编译链接着色器程序。-创建VBO和VAO(顶点数组对象)。在绘制阶段:-对于每个要绘制的字符串,计算每个字符的四边形顶点(包括位置和纹理坐标)。-将顶点数据上传到VBO。-绑定纹理和着色器程序。-设置必要的OpenGL状态(如混合,用于支持透明)。-绘制四边形。注意:字符的位置计算需要根据字符的图集位置和屏幕位置进行转换。下面是一个简化的代码框架:```cpp//在OpenGL窗口/部件的类中classMyGLWidget:publicQOpenGLWidget,protectedQOpenGLExtraFunctions{public:MyGLWidget(QWidget*parent=nullptr);~MyGLWidget();protected:voidinitializeGL()override;voidpaintGL()override;voidresizeGL(intwidth,intheight)override;private:voidcreateTextureAtlas();voidsetupShader();voidrenderText(constQString&text,floatx,floaty,floatscale);GLuintm_textureAtlasID;QOpenGLShaderProgram*m_program;GLuintm_vao,m_vbo;//其他必要的变量};```在initializeGL中:```cppvoidMyGLWidget::initializeGL(){initializeOpenGLFunctions();//初始化QOpenGLExtraFunctions//设置OpenGL状态glClearColor(0.1f,0.1f,0.1f,1.0f);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);//创建纹理图集createTextureAtlas();//设置着色器setupShader();//创建VAO和VBOglGenVertexArrays(1,&m_vao);glGenBuffers(1,&m_vbo);glBindVertexArray(m_vao);glBindBuffer(GL_ARRAY_BUFFER,m_vbo);//初始化VBO大小(例如,预分配空间),但这里我们在渲染时动态填充//设置顶点属性指针//位置属性glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)0);glEnableVertexAttribArray(0);//纹理坐标属性glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)(2*sizeof(float)));glEnableVertexAttribArray(1);glBindBuffer(GL_ARRAY_BUFFER,0);glBindVertexArray(0);}```创建纹理图集的函数(简化):```cppvoidMyGLWidget::createTextureAtlas(){//假设我们创建一个1024x1024的图集,包含16x16个字符(每个字符64x64)QImageatlasImage(1024,1024,QImage::Format_RGBA8888);atlasImage.fill(Qt::transparent);QPainterpainter(&atlasImage);QFontfont("Arial",48);//使用大号字体以便清晰painter.setFont(font);painter.setPen(Qt::white);//记录每个字符的纹理坐标(这里我们只存储了开始位置,实际需要每个字符的四个角的坐标)//在实际应用中,我们需要一个结构来存储每个字符的纹理坐标信息//例如,我们只绘制一个字符'A'QRectcharRect(0,0,64,64);painter.drawText(charRect,Qt::AlignCenter,"A");painter.end();//生成纹理glGenTextures(1,&m_textureAtlasID);glBindTexture(GL_TEXTURE_2D,m_textureAtlasID);glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,atlasImage.width(),atlasImage.height(),0,GL_RGBA,GL_UNSIGNED_BYTE,atlasImage.bits());glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glBindTexture(GL_TEXTURE_2D,0);}```着色器设置:```cppvoidMyGLWidget::setupShader(){m_program=newQOpenGLShaderProgram(this);m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,"#version330core\n""layout(location=0)invec2aPos;\n""layout(location=1)invec2aTexCoord;\n""outvec2TexCoord;\n""voidmain(){\n""gl_Position=vec4(aPos,0.0,1.0);\n""TexCoord=aTexCoord;\n""}\n");m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,"#version330core\n""invec2TexCoord;\n""outvec4FragColor;\n""uniformsampler2DtextureAtlas;\n""voidmain(){\n""FragColor=texture(textureAtlas,TexCoord);\n""}\n");m_program->link();}```渲染文本函数(示例,只绘制一个字符):```cppvoidMyGLWidget::renderText(constQString&text,floatx,floaty,floatscale){//激活程序m_program->bind();glBindVertexArray(m_vao);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,m_textureAtlasID);m_program->setUniformValue("textureAtlas",0);//对于每个字符,我们生成6个顶点(两个三角形)//这里我们只绘制一个字符'A',假设它在图集的位置为(0,0)到(64/1024,64/1024)的纹理坐标//屏幕位置:x,y,字符大小假设为scale*64floatw=64*scale;floath=64*scale;//顶点数据:位置和纹理坐标floatvertices[]={//位置//纹理坐标x,y,0.0f,0.0f,x+w,y,64.0f/1024.0f,0.0f,x,y+h,0.0f,64.0f/1024.0f,x,y+h,0.0f,64.0f/1024.0f,x+w,y,64.0f/1024.0f,0.0f,x+w,y+h,64.0f/1024.0f,64.0f/1024.0f};glBindBuffer(GL_ARRAY_BUFFER,m_vbo);glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_DYNAMIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER,0);//解绑,因为已经复制到VAOglDrawArrays(GL_TRIANGLES,0,6);glBindVertexArray(0);m_program->release();}```在paintGL中调用:```cppvoidMyGLWidget::paintGL(){glClear(GL_COLOR_BUFFER_BIT);renderText("A",100,100,1.0f);}```注意:以上代码是简化示例,实际应用中需要处理多个字符,并且需要预先计算每个字符在图集中的纹理坐标(通常用一个结构存储)。此外,对于中文字符集,字符数量庞大,需要更大的图集或动态图集管理。以上方法实现了基本的纹理图集文本绘制。在Qt中,也有其他方法(如QOpenGLTextureGlyphCache)用于文本渲染,但使用QOpenGLExtraFunctions直接操作可以更灵活。引用说明:在实现纹理图集时,我们使用了Qt的QImage和QPainter来生成纹理,这类似于引用[2]中提到的“masks”和“filters”处理方式,但我们的示例中未涉及这些高级特性。另外,引用[1]提到当前只支持Qt-Mac和GLX,但我们的代码基于QOpenGLWidget,在支持OpenGL的平台上均可运行(包括Windows、Linux和Mac)[^1][^2]。相关问题:§§相关问题§§1.如何优化纹理图集以减少内存占用?2.如何处理动态添加的字符(如新出现的字符)?3.在Qt中,除了使用QOpenGLExtraFunctions,还有哪些文本渲染方法?</think>### Qt中使用QOpenGLExtraFunctions实现纹理图集文本绘制 在Qt中通过纹理图集实现高效文本绘制,主要步骤如下: #### 1. **创建纹理图集** ```cpp // 创建包含所有字符的大纹理 QImage atlas(2048, 2048, QImage::Format_RGBA8888); atlas.fill(Qt::transparent); QPainter painter(&atlas); painter.setFont(QFont("Arial", 48)); painter.setPen(Qt::white); // 存储字符纹理坐标 QHash<QChar, QRectF> glyphRects; const QString charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (int i = 0; i < charSet.length(); ++i) { QChar ch = charSet[i]; QRectF rect = painter.boundingRect(QRectF(), Qt::AlignLeft, QString(ch)); painter.drawText(rect.topLeft(), QString(ch)); glyphRects[ch] = rect; } ``` #### 2. **初始化OpenGL纹理** ```cpp void initializeGL() override { initializeOpenGLFunctions(); glGenTextures(1, &m_textureAtlas); glBindTexture(GL_TEXTURE_2D, m_textureAtlas); // 设置纹理参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // 上传纹理数据 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, atlas.width(), atlas.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, atlas.constBits()); } ``` #### 3. **创建着色器程序** 顶点着色器 (`text.vert`): ```glsl #version 330 core layout(location = 0) in vec2 vertex; layout(location = 1) in vec2 texCoord; out vec2 vTexCoord; uniform mat4 projection; void main() { gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); vTexCoord = texCoord; } ``` 片段着色器 (`text.frag`): ```glsl #version 330 core in vec2 vTexCoord; out vec4 fragColor; uniform sampler2D atlas; uniform vec4 textColor; void main() { vec4 sampled = vec4(1.0, 1.0, 1.0, texture(atlas, vTexCoord).r); fragColor = textColor * sampled; } ``` #### 4. **渲染文本** ```cpp void renderText(const QString& text, float x, float y, float scale) { m_program->bind(); glBindVertexArray(m_vao); glBindTexture(GL_TEXTURE_2D, m_textureAtlas); // 设置投影矩阵 QMatrix4x4 projection; projection.ortho(0, width(), height(), 0, -1, 1); m_program->setUniformValue("projection", projection); m_program->setUniformValue("textColor", QVector4D(1, 1, 1, 1)); // 为每个字符生成四边形 std::vector<float> vertices; for (auto ch : text) { const QRectF& rect = glyphRects[ch]; // 计算顶点和纹理坐标 float xpos = x + rect.left() * scale; float ypos = y - rect.top() * scale; float w = rect.width() * scale; float h = rect.height() * scale; // 添加两个三角形(6个顶点) addGlyphQuad(vertices, xpos, ypos, w, h, rect); x += rect.width() * scale; } // 上传顶点数据并绘制 glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 4); } ``` #### 5. **关键优化点** 1. **批处理渲染**:一次性上传所有字符顶点数据 2. **纹理过滤**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值