1. 程式人生 > >Cocos2dx-OpenGL ES 2.0教程:你的第一個三角形(1)

Cocos2dx-OpenGL ES 2.0教程:你的第一個三角形(1)

前言

在本系列教程中,我會以當下最流行的2D引擎Cocos2D-X為基礎,介紹OpenGL ES 2.0的一些基本用法。本系列教程的宗旨是OpenGL掃盲,讓大家在使用Cocos2D-X過程中,知其然,更知其所以然。因為我自己的圖形學水平有限,所以這些教程不會涉及非常底層的數學原理,同時也不會過多地提及OpenGL本身的一些細節知識。但是我會在每篇文章的最後給出一些參考連結,大家可以順藤摸瓜,一舉Get OpenGL這個新技能。

我第一次學習OpenGL是在2008年,但是那時候學得很爛,被各種矩陣變換搞得雲裡霧裡。我於今年年初徹底重新學習OpenGL,目前來講,應該算是入門了,至少矩陣變換是理解了,同時也會自己寫一些簡單的shader,可以進行OpenGL除錯了。但是,我的學習之路才剛剛開始,我希望在我自己學習的過程,把有用的一些知識記錄下來,方便自己和他人查閱。經過這次重新學習,我個人覺得,OpenGL真的沒有那麼難,只要你用心,就一定可以學會。當然,好的學習方法和好的學習資料肯定是會使之事半功倍的,希望接下來我的這些博文能為大家帶來些許幫助。

在第一篇文章正式開始前,我談下我自己的入門心得體會吧,而《如何學習OpenGL》這是個更大的話題,等我OpenGL水平精進之後,我再單獨寫一篇文章來談談我的看法。

目前來說,我的體會是“三要”和“三不要”。

  • 要理解OpenGL渲染管線

  • 要理解OpenGL是個狀態機

  • 要多動手實踐。

當然還有最重要的“三不要”:

  • 不要每天去群裡問怎樣最快能學好OpenGL

  • 不要每天去看各種資料而不動手寫一點程式碼

  • 不要出了問題到處問,嘗試先自己解決,實在解決不了再問

正文

準備工作

首先,是建立一個新的工程(注意我這裡使用的版本是3.7. Update: Tuesday, June 16, 2015)。開啟命令列工具,然後輸入下列命令:

1
cocos new -l cpp

如果對於上述命令不瞭解的使用者,請猛戳這裡.

編譯並執行成功,然後把HelloWorldScene.cpp裡面的init函式修改成下面的樣子:

1
2
3
4
5
6
7
8
9
10
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    return true;
    }

此時,再編譯執行之。你將會得到以下介面。

firstfirst

傳送CustomCommand

由於Cocos2D-X 從3.0開始引入了一種新的渲染機制,所有的OpenGL渲染程式碼不再放到每一個node的draw函式裡面,而是通過各種RenderCommand封裝起來,然後新增到一個渲染佇列裡面去,最後在每一幀結束時把所有的這些命令都渲染出來。具體細節,大家可以參考這個文件.

首先,開啟HelloWorldScene.h,新增一個onDraw函式,一個CustomCommand成員變數,並且過載Layer的visit函式,程式碼如下:

1
2
3
4
5
6
7
8
9
10
class HelloWorld : public cocos2d::Layer
{
public: 
    //其它函式省略
    virtual void visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags) override;

    void onDraw();
private:
    CustomCommand _command;
};

然後我們實現這個visit函式:

1
2
3
4
5
6
7
void HelloWorld::visit(cocos2d::Renderer *renderer, const Mat4 &transform,uint32_t parentFlags)
{
    Layer::visit(renderer, transform, parentFlags);
    _command.init(_globalZOrder);
    _command.func = CC_CALLBACK_0(HelloWorld::onDraw, this);
    Director::getInstance()->getRenderer()->addCommand(&_command);
}

這裡要稍微解釋一下。由於此函式是個過載的虛擬函式,所以我們在函式的最開始呼叫了父類的visit函式。如果你不呼叫父類的visit函式,那麼當你往HelloWorldScene裡面新增節點的時候,它們是不會被渲染出來的。(這個留給讀者自己去完成)

