【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()函數在后端緩沖區進行圖形渲染,然后交換前端和后端緩沖區,讓后端緩沖區圖形顯示到界面上。
最后看看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, // v10.5f, -0.5f, 0.5f, // v20.5f, 0.5f, 0.5f, // v30.5f, -0.5f, -0.5f, // v40.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 face5, 6, 7, 4, 5, 7, // back face0, 1, 7, 0, 6, 7, // left face2, 3, 4, 3, 4, 5, // right face0, 3, 5, 0, 5, 6, // top face1, 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 绘制彩色立方体的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网上补卡一般多久可以到 在网上补卡需要多
- 下一篇: DelayedOperationPurg