【Qt for Android】OpenGL ES 绘制彩色立方体

简介:

Qt 内置对OpenGL ES的支持。选用Qt进行OpenGL ES的开发是很方便的,很多辅助类都已经具备。从Qt 5.0開始添加了一个QWindow类,该类既能够使用OpenGL绘制3D图形,也能够使用QPainter绘制2D传统的GDI+图形。5.0曾经的QGLWidget不推荐再使用。在即将到来(官方时间是今年秋天)Qt 5.4会全然废弃QGLWidget。作为替代将会新增QOpenGLWidget和QOpenGLWindow类来方便OpenGL的编程。

好了废话不多说了。今天我会使用OpenGL ES绘制一个彩色立方体,先在桌面平台编译执行成功后,再针对Android平台编译一次就可以。以下是在Android上的执行效果。基于同一个着色器绘制了四个同样的彩色立方体。

为了使用OpenGL,我从QWindow类和QOpenGLFunctions类派生出了一个OpenGLWindow类。该类声明例如以下:

class OpenGLWindow : public QWindow, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit OpenGLWindow(QWindow *parent = 0);
    ~OpenGLWindow();

    virtual void initialize();
    virtual void render(QPainter *painter);
    virtual void render(double currentTime = 0.0);      // elapsed seconds from program started.

protected:
    void exposeEvent(QExposeEvent *event);

private:
    QOpenGLPaintDevice *m_device;
    QOpenGLContext *m_context;
    QTime   startTime;
    GLuint m_program;
//    QOpenGLShaderProgram *m_shaderProgram;
 };

因为继承自QWindow因此能够使用QWindow提供的OpenGL环境,不须要EGL来控制本地窗体显示图形。同一时候因为继承自QOpenGLFunctions,所以在OpenGLWindow类的成员函数中能够直接使用 gl* 风格的原生的OpenGL API。

在Qt中提供了非常多封装好的OpenGL便捷类,如QOpenGLShaderProgram能够非常方便的对着色器程序进行操作,但这样做可能对不熟悉Qt的人不友好,所以这里我不用Qt提供的便捷类,而直接使用原生的C风格的 OpenGL API进行操作。这全然是能够的(这也是我喜欢Qt的原因之中的一个:提供自身类库的同一时候,同意你使用非Qt的类,并提供二者之间的转换。如Qt中的容器类QVector、QMap、QString能够和C++标准库中的对应容器相互转换)。

甚至你能够混合使用Qt的OpenGL类和原生的OpenGL API。

以下看看几个关键的函数。

首先是initialize()负责着色器的创建、编译、链接等操作。并设置背景色。

代码例如以下。当中被凝视的部分是使用Qt自带类库实现同样的功能。

void OpenGLWindow::initialize()
{
    const char *vertexShaderSrc =
            "attribute vec4 a_position; \n"
            "uniform mat4 u_mvp; \n"
            "varying vec4 v_color; \n"
            "void main()               \n"
            "{                         \n"
            "      v_color = a_position*0.7 + 0.5;  \n"
            "      gl_Position = u_mvp * a_position; \n"
            "}                              \n";
    const char *fragmentShaderSrc =
            "varying vec4 v_color; \n"
            "void main()                    \n"
            "{                              \n"
            "   gl_FragColor = v_color; \n"
            "}                              \n";

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSrc, NULL);
    glCompileShader(vertexShader);

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSrc, NULL);
    glCompileShader(fragmentShader);

    m_program = glCreateProgram();
    glAttachShader(m_program, vertexShader);
    glAttachShader(m_program, fragmentShader);
    glLinkProgram(m_program);

//    // add vertex shader(compiled internal)
//    m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSrc);
//    // add fragment shader(compiled internal)
//    m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSrc);
//    // link shaders to program
//    m_shaderProgram->link();


    // set the background clear color.
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}

再来看看QWindow的QExposeEvent事件,当QWindow须要重绘时会调用该事件的处理函数exposeEvent()。这里我对该事件的处理函数进行了重写。isExposed()用来推断当前窗体是否显示在屏幕上(onScreen or offScreen)。仅仅有显示在屏幕上时才重绘(尽管窗体须要重绘,可是因为没有显示在屏幕上。重绘了也没人看得见,所以加这个推断能够降低不必要的画图操作)。

首先创建OpenGL上下文,然后进行调用对应的初始化函数,这两步仅仅在第一次被运行,以后不会再运行。接下来是该函数的核心部分,调用render()函数在后端缓冲区进行图形渲染,然后交换前端和后端缓冲区,让后端缓冲区图形显示到界面上。

void OpenGLWindow::exposeEvent(QExposeEvent *event)
{
    Q_UNUSED(event)
    static bool needInit = true;

    // Returns true if this window is exposed in the windowing system.
    if (isExposed())
    {
        if (m_context == nullptr)
        {
            m_context = new QOpenGLContext(this);
            m_context->setFormat(requestedFormat());
            m_context->create();
        }
        m_context->makeCurrent(this);

        if (needInit)
        {
            initializeOpenGLFunctions();
            this->initialize();
            needInit = false;
        }

        // calculate elapsed seconds from program started.
        double duration = startTime.msecsTo(QTime::currentTime()) / 1000.0;
        render(duration);

        m_context->swapBuffers(this);
    }
}

最后看看render()渲染函数,这个也是学习OpenGL的主要部分。

