天天看點

easyui 添加下拉框資料_OpenGL + Qt: 1 - 用下拉框選顔色

第0篇:OpenGL + Qt: 0 - 三角形繪制

本篇将在上一篇的基礎上着重介紹兩項内容:通過 Fragment Shader 設定繪制顔色和 Qt 的信号-槽機制。

在前一篇中我們繪制了基本的三角形,但是除了畫一個紅色的三角形,就什麼也沒有了。在這一篇中我将利用 Qt 下拉框和 OpenGL 配合,改變繪制的三角形的顔色。更好的界面互動也是我們選擇 Qt 而不是那些專為 OpenGL 打造的界面庫的重要原因。

主視窗布局

我們需要把繪圖的 PaintingWidget 放到主視窗中,同時還要放入一個下拉框。在 Qt 中,我們可以通過控制視窗的布局方式,由布局方式調整子元件的大小,進而在使用者調整視窗大小的時候也能保持界面的美觀。因為隻有一個選擇框,我們的設計和效果圖如下:

easyui 添加下拉框資料_OpenGL + Qt: 1 - 用下拉框選顔色

為此我們修改主視窗的代碼。原本主視窗是繼承 QMainWindow,為了簡單起見,我們也将它修改成繼承 QWidget,并且聲明子元件下拉框和繪圖的 PaintingWidget 的成員變量,為了進行布局再添加一個 QVBoxLayout,這個對象是垂直布局管理對象,可以把建構垂直地放置在視窗中。

#include 
           

而構造函數的實作也比較簡單,唯一需要注意的是我們需要用 setLayout() 方法設定布局管理器,然後用布局管理器的 addWidget() 方法逐次添加元件。

MainWindow
           

這樣我們就完成了視窗的設計,可以開始實作功能了。

重新設計 PaintingWidget

為了支援通過下拉框修改顔色,我們給 PaintingWidget 設計一個修改顔色的接口:

void 
           

這個接口通過給定顔色的 RGB 值來确定顔色。接下來實作分為兩部分:首先我們需要有一種方式能夠把使用者選中的顔色傳遞給着色器用于渲染;而當顔色需要被修改的時候,需要傳遞新的顔色值,并且進行重繪。

顔色控制

片段着色器(Fragment Shader)負責管理渲染圖像的顔色、紋理等資訊。我們在上一篇中提到過,通過對唯一的輸出值指派,Fragment Shader 可以指定顔色。具體的方法是,我們需要對每個渲染的頂點一個顔色,而 OpenGL 繪制三角形的時候,會取三角形三個頂點顔色的插值,作為三角形中像素的顔色。如果隻想繪制一個純色的三角形,而不是漸變的三角形,隻需要取三個頂點顔色相同即可,這也是我們在前一篇所提到的。

然而 Fragment Shader 并不能直接從宿主程式中得到輸入,它隻接受來自 Vertex Shader 的輸出。是以我們隻能把顔色值傳遞給 Vertex Shader,并由它轉發給 Fragment Shader,進而完成整個渲染過程。

首先我們先修改 Vertex Shader,讓它接受一個新的輸入,位置為1,命名為colVertex。于此同時,我們再聲明一個新的輸出,類型為四維向量,因為在 OpenGL 中,顔色是用 RGBA 四個通道來刻畫的。在代碼中直接将 Alpha 通道的值設定為1.0。

#version 330
           

在這段代碼的第4行,我們把輸出命名為 cols,這樣在 Fragment Shader 裡就可以聲明其為輸入,并直接在主函數中指派給輸出變量。以下是新的 Fragment Shader 的代碼:

#version 330
           

修改完着色器的代碼,我們需要修改調用繪圖函數的代碼,将顔色資料傳遞給着色器。

m_cbo 
           

我們用 glVertexAttribPointer() 函數将這個緩沖區設定為位置1的緩沖區,需要注意的是,我們沒有把緩沖區對象作為參數給這個函數,而是在調用之前,使用 VBO 對象的 bind() 方法,将其設定為目前 OpenGL Context 的預設緩沖區,這樣在調用 release() 方法之前,所有需要用緩沖區的對象都會使用這個頂點緩沖作為預設緩沖區。先綁定再操作也是在 Qt 中操作 OpenGL 對象的一般模式。

至于填充緩沖區的任務,則交給 fillColorBuffer() 方法實作,這樣,在使用者請求修改顔色的時候,也可以通過調用這個方法進行繪制。因為我們目前隻繪制純色的三角形,我們在 PaintingWidget 中用一個三維數組 colorBuffer 儲存顔色值:

GLfloat 
           

在初始化PaintingWidget的時候,把它初始化為紅色:

memset
           

這樣在實作fillColorBuffer()方法的時候,我們先将這個顔色值複制三份,然後用 QOpenGLBuffer 對象的 write() 方法把顔色資料寫入頂點緩沖,進而交給Shader。這個 write() 方法的第一個參數是寫入的起始位置,第二個參數是寫入的資料的數組指針,第三個則是寫入資料的大小。

void 
           

最後,我們需要實作 setColor() 方法,它先修改colorBuffer數組,然後調用fillColorBuffer()方法,代碼如下:

void 
           

這裡我們需要注意兩點:

  1. 如果需要調用 write() 方法,首先需要調用 bind() 方法,才能保證操作成功。
  2. 修改完緩沖區,必須通過調用 update() 方法進行重繪。這個方法是 QOpenGLWidget 提供的,事實上,調用它之後并不會立即進行重繪,而是産生一個要求繪圖的事件,等到這個事件被處理了,才會進行繪制。這允許我們在 paintGL() 中要求重繪,進而産生動畫的效果。

響應下拉框選擇

完成了 PaintingWidget 的修改,我們暴露了一個 setColor() 接口。接下來我們需要在使用者通過下拉框選擇新的顔色時調用 setColor() 方法進行響應。這時候就需要使用 Qt 的信号機制,把下拉框的選擇發生的信号傳遞給處理修改顔色的代碼。

在 Qt 中,QObject 可以觸發不同的信号(signal),而連接配接到這個信号上的“槽函數”(slot)則都會被調用來處理這個信号。在我們這個視窗中,最簡單的辦法是在主視窗中建立一個槽函數,用于處理下拉框的選擇信号 currentIndexChanged(const QString&)。首先在主視窗類的聲明類中添加這樣一個函數:

private 
           

Qt 中的槽必須在聲明的時候添加 slots 作為标記,這是 Qt 對 C++ 文法進行的擴充,最後在編譯階段,Qt 會把這些信号和槽轉換成标準的 C++ 函數進而讓編譯器可以處理。根據 Qt 的信号機制,槽函數的參數表需要和信号一緻,而信号參數表的對應位置會作為資訊傳遞給槽函數。下拉框的 currentIndexChanged 的參數是被選中的下拉框文字,可用的信号都可以在文檔中找到。為了簡單起見,我們直接根據顔色名設定顔色的 RGB 取值。其實作如下:

void 
           

最後在構造函數中,把這個槽和信号連接配接在一起:

connect
           

Qt 的 connect 函數接受四個參數,分别是信号發出對象,信号名,接收對象和槽的名字。信号和槽都需要用對應的宏進行标記。

終于大功告成,而可以編譯執行,通過下拉框修改三角形的顔色了!

easyui 添加下拉框資料_OpenGL + Qt: 1 - 用下拉框選顔色

到本篇為止,代碼可以在以下連結得到:

https://github.com/linmx0130/QGLDemo/tree/ch1