时月oe 2023-04-14 14:01 采纳率: 44.4%
浏览 16

Qt Opengl移植安卓出现的问题

最近在学习opengl,目的是实现三维物体的绘制,使用的是,在PC端可以正常执行

Qt的安卓已经配置好了,但是在移植到安卓执行的时候突然编译出错

img


我使用的是QOpenGLFunctions_3_3_Core头文件,但是在编译的时候提示没有QOpenGLFunctions_3_3_Core这个类,导致项目一直编译不通过,请问各位这是什么原因呢?十分感谢

这是部分代码的截图

img


可以看到如果使用安卓Kits进行编译的话,QOpenGLFunctions_3_3_Core这个类是识别不出来的

  • 写回答

1条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2023-04-14 16:11
    关注
    • 这篇博客: QT中使用自带的OpenGL模块开发中的 2、使用QOpenGLFunctions 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
    • QOpenGLFunctions类提供跨平台访问的OpenGL ES 2.0 API,QOpenGLFunctions提供了一个在所有OpenGL系统上都可用的保证API, 并在需要它的系统上负责功能解析。使用QOpenGLFunctions的推荐方法是直接继承,同时在初始化函数中void initializeGL() 调用此接口initializeOpenGLFunctions() 进行初始化。如下:

      OpenGL ES相对OpenGL删减了一切低效能的操作方式,有高性能的决不留低效能的,即只求效能不求兼容性(和苹果的作风类似)。也就是说很多opengl函数无法使用,如glDrawPixels等。

      典型:
      1.没有double型数据类型,但加入了高性能的定点小数数据类型。
      2.没有glBegin/glEnd/glVertex,只能用glDrawArrays/glDraw…
      3.没有实时将非压缩图片数据转成压缩贴图的功能,程序必须直接提供压缩好的贴图

      数据类型:
      1: i GLint 整数型
      2: f GLfixed 定点小数
      3: x GLclampx 限定型定点小数

      删除的功能:
      1.glBegin/glEnd
      2.glArrayElement
      3.显示列表
      4.求值器
      5.索引色模式
      6.自定义裁剪平面
      7.glRect
      8.图像处理(这个一般显卡也没有,FireGL/Quadro显卡有)
      9.反馈缓冲
      10.选择缓冲
      11.累积缓冲
      12.边界标志
      13.glPolygonMode
      14.GL_QUADS,GL_QUAD_STRIP,GL_POLYGON
      15.glPushAttrib,glPopAttrib,glPushClientAttrib,glPopClientAttrib
      15.TEXTURE_1D、TEXTURE_3D、TEXTURE_RECT、TEXTURE_CUBE_MAP
      16.GL_COMBINE
      17.自动纹理坐标生成
      18.纹理边界
      19.GL_CLAMP、GL_CLAMP_TO_BORDER
      20.消失纹理代表
      21.纹理LOD限定
      22.纹理偏好限定
      23.纹理自动压缩、解压缩
      24.glDrawPixels,glPixelTransfer,glPixelZoom
      25.glReadBuffer,glDrawBuffer,glCopyPixels

      其它注意事项:
      1.glDrawArrays等函数中数据必须紧密排列,即间隔为0
      2.各种数据的堆栈深度较低

      参考代码:

      #ifndef WIDGET_H
      #define WIDGET_H
      
      #include <QOpenGLWidget>
      #include <QOpenGLFunctions>
      #include <QOpenGLShaderProgram>
      
      //QOpenGLFunctions类提供了跨平台访问的OpenGL ES 2.0 API,QOpenGLFunctions提供了一个在所有OpenGL系统上都可用的保证API,并在需要它的系统上负责功能解析。使用QOpenGLFunctions的推荐方法是直接继承:
      class Widget : public QOpenGLWidget, protected QOpenGLFunctions
      {
      public:
          Widget(QWidget *parent = 0);
          ~Widget();
          void initializeGL();                ///< 初始化
          void resizeGL(int w, int h);        ///< 当窗口发生变化时重新初始化
          void paintGL();                     ///< 绘制
      
          void initVbo();                     ///< 初始化Vbo
          void loadTextures();                ///< 加载纹理
          void keyPressEvent(QKeyEvent * e);  ///< 键盘事件
      private:
          /* [1] 需要定点着色器和片段着色器,不然做不了任何渲染 */
          /*   这里定义了一个着色器[顶点着色器、片段着色器]编译对象 */
          QOpenGLShaderProgram * program;
          ///< 可以根据此id,利用glGetUniformLocation等方法获取shader里面的属性
          GLuint programid;
      
          ///< 其实还有视图矩阵、投影矩阵、MVP矩阵,这里简单的用下,不区分特别细!
          ///< 分三个这样的矩阵,分别是模型矩阵、视图矩阵、透视矩阵,这样以后灵活性更强:
          ///  1. 比如单独控制模型灯光跟随,shader可能需要传入除了mvp矩阵外的模型矩阵*视图矩阵这样一个矩阵
          QMatrix4x4 m_projection;
      
          ///< 矩阵、顶点、颜色在着色器里面的位置
          GLuint matrixLocation, vertexLocation, textureLocation,
                 samplerLocation;
      
          ///< 顶点、索引、颜色->buffer的标识
          GLuint verVbo, v_indexVbo, textureVbo;
          GLuint texture;
      
          int vVerticesLen;   ///< 顶点数组长度
          int tri_indexLen;   ///< 索引数组长度
          int textureCoordLen;///< 纹理坐标数组长度
      };
      
      #endif // WIDGET_H
      
      
      #include "widget.h"
      #include<QKeyEvent>
      #include<QGLWidget>
      #include<QOpenGLFunctions_3_2_Core>
      
      Widget::Widget(QWidget *parent) : QOpenGLWidget(parent)
      {
          ///< 官方文档有这样设置,具体还没细细看,但是意思吧,就是告诉渲染属性,使用版本等;有空可以深入研究,光看效果不一定能看出什么区别!
          ///< 这个有些是放到main.cpp中去了,通过new widget.setFormat(format)那样去设置,不清楚...我觉得本质是一样,都是给该widget设置属性。
      //    QSurfaceFormat format;
      //    format.setDepthBufferSize(24);
      //    format.setStencilBufferSize(8);
      //    format.setVersion(3, 2);
      //    format.setProfile(QSurfaceFormat::CoreProfile);
      //    setFormat(format);
      }
      
      Widget::~Widget()
      {
          glDeleteBuffers(1, &verVbo);
          glDeleteBuffers(1, &v_indexVbo);
          glDeleteProgram(programid);
          glDeleteTextures(1, &texture);
      }
      
      /* 1.1 着色器代码 */
      /* *********************************************
       *   顶点着色器定义一个输入,它是 4 个成员的矢量 vPosition。
       *   主函数声明着色器宣布着色器开始执行。着色器主体非常简单,
       *   它复制输入 vPosition 属性到 gl_Position 输出变量中。
       *   每个顶点着色器必须输出位置值到 gl_Position 变量中,
       *   这个变量传入到管线的下一个阶段中。
       *   matrix主要是模型视图矩阵,控制位置和旋转等
       * ******************************************** */
      /* 顶点着色器 */
      static const char *vertexShaderSourceCore =
          "attribute vec4 vPosition;\n"
          "uniform highp mat4 matrix;\n"
          "attribute vec2 TexCoord;\n"
          "varying vec2 TexCoord0;\n"
          "void main() {\n"
          "   TexCoord0 = TexCoord;\n"
          "   gl_Position = matrix * vPosition;\n"
          "}\n";
      
      /* *********************************************
       *   gl_FragColor,gl_FragColor是片段着色器最终的输出值,
       *   本例中输出值来自外部传入的颜色数组。
       * ******************************************** */
      
      /* 片段着色器 */
      static const char *fragmentShaderSourceCore =
          "varying vec2 TexCoord0;\n"
          "uniform sampler2D gSampler;\n"
          "void main() {\n"
          "   gl_FragColor = texture2D(gSampler, TexCoord0.st);\n"
          "}\n";
      
      
      ///* 2.1 三角形顶点的坐标 */
      //GLfloat vVertices[] = {0.0f, 0.5f, 0.0f,
      //                       -0.5f, -0.5f, 0.0f,
      //                       0.5f, -0.5f, 0.0f};
      ///* 2.2 三角形顶点的索引 */
      //GLuint tri_index[] = {0, 1, 2};
      ///* 2.3 顶点颜色数组 */
      //GLfloat colors[] = {1.0f, 0.0f, 0.0f,0.5f,
      //                    0.0f, 1.0f, 0.0f,0.5f,
      //                    0.0f, 0.0f, 1.0f,0.5f};
      
      /* 2.1 正方体顶点的坐标 */
      GLfloat vVertices[] = {-0.5f, -0.5f, 0.5f,
                             0.5f, -0.5f, 0.5f,
                             -0.5f,  0.5f, 0.5f,
                             0.5f,  0.5f, 0.5f,
      
                             -0.5f, -0.5f,  -0.5f,
                             0.5f, -0.5f,  -0.5f,
                             -0.5f,  0.5f,  -0.5f,
                             0.5f,  0.5f,  -0.5f,
      
                             -0.5f, -0.5f,  -0.5f,
                             -0.5f, -0.5f,  0.5f,
                             -0.5f,  0.5f,  -0.5f,
                             -0.5f,  0.5f,  0.5f,
      
                             0.5f, -0.5f,  -0.5f,
                             0.5f, -0.5f,  0.5f,
                             0.5f,  0.5f,  -0.5f,
                             0.5f,  0.5f,  0.5f,
      
                             -0.5f, 0.5f,  -0.5f,
                             -0.5f, 0.5f,  0.5f,
                             0.5f,  0.5f,  -0.5f,
                             0.5f,  0.5f,  0.5f,
      
                             -0.5f, -0.5f,  -0.5f,
                             -0.5f, -0.5f,  0.5f,
                             0.5f,  -0.5f,  -0.5f,
                             0.5f,  -0.5f,  0.5f};
      /* 2.2 正方体顶点的索引 */
      GLuint tri_index[] = {0, 3, 2,
                            0, 1, 3,
                            4, 7, 6,
                            4, 5, 7,
                            8, 11, 10,
                            8, 9, 11,
                            12, 15, 14,
                            12, 13, 15,
                            16, 19, 18,
                            16, 17, 19,
                            20, 23, 22,
                            20, 21, 23};
      
      ///< 纹理点 6个面 每个面四个纹理坐标映射???好像不对呀,绘制出来花花的...
      ///< 还得好好思考下纹理坐标和现在的顶点索引坐标如何对应!!!
      /// 下面这个先注释掉,上面的说法:六个面,每个面四个纹理坐标映射好像不对....
      //float texCoords[] =
      //{
      //    0.0f, 0.0f,
      //    1.0f, 0.0f,
      //    0.0f, 1.0f,
      //    1.0f, 1.0f,
      
      //    0.0f, 0.0f,
      //    1.0f, 0.0f,
      //    0.0f, 1.0f,
      //    1.0f, 1.0f,
      
      //    0.0f, 0.0f,
      //    1.0f, 0.0f,
      //    0.0f, 1.0f,
      //    1.0f, 1.0f,
      //    0.0f, 0.0f,
      //    1.0f, 0.0f,
      //    0.0f, 1.0f,
      //    1.0f, 1.0f,
      //    0.0f, 0.0f,
      //    1.0f, 0.0f,
      //    0.0f, 1.0f,
      //    1.0f, 1.0f,
      //    0.0f, 0.0f,
      //    1.0f, 0.0f,
      //    0.0f, 1.0f,
      //    1.0f, 1.0f
      //};
      ///< 我们就来手动修改下,直到不花为止,然后回过头去细细分析下,具体原因?
      ///  这样学习起来更快,毕竟现有感觉,带着感觉去更加有激情和感悟...
      ///  不过按照自己认为的坐标去对应,始终不对(上下两面还是花)...哎,缓一缓,先仔细研究下再回过头来修改...
      float texCoords[] =
      {
          0.0f, 0.0f,
          1.0f, 0.0f,
          0.0f, 1.0f,
          1.0f, 1.0f,
      
          0.0f, 0.0f,
          1.0f, 0.0f,
          0.0f, 1.0f,
          1.0f, 1.0f,
      
          0.0f, 0.0f,
          1.0f, 0.0f,
          0.0f, 1.0f,
          1.0f, 1.0f,
      
          0.0f, 0.0f,
          1.0f, 0.0f,
          0.0f, 1.0f,
          1.0f, 1.0f,
      
          0.0f, 0.0f,
          1.0f, 0.0f,
          0.0f, 1.0f,
          1.0f, 1.0f,
      
          0.0f, 0.0f,
          1.0f, 0.0f,
          0.0f, 1.0f,
          1.0f, 1.0f
      };
      
      /**
       * @brief 初始化模型信息vbo【显存】
       */
      void Widget::initVbo()
      {
          ///< 计算获得数组长度,之后会用到该变量,这样只需要改动这里即可!如果用链表,直接.size()即可求出!
          vVerticesLen = sizeof(vVertices)/sizeof(GLfloat);
          tri_indexLen = sizeof(tri_index)/sizeof(GLuint);
          textureCoordLen = sizeof(texCoords)/sizeof(GLfloat);
      
          qDebug() << vVerticesLen;
          qDebug() << tri_indexLen;
      
          ///< 初始化顶点buffer并装载数据到显存
          glGenBuffers(1, &verVbo);
          glBindBuffer(GL_ARRAY_BUFFER, verVbo);
          glBufferData(GL_ARRAY_BUFFER, vVerticesLen * sizeof(GLfloat), vVertices, GL_STATIC_DRAW);
      
          ///< 初始化索引buffer并装载数据到显存
          glGenBuffers(1, &v_indexVbo);
          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, v_indexVbo);
          glBufferData(GL_ELEMENT_ARRAY_BUFFER, tri_indexLen * sizeof(GLuint), tri_index, GL_STATIC_DRAW);
      
          ///< 初始化纹理坐标buffer并装载到显存
          glGenBuffers(1, &textureVbo);
          glBindBuffer(GL_ARRAY_BUFFER, textureVbo);
          glBufferData(GL_ARRAY_BUFFER, textureCoordLen * sizeof(GLfloat), texCoords, GL_STATIC_DRAW);
      }
      
      /**
       * @brief 装载纹理,具体纹理知识可以参考网友资料:
       * http://www.cnblogs.com/tornadomeet/archive/2012/08/24/2654719.html
       */
      void Widget::loadTextures()
      {
          QImage tex, buf;
          if (!buf.load("../2012082420060914.jpg"))
          {
              qWarning("annot open the image...");
              QImage dummy(128, 128, QImage::Format_RGB32);
              dummy.fill(Qt::green);
              buf = dummy;
          }
      
          ///< 转换为OpenGL支持的格式
          tex = QGLWidget::convertToGLFormat(buf);
      
          ///< 开辟一个纹理内存,内存指向texture
          glGenTextures(1, &texture);
          ///< 将创建的纹理内存指向的内容绑定到纹理对象GL_TEXTURE_2D上,
          ///  经过这句代码后,以后对GL_TEXTURE_2D的操作的任何操作都同时对应与它所绑定的纹理对象
          glBindTexture(GL_TEXTURE_2D, texture);
          ///< 开始真正创建纹理数据
          glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(),
                       0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());
      
          ///< 当所显示的纹理比加载进来的纹理小时,采用GL_LINEAR的方法来处理
          glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
          ///< 当所显示的纹理比加载进来的纹理大时,采用GL_LINEAR的方法来处理
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      }
      
      void Widget::initializeGL()
      {
          qDebug("+++ initializeGL +++");
          /* 0. 初始化函数,使得函数可以使用 */
          initializeOpenGLFunctions();
      
          /* 创建项目对象链接着色器 */
          /* 1. 初始化最大的任务是装载顶点和片段着色器 */
          program = new QOpenGLShaderProgram(this);
          /* 一旦应用程序已经创建了顶点、片段着色器对象,
             * 它需要去创建项目对象,项目是最终的链接对象,
             * 每个着色器在被绘制前都应该联系到项目或者项目对象。
             * ***************************************** */
          /* 1.2 加载 */
          if(!program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSourceCore))
          {
              return;
          }
          if(!program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSourceCore))
          {
              return;
          }
      
          /* 1.3 设置属性位置,将vPosition属性设置为位置0, vertex为位置1
                 这里我就让程序自动分配,当然你也可以手动; 我在后面通过代码获取到了!
          */
          //program->bindAttributeLocation("vertex", 1);
          //program->bindAttributeLocation("vPosition", 0);
          //program->bindAttributeLocation("a_color", 1);
          //program->bindAttributeLocation("matrix", 2);
      
          /* 1.4 链接项目检查错误 */
          if( !program->link() )
          {
              return;
          }
      
          if( !program->bind() ){
              return ;
          }
      
          ///< 获取shaderprogram的id号,然后可以通过id号获取一些属性...
          programid = program->programId();
      
          ///< 从shaderprogram里面获取变量标识,总共用到两种方式,看你喜好!倾向第一种
          matrixLocation = glGetUniformLocation(programid, "matrix");
          vertexLocation = glGetAttribLocation(programid, "vPosition");
          textureLocation = program->attributeLocation("TexCoord");
          samplerLocation = program->uniformLocation("gSampler");
      
          ///< 初始化vbo,对于实时变化的数据,可能需要在paintGL()里面每次调用!
          initVbo();
      
          ///< 装载纹理
          loadTextures();
      
          ///< 允许采用2D纹理技术
          glEnable(GL_TEXTURE_2D);
          ///< 设置背景颜色
          glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
          ///< 开启深度测试,避免颜色相互透过,具体需要自己深入学习的哦!
          glEnable(GL_DEPTH_TEST);
          ///< 设置深度测试类型 - 不设置也会默认
          glDepthFunc(GL_LEQUAL);
      
          ///< 这个地方先于resizeGL运行,所以这里设置无效!我一开始犯了这个错误!!!
          //m_projection.translate(0.0f, 0.0f, -1.0f);
      }
      
      void Widget::resizeGL(int w, int h)
      {
          /* 2.1 viewport 设定窗口的原点 origin (x, y)、宽度和高度 */
          glViewport(0, 0, w, h);
      
          ///< 模型矩阵重置
          m_projection.setToIdentity();
          ///< 透视投影【做了简单容错】
          qreal aspect = qreal(w) / qreal(h ? h : 1);
          m_projection.perspective(60.0f, aspect, 1.0f, 100.0f);
          ///< 增加了模型矩阵,需要做一定偏移量,保证物体刚开始渲染出来时可以被看到!
          m_projection.translate(0.0f, 0.0f, -2.0f);
      }
      
      void Widget::paintGL()
      {
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      
          ///< shader传入模型视图矩阵
          glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, m_projection.data());
      
          ///< shader绑定并启用顶点数组buffer
          glBindBuffer(GL_ARRAY_BUFFER, verVbo);
          glEnableVertexAttribArray(vertexLocation);
          ///< 顶点xyz坐标,所以每三个作为一个顶点值
          glVertexAttribPointer( vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
      
          ///< shader绑定并顶点索引数组buffer - 索引无需启用
          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, v_indexVbo);
      
          ///< 绑定纹理
          glUniform1i(samplerLocation, 0);
          glActiveTexture(GL_TEXTURE_2D);
          glBindTexture(GL_TEXTURE_2D, texture);
          glBindBuffer(GL_ARRAY_BUFFER, textureVbo);
          glEnableVertexAttribArray(textureLocation);
          ///< 2是标识两个float为一个纹理坐标,从varying vec2 TexCoord0也可以看出!
          glVertexAttribPointer(textureLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
      
          glDrawElements(GL_TRIANGLES, tri_indexLen, GL_UNSIGNED_INT, 0);
      
          ///< 解绑buffer、关闭启用顶点、颜色数组、解绑纹理
          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
          glBindBuffer(GL_ARRAY_BUFFER, 0);
          glBindTexture(GL_TEXTURE_2D, 0);
          glBindBuffer(GL_ARRAY_BUFFER, 0);
          glDisableVertexAttribArray(textureLocation);
          glDisableVertexAttribArray(vertexLocation);
      }
      
      /**
       * @brief 写了键盘监听事件,可以控制模型旋转,方便预览
       * @param k
       */
      void Widget::keyPressEvent(QKeyEvent * k)
      {
          qDebug("+++ keyPressEvent +++");
          if(k->key() == Qt::Key_A)
          {
              m_projection.rotate(4, 0, 1, 0);
          }
          else if(k->key() == Qt::Key_D)
          {
              m_projection.rotate(-4, 0, 1, 0);
          }
          else if(k->key() == Qt::Key_W)
          {
              m_projection.rotate(4, 1, 0, 0);
          }
          else if(k->key() == Qt::Key_S)
          {
              m_projection.rotate(-4, 1, 0, 0);
          }
          update();
      }
      
      
      #include "widget.h"
      #include <QApplication>
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
      
          Widget widget(0);
          widget.show();
      
          return a.exec();
      }
      
      

      效果图:
      在这里插入图片描述

    评论

报告相同问题?

问题事件

  • 创建了问题 4月14日

悬赏问题

  • ¥15 c++ gmssl sm2验签demo
  • ¥15 关于模的完全剩余系(关键词-数学方法)
  • ¥15 有没有人懂这个博图程序怎么写,还要跟SFB连接,真的不会,求帮助
  • ¥15 关于移动机器人坐标计算
  • ¥30 模拟电路 logisim
  • ¥15 PVE8.2.7无法成功使用a5000的vGPU,什么原因
  • ¥15 is not in the mmseg::model registry。报错,模型注册表找不到自定义模块。
  • ¥15 安装quartus II18.1时弹出此error,怎么解决?
  • ¥15 keil官网下载psn序列号在哪
  • ¥15 想用adb命令做一个通话软件,播放录音