在render()函数的開始部分创建了一个QOpenGLPaintDevice实例,该演示样例用于绘制QPainter的画图操作。这里能够忽略,删掉也能够。

接下来就是定义立方体的顶点位置,以及顶点索引。创建2个顶点缓冲区对象(vertex buffer object),通过glBufferData()函数将立方体的顶点位置和顶点所以放到顶点缓冲区对象中,将顶点位置通过glVertexAttribPointer()传递给顶点着色器。计算model/view/projection。然后将结果通过glUniformMatrix4fv()传递给顶点着色器中的mvp。最后使用glDrawElement()绘制立方体。

立方体每一个点的颜色由其所在的位置决定,所以不同位置的顶点具有不同颜色。

void OpenGLWindow::render(double currentTime)
{
    if (m_device == nullptr)
        m_device = new QOpenGLPaintDevice;
    m_device->setSize(this->size());

    static GLfloat vCubeVertices[] = {
        -0.5f,  0.5f,  0.5f,  // v0
        -0.5f, -0.5f,  0.5f,  // v1
         0.5f, -0.5f,  0.5f,  // v2
         0.5f,  0.5f,  0.5f,  // v3
         0.5f, -0.5f, -0.5f,  // v4
         0.5f,  0.5f, -0.5f,  // v5
        -0.5f,  0.5f, -0.5f,  // v6
        -0.5f, -0.5f, -0.5f, // v7
    };
    static GLushort vCubeIndices[] = {
        0, 1, 2, 0, 2, 3,   // front face
        5, 6, 7, 4, 5, 7,   // back face
        0, 1, 7, 0, 6, 7,   // left face
        2, 3, 4, 3, 4, 5,   // right face
        0, 3, 5, 0, 5, 6,   // top face
        1, 2, 4, 1, 4, 7    // bottom face
    };

    glViewport(0, 0, width(), height());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(m_program);

    GLuint vbos[2];
    glGenBuffers(2, vbos);

    glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vCubeVertices), vCubeVertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vCubeIndices), vCubeIndices, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindAttribLocation(m_program, 0, "a_position");

    static GLfloat angle = 0.0;
    GLuint mvpLoc = glGetUniformLocation(m_program, "u_mvp");
    QMatrix4x4 model, view, projection, mvp;
    model.rotate(angle + 5, QVector3D(1,0,0));
    model.rotate(angle - 5, QVector3D(0,1,0));
    model.scale(0.5, 0.5, 0.5);
    view.translate(0.5, 0.5, 0);
    angle += 10;
    mvp = projection * view * model;
    glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvp.constData());

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
    glDrawElements(GL_TRIANGLES, sizeof(vCubeIndices)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

    /* draw another cube in different place with the same shader */
    view.translate(-1.0, 0, 0);
    mvp = projection * view * model;
    glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvp.constData());
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
    glDrawElements(GL_TRIANGLES, sizeof(vCubeIndices)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

    view.translate(0.0, -1.0, 0);
    mvp = projection * view * model;
    glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvp.constData());
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
    glDrawElements(GL_TRIANGLES, sizeof(vCubeIndices)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

    view.translate(1.0, 0, 0);
    mvp = projection * view * model;
    glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvp.constData());
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
    glDrawElements(GL_TRIANGLES, sizeof(vCubeIndices)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

    QPainter painter(m_device);
    render(&painter);

    glDeleteBuffers(2, vbos);
}

好了大致就这样了~









本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5093679.html,如需转载请自行联系原作者

相关文章
|
2月前
QT4.7版本的OPENGL的3D旋转模型例子
QT4.7版本的OPENGL的3D旋转模型例子
|
2月前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
58 1
Android开发之使用OpenGL实现翻书动画
|
2月前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
33 1
Android开发之OpenGL的画笔工具GL10
|
2月前
|
前端开发 API vr&ar
Android开发之OpenGL绘制三维图形的流程
即将连载的系列文章将探索Android上的OpenGL开发,这是一种用于创建3D图形和动画的技术。OpenGL是跨平台的图形库,Android已集成其API。文章以2D绘图为例,解释了OpenGL的3个核心元素:GLSurfaceView(对应View)、GLSurfaceView.Renderer(类似Canvas)和GL10(类似Paint)。通过将这些结合,Android能实现3D图形渲染。文章介绍了Renderer接口的三个方法,分别对应2D绘图的构造、测量布局和绘制过程。示例代码展示了如何在布局中添加GLSurfaceView并注册渲染器。
120 1
Android开发之OpenGL绘制三维图形的流程
|
2月前
|
机器学习/深度学习 API vr&ar
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
268 4
|
2月前
|
Linux API iOS开发
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
79 0
|
2月前
Qt+OpenGL 打砖块游戏
Qt+OpenGL 打砖块游戏
34 0
|
2月前
|
数据可视化 API vr&ar
qt“五彩斑斓“ opengl
qt“五彩斑斓“ opengl
|
2月前
|
XML 前端开发 Java
【Android App】三维处理中三维投影OpenGL功能的讲解及实战(附源码和演示 超详细必看)
【Android App】三维处理中三维投影OpenGL功能的讲解及实战(附源码和演示 超详细必看)
44 1
|
2月前
|
XML Java Android开发
Android App开发中OpenGL三维投影的讲解及实现(附源码和演示 简单易懂)
Android App开发中OpenGL三维投影的讲解及实现(附源码和演示 简单易懂)
49 1