然後,我們使用_globalZOrder和一個std::function來初始化CustomCommand。_globalZOrder會影響渲染的順序,這個在後面的博文中再詳細探討。而std::function會在CustomCommand被render佇列處理的時候被呼叫。最後我們把該CustomCommand新增到renderer裡面去。

最後,讓我們看看onDraw函式,它是整個繪製三角形的核心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void HelloWorld::onDraw()
{
    //獲得當前HelloWorld的shader
    auto glProgram = getGLProgram();
   //使用此shader
    glProgram->use();
   //設定該shader的一些內建uniform,主要是MVP,即model-view-project矩陣
    glProgram->setUniformsForBuiltins();

    auto size = Director::getInstance()->getWinSize();
    //指定將要繪製的三角形的三個頂點,分別位到螢幕左下角,右下角和正中間的頂端
    float vertercies[] = { 0,0,   //第一個點的座標
                            size.width, 0,   //第二個點的座標
                           size.width / 2, size.height};  //第三個點的座標
    //指定每一個頂點的顏色,顏色值是RGBA格式的,取值範圍是0-1
    float color[] = { 0, 1,0, 1,    //第一個點的顏色,綠色
                        1,0,0, 1,  //第二個點的顏色, 紅色
                         0, 0, 1, 1};  //第三個點的顏色, 藍色
    //啟用名字為position和color的vertex attribute
    GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR);
    //分別給position和color指定資料來源
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertercies);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, color);
    //繪製三角形,所謂的draw call就是指這個函式呼叫
    glDrawArrays(GL_TRIANGLES, 0, 3);
    //通知cocos2d-x 的renderer,讓它在合適的時候呼叫這些OpenGL命令
    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 3);
    //如果出錯了,可以使用這個函式來獲取出錯資訊
    CHECK_GL_ERROR_DEBUG();
}

如果你現在直接執行程式,會crash。這是因為我們還沒有指定Shader,所以下面的呼叫會失敗:

1
2
3
4
5
6
7
8
9
auto glProgram = getGLProgram();
glProgram->use();
glProgram->setUniformsForBuiltins();
``` 

接下來,讓我們在HelloWorldScene.cpp的init方法中加入下列程式碼:

```cpp
this->setGLProgram(GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_COLOR));

這個呼叫的含義是從Cocos2D-X的shader快取中取出一個帶有position和color頂點屬性的shader,然後傳給HelloWorld這個Layer.如果你是第一次接觸OpenGL ES,看到這句話肯定無法理解,不過沒有關係,後面的文章我們逐步講清楚。如果你等不及,也可以先看我在文章最後推薦的連結。

接下來,執行一下程式.恭喜你,你的第一個漂亮的三角形完成啦,還算簡單吧:)

cocos2d-x-es-1.1cocos2d-x-es-1.1

本教程原始碼下載,請認準tutorial1分支。

結束語

為了保持第一篇文章的簡單性,我只在畫三角形的程式碼裡面給了一些註釋,因為我並不想一開始就涉及到OpenGL底層的一些細節,而且有些內容我一時半會兒也很難說清楚。所以,我會在文章的最後給出一些參考連結,強烈推薦大家在看完本文後,有時間就多看一看這些連結,相信對理解上面的程式碼有幫助。下一篇文章中,我將給大家介紹如何編寫自己的shader,包括vertex attribute, uniform,vertex shader, fragment shader等內容。如果您對本文有什麼建議或者意義,歡迎在下方評論。

寫在最後

關於參考連結:所有的推薦閱讀都是我精心挑選的,部分內容我自己看過,另外一些我也正在計劃看。如果大家有好的資料,歡迎推薦給我。
關於評論:請不要找我要電子書,所有的電子書都可以通過google找到。

另外,我推薦的資料大部分都是英文版,如果對英文不是很感冒的同學,可以看翻譯的版本

推薦閱讀

網站:

視訊:

書籍: