通过这篇介绍,希望可以帮到大家更好的绘制3d图形
QT6与OpenGL的结合
1. Qt 6提供了许多增强型模块,如Qt Quick 3D和Qt 3D,这些模块可以直接与OpenGL进行交互,使得在Qt 6中使用OpenGL进行图形渲染变得更加高效和便捷。
2. 要在Qt 6中使用OpenGL,首先需要在项目中包含相应的OpenGL库。
pro文件加入:QT += charts opengl
win32: LIBS += -lOpenGL32
win32: LIBS += -lGLU32
unix: LIBS += -lglut -lGLU
3. 在Qt 6中,可以通过多种方式对OpenGL渲染性能进行优化
4. 利用OpenGL的纹理管理,减少CPU到GPU的数据传输
5. 在Qt 6中,可以通过多种方式对OpenGL渲染性能进行优化
6. 操作简单
在QOpenGLWidget组件上渲染opengl:继承QOpenGLWidget和QOpenGLFunctions_4_5_Core
实现三个虚函数:initializeGL、resizeGL、paintGL
函数介绍
glViewport: 重置当前的视口
glMatrixMode 选择投影矩阵
gluPerspective 建立透视投影矩阵,基于窗口宽度和高度
glTranslatef 平移
glRotatef 旋转
glPushMatrix 设置新的状态
gluProject 坐标转换;比如将三维坐标投影到屏幕,或者鼠标坐标转换为世界坐标等
参考:https://learn.microsoft.com/zh-cn/windows/win32/opengl/glrecti
绘图介绍
1. 绘制坐标系和网格
glPushMatrix();
{
glLineWidth(1.5);
//沿x,y,z轴方向的平移, 平移到原点
glTranslatef(m_translate.x, m_translate.y, m_translate.z);
glBegin(GL_LINES); //绘制坐标轴
{
glEnable(GL_LINE_SMOOTH);
//从原点绘制三条线
glColor3f(1, 0, 0); glVertex3f(0, 0, 0); glVertex3f(m_Size.x, 0, 0); //X
glColor3f(0, 1, 0); glVertex3f(0, 0, 0); glVertex3f(0, m_Size.y, 0); //Y
glColor3f(0, 0, 1); glVertex3f(0, 0, 0); glVertex3f(0, 0, m_Size.z); //Z
}
glEnd();
//绘制网格
glColor3f(1, 1, 1);
glLineWidth(1.0f);
//绘制xy平面的网格, m_xyGridNum网格的横竖的数量
paintXYGrid(m_xyGridNum[0], m_xyGridNum[1]);
//绘制yz平面的网格, m_yzGridNum网格的横竖的数量
paintYZGrid(m_yzGridNum[0], m_yzGridNum[1]);
}
glPopMatrix();
paintXYGrid:函数
//计算分割成vNum数量,每一个网格的距离
float xSpacing = m_Size.x / vNum;
float ySpacing = m_Size.y / hNum;
glBegin(GL_LINES);
glEnable(GL_LINE_SMOOTH);
for(int i = 1; i <= hNum; ++i)
{
float y = ySpacing * i;
//绘制线
glVertex3f(0, y, 0);
glVertex3f(m_Size.x, y, 0);
}
for(int i = 1; i <= vNum; ++i)
{
float x = xSpacing * i;
glVertex3f(x, 0, 0);
glVertex3f(x, m_Size.y, 0);
}
glEnd();
paintYZGrid:函数
float zSpacing = m_Size.z / vNum;
float ySpacing = m_Size.y / hNum;
glBegin(GL_LINES);
glEnable(GL_LINE_SMOOTH);
for(int i = 1; i <= hNum; ++i)
{
float y = ySpacing * i;
glVertex3f(0, y, 0);
glVertex3f(0, y, m_Size.z);
}
for(int i = 1; i <= vNum; ++i)
{
float z = zSpacing * i;
glVertex3f(0, 0, z);
glVertex3f(0, m_Size.y, z);
}
glEnd();
2. 坐标轴箭头
// 坐标轴箭头
GLUquadricObj *objCylinder = gluNewQuadric();
glPushMatrix(); //X
{
glColor3f(1, 0, 0);
//先平移到要绘制的三维坐标的点
glTranslatef(m_translate.x + m_Size.x, m_translate.y, m_translate.z);
glRotatef(90, 0, 1, 0); //按照y轴旋转90
gluCylinder(objCylinder, 0.06, 0.0, 0.4, 50, 1);
}
glPopMatrix();
glPushMatrix(); //Y
{
glColor3f(0, 1, 0);
glTranslatef(m_translate.x, m_translate.y+m_Size.y, m_translate.z);
glRotatef(-90, 1, 0, 0.0); // 沿着x轴转90度 使得y轴对着原z轴方向
gluCylinder(objCylinder, 0.06, 0.0, 0.4, 50, 1);
}
glPopMatrix();
glPushMatrix(); //Z
{
glColor3f(0, 0, 1);
glTranslatef(m_translate.x, m_translate.y, m_translate.z+m_Size.z);
gluCylinder(objCylinder, 0.06, 0.0, 0.4, 50, 1);
}
glPopMatrix();
3. 绘制3d文件
通过多条线绘制一个立体的文字
//生成文字绘制路径
QPainterPath path;
path.addText(QPointF((start.x+start.y+start.z)/2, 0), QFont("Microsoft YaHei UI", 2), text);
//转换成多边形列表
QList<QPolygonF> poly = path.toSubpathPolygons();
QList<QPolygonF>::iterator iter = poly.begin();
glLineWidth(1);
glColor3f(color.redF(), color.greenF(), color.blueF());
while(iter != poly.end())
{
glBegin(GL_LINE_LOOP);
{
QPolygonF::iterator it = (*iter).begin();
while(it != iter->end())
{
glVertex3f(start.x+0.02, start.y - it->ry() * 0.1, start.z - it->rx() * 0.1);
it++;
}
}
glEnd();
iter++;
}
4. 利用texture绘制2d文字
两种方式:1. 通过图片加载到texture绘制文字
2. 通过文本生成QImage贴图到texture
利用:QOpenGLShaderProgram和QOpenGLTexture绘制。QOpenGLShaderProgram是QT中用于管理OpenGL着色器程序的类;QOpenGLTexture
来进行纹理操作。
工具类:
class CPaint2DTexture
{
public:
CPaint2DTexture();
virtual ~CPaint2DTexture();
void createImage(QString& name, QFont &font, QColor& color);
void createImage(QString& filePath);
void resizeSize(int w, int h);
void createTexture();
public:
QOpenGLTexture* texture;
QImage* img;
int width;
int height;
};
class CPaint2DTextTool
{
public:
CPaint2DTextTool(int n, bool occlusion = true);
virtual ~CPaint2DTextTool();
void initShaderProgram();
//通过文本绘制:index绘制在哪个texture;name绘制的文字;font字体;color颜色
CPaint2DTexture* setText(int index, QString& name, QFont &font, QColor& color);
//通过文件绘制:index绘制在哪个texture;filePath图片(png)路径
CPaint2DTexture* setText(int index, QString& filePath);
void resizeSize(int index, int w, int h);
void createTextures();
CPaint2DTexture* getTexture(int index);
void getSize(int index, GLdouble* weight, GLdouble* height);
/**
* @brief drawTexture 真正绘制在窗口的函数
* @param index 绘制texture
* @param wx 表示绘制的x坐标
* @param wy 表示y坐标
* @param xOffset x偏移值
* @param yOffset y偏移值
* @return
*/
bool drawTexture(int index, GLdouble wx, GLdouble wy, GLdouble xOffset, GLdouble yOffset);
bool drawTexture(int index, GLdouble wx, GLdouble wy);
private:
void draw(CPaint2DTexture* tmp, int wx, int wy);
private:
int m_nums;
bool m_occlusion;
QOpenGLVertexArrayObject VAO;
QOpenGLBuffer VBO;
QOpenGLShaderProgram shaderProgram;
CPaint2DTexture** textures;
};
CPaint2DTexture::CPaint2DTexture(){
texture = NULL;
img = NULL;
width = 0;
height = 0;
}
CPaint2DTexture::~CPaint2DTexture() {
if (texture != NULL) {
if(texture->isCreated())
texture->destroy();
delete texture;
texture = NULL;
}
if (img != NULL) {
delete img;
img = NULL;
}
}
void CPaint2DTexture::createImage(QString& name, QFont &font, QColor& color) {
QFontMetrics fontMetrics(font);
QRect bounding= fontMetrics.boundingRect(name); //获取到字符串的外包矩形
if (img != NULL) {
delete img;
img = NULL;
}
img = new QImage(bounding.size(),QImage::Format_ARGB32);
img->fill(Qt::transparent); //使用透明色填充Image
QPainter painter;
painter.begin(img);
painter.setRenderHints(QPainter::TextAntialiasing|QPainter::Antialiasing); //开启抗锯齿
painter.setFont(font);
painter.setPen(color);
painter.drawText(QRect(0,0,img->width(),img->height()),Qt::AlignCenter,name);
painter.end();
if (texture == NULL) {
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
}
width = img->width();
height = img->height();
}
void CPaint2DTexture::createImage(QString& filePath) {
img = new QImage(filePath);
if (texture == NULL) {
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
}
width = img->width();
height = img->height();
}
void CPaint2DTexture::resizeSize(int w, int h) {
if (texture == NULL || img == NULL) {
return;
}
int tw = img->width();
int th = img->height();
float ws = float(w) / float(tw);
float hs = float(h) / float(th);
if (ws > hs) {
width = int(tw * hs);
height = int(th * hs);
}
else{
width = int(tw * ws);
height = int(th * ws);
}
}
void CPaint2DTexture::createTexture() {
if (texture == NULL) {
return;
}
if (texture->isCreated()) {
return;
}
texture->create();
texture->setData(img->mirrored());
}
//
CPaint2DTextTool::CPaint2DTextTool(int n, bool occlusion){
m_nums = n;
textures = new CPaint2DTexture*[m_nums];
for(int i = 0; i < m_nums; i++) {
textures[i] = NULL;
}
m_occlusion = occlusion;
}
void CPaint2DTextTool::initShaderProgram()
{
VAO.create();
QOpenGLVertexArrayObject::Binder bind(&VAO);
VBO.create();
VAO.bind();
VBO.bind();
static float vertex[]={
//顶点 //纹理坐标
-1.0f,-1.0f, 0.0f,0.0f, //左下
-1.0f, 1.0f, 0.0f,1.0f, //左上
1.0f,-1.0f, 1.0f,0.0f, //右下
1.0f, 1.0f, 1.0f,1.0f //右上
};
VBO.allocate(vertex,sizeof (vertex));
if (m_occlusion) {
//vec4(pos,1,1) 控制 第二个值的范围来控制深度: 0 -1 0表示最前面; 1表示最小面
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,"#version 450 core\n"
"attribute vec2 pos;\n" //传入顶点位置
"attribute vec2 texCoord;\n" //颜色
"varying vec2 outCoord;\n" //传递给片段着色器
"void main(void) {\n"
" gl_Position = vec4(pos,1,1);\n" //输出的位置
" outCoord = texCoord;\n"
"}\n");
}
else {
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,"#version 450 core\n"
"attribute vec2 pos;\n"
"attribute vec2 texCoord;\n"
"varying vec2 outCoord;\n"
"void main(void) {\n"
" gl_Position = vec4(pos,0,1);\n"
" outCoord = texCoord;\n"
"}\n");
}
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform sampler2D texture;\n"
"varying vec2 outCoord;\n"
"void main(void) {\n"
" gl_FragColor = texture2D(texture, outCoord);\n" //取颜色
"}\n");
shaderProgram.link();
shaderProgram.enableAttributeArray(0);
shaderProgram.enableAttributeArray(1);
shaderProgram.setAttributeBuffer(0,GL_FLOAT,0, 2,4*sizeof (float));
shaderProgram.setAttributeBuffer(1,GL_FLOAT,2*sizeof (float),2,4*sizeof (float));
VAO.release();
VBO.release();
}
CPaint2DTexture* CPaint2DTextTool::setText(int index, QString& name, QFont &font, QColor& color) {
if (name == "" || index >= m_nums) {
return NULL;
}
if (textures[index] == NULL) {
textures[index] = new CPaint2DTexture();
}
textures[index]->createImage(name, font, color);
return textures[index];
}
CPaint2DTexture* CPaint2DTextTool::setText(int index, QString& filePath){
if (filePath == "" || index >= m_nums) {
return NULL;
}
QFile file(filePath);
if(file.exists()) {
if (textures[index] == NULL) {
textures[index] = new CPaint2DTexture();
}
textures[index]->createImage(filePath);
}
return textures[index];
}
void CPaint2DTextTool::resizeSize(int index, int w, int h){
if (index >= m_nums) {
return;
}
if (textures[index] == NULL){
return;
}
textures[index]->resizeSize(w, h);
}
void CPaint2DTextTool::createTextures() {
for(int i = 0; i < m_nums; i++) {
if (textures[i] != NULL) {
textures[i]->createTexture();
}
}
}
CPaint2DTexture* CPaint2DTextTool::getTexture(int index){
if (index >= m_nums) {
return NULL;
}
return textures[index];
}
void CPaint2DTextTool::getSize(int index, GLdouble* weight, GLdouble* height){
if (index >= m_nums) {
return;
}
CPaint2DTexture* tmp = textures[index];
if (tmp == NULL){
return;
}
*weight = tmp->width;
*height = tmp->height;
}
bool CPaint2DTextTool::drawTexture(int index, GLdouble wx, GLdouble wy,
GLdouble xOffset, GLdouble yOffset){
if (index >= m_nums) {
return false;
}
CPaint2DTexture* tmp = textures[index];
if (tmp == NULL){
return false;
}
wx = wx - tmp->width * 0.5 + xOffset;
wy = wy - tmp->height * 0.5 + yOffset;
draw(tmp, int(wx), int(wy));
return true;
}
bool CPaint2DTextTool::drawTexture(int index, GLdouble wx, GLdouble wy){
if (index >= m_nums) {
return false;
}
CPaint2DTexture* tmp = textures[index];
if (tmp == NULL){
return false;
}
draw(tmp, int(wx), int(wy));
return true;
}
CPaint2DTextTool::~CPaint2DTextTool() {
for(int i = 0; i < m_nums; i++) {
if (textures[i] != NULL)
{
delete textures[i];
textures[i] = NULL;
}
}
delete textures;
}
void CPaint2DTextTool::draw(CPaint2DTexture* tmp, int wx, int wy){
glViewport(wx, wy, tmp->width, tmp->height);
QOpenGLVertexArrayObject::Binder bind(&VAO);
shaderProgram.bind();
tmp->texture->bind(0);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
shaderProgram.release();
}
工程代码:opengl: QT 和 opengl