【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);
}
好了大致就这样了~
【Qt for Android】OpenGL ES 绘制彩色立方体的更多相关文章
- Android OpenGL ES(八)绘制点Point ..
上一篇介绍了OpenGL ES能够绘制的几种基本几何图形:点,线,三角形.将分别介绍这几种基本几何图形的例子.为方便起见,暂时在同一平面上绘制这些几何图形,在后面介绍完OpenGL ES的坐标系统和坐 ...
- 使用OpenGL ES绘制3D图形
如果应用定义的顶点不在同一个平面上,并且使用三角形把合适的顶点连接起来,就可以绘制出3D图形了. 使用OpenGL ES绘制3D图形的方法与绘制2D图形的步骤大致相同,只是绘制3D图形需要定义更多的 ...
- 【AR实验室】OpenGL ES绘制相机(OpenGL ES 1.0版本)
0x00 - 前言 之前做一些移动端的AR应用以及目前看到的一些AR应用,基本上都是这样一个套路:手机背景显示现实场景,然后在该背景上进行图形学绘制.至于图形学绘制时,相机外参的解算使用的是V-SLA ...
- Android OpenGL ES 开发教程 从入门到精通
感谢,摘自:http://blog.csdn.net/mapdigit/article/details/7526556 Android OpenGL ES 简明开发教程 Android OpenGL ...
- Android OpenGL ES(十二):三维坐标系及坐标变换初步 .
OpenGL ES图形库最终的结果是在二维平面上显示3D物体(常称作模型Model)这是因为目前的打部分显示器还只能显示二维图形.但我们在构造3D模型时必须要有空间现象能力,所有对模型的描述还是使用三 ...
- Android OpenGL ES(七)基本几何图形定义 .
在前面Android OpenGL ES(六):创建实例应用OpenGLDemos程序框架 我们创建了示例程序的基本框架,并提供了一个“Hello World”示例,将屏幕显示为红色. 本例介绍Ope ...
- Android OpenGL ES(五)GLSurfaceView .
Android OpenGL ES 相关的包主要定义在 javax.microedition.khronos.opengles GL 绘图指令 javax.microedition.khrono ...
- Android OpenGL ES(四)关于EGL .
OpenGL ES的javax.microedition.khronos.opengles 包定义了平台无关的GL绘图指令,EGL(javax.microedition.khronos.egl ) 则 ...
- Android OpenGL ES 开发(二): OpenGL ES 环境搭建
零:环境搭建目的 为了在Android应用程序中使用OpenGL ES绘制图形,必须要为他们创建一个视图容器.其中最直接或者最常用的方式就是实现一个GLSurfaceView和一个GLSurfaceV ...
随机推荐
- SqlServer和Oracle中一些常用的sql语句7 游标
declare db_cursor4 scroll cursor for select * from 供应商 --声明游标 open db_cursor4 --打开游标 fetch first fro ...
- Android 带你从源码的角度解析Scroller的滚动实现原理
转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273),请尊重他人的辛勤劳动成果,谢谢! 今天给大 ...
- python httpConnection详解
模块urllib,urllib2,httplib的区别 httplib实现了http和https的客户端协议,但是在python中,模块urllib和urllib2对httplib进行了更上层的封装. ...
- uva 10129
主要是求能否形成联通的欧拉回路 并查集+ 欧拉回路判断 一开始用dfs判断联通,死活A不出来,Wa了好多次………哭…… 并查集一次就AC了 感觉还是并查集代码好写一点, 因为dfs还要判断入口在哪里… ...
- 免费APP在线測试工具以及其用法
免费APP漏洞安全检測工具:http://safe.ijiami.cn/ 漏洞分析是爱加密推出免费 APP 漏洞分析平台,服务包含一键对APK 进行签名数据信息採集.内部配置信息採集.市场渠道相关信息 ...
- 用VC制作应用程序启动画面
摘 要:本文提供了四种启动画面制作方法. 使用启动画面一是可以减少等待程序加载过程中的枯燥感(尤其是一些大型程序):二是 可以用来显示软件名称和版权等提示信息.怎样使用VC++制作应用程序的启动画面呢 ...
- RobotFrameWork(十一)AutoItLibrary测试库在win7(64bit)下安装及简单使用
最近安装AutoItLibrary,发现在win7 x64下无法安装成功,后来经过定位,发现是3rdPartyTools\AutoIt目录下面AutoItX3.dll的问题.因为AutoItX3.dl ...
- Android读取网络图片
本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 在android4.0之后,已不同意在主线程中进行网络请求操作了, 否则会出现N ...
- 带有机器人框架的.NET自己主动化測试
Clayton Neal在软件測试和质量保证方面有超过13年的经验,当中有八年的Windows, web,和移动应用程序的測试自己主动化经验.他在測试领域的全部等级都工作过.近期他在Bloomberg ...
- rsyslog+LogAnalyzer 日志收集
Linux 之rsyslog+LogAnalyzer 日志收集系统 一.LogAnalyzer介绍 LogAnalyzer工具提供了一个易于使用,功能强大的前端,用于搜索,查看和分析网络活动数据,包括 ...