天天看點

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

             本節所用Cocos2d-x版本:cocos2d-2.0-x-2.0.2

         一個圖形引擎,總是由建構點,線,面的繪制功能寫起來的。點,線,面。構成了最初的圖形基礎。是以說,掌握點,線,面是掌握引擎的基礎。

         在Cocos2d-x 1.0版本中,提供了使用OpenGL API來建立點,線,面的例子DrawPrimitivesTest。而在2.0中,同樣的例子名稱,而内部實作卻差别巨大。我們知道,在Cocos2d-x 2.0版本,相較于1.0,增加了shader的支援,而DrawPrimitivesTest這個例子,就是學習基礎Shader的最好教程。

學前提示:

       打開TestCpp工程,找到Classes下的DrawPrimitivesTest目錄。打開兩個檔案:

DrawPrimitivesTest.h/cpp

#ifndef _DRAW_PRIMITIVES_TEST_H_  

#define _DRAW_PRIMITIVES_TEST_H_  

//包含Cocos2d頭檔案  

////----#include "cocos2d.h"  

//使用TestScene這個CCScene類  

#include "../testBasic.h"  

//定義派生于CCLayer的類DrawPrimitivesTest,重載draw用于進行手動渲染處理  

class DrawPrimitivesTest : public CCLayer  

{  

public:  

//構造  

    DrawPrimitivesTest();  

    //析構  

virtual void draw();  

};  

//定義派生于TestScene的類DrawPrimitiveTestScene,做為TestCpp工程中TestController類集中管理的各個小功能例子的場景  

class DrawPrimitivesTestScene : public TestScene  

//重載啟動此功能例子的場景函數  

    virtual void runThisTest();  

#endif  

OK,頭檔案看完了,現在看CPP檔案:

#include "DrawPrimitivesTest.h"  

//構造函數  

DrawPrimitivesTest::DrawPrimitivesTest()  

}  

//手動處理的渲染函數  

void DrawPrimitivesTest::draw()  

//取得螢幕大小  

    CCSize s = CCDirector::sharedDirector()->getWinSize();  

//檢測是否有OpenGL錯誤發生,如果有則列印錯誤  

    CHECK_GL_ERROR_DEBUG();  

    //平滑模式,即高洛德着色  

//    glEnable(GL_LINE_SMOOTH);  

//繪制一條件,參1為起點,參2為終點,ccp為生成CCPoint的宏  

    ccDrawLine( ccp(0, 0), ccp(s.width, s.height) );  

    //設定線寬  

glLineWidth( 5.0f );  

//設定後面要進行繪制時所用的色彩  

ccDrawColor4B(255,0,0,255);  

//繪制線條  

ccDrawLine( ccp(0, s.height), ccp(s.width, 0) );  

    //設定點的大小  

ccPointSize(64);  

ccDrawColor4B(0,0,255,128);  

//繪制一個點  

    ccDrawPoint( ccp(s.width / 2, s.height / 2) );  

// 繪制四個點  

    //這裡建立位置點數組  

CCPoint points[] = { ccp(60,60), ccp(70,70), ccp(60,70), ccp(70,60) };  

ccPointSize(4);  

ccDrawColor4B(0,255,255,255);  

//使用位置點數組做為四個頂點的位置進行繪制  

    ccDrawPoints( points, 4);  

//繪制一個綠色圓  

glLineWidth(16);  

ccDrawColor4B(0, 255, 0, 255);  

    //繪制圓函數,參1是中心點,參2為半徑,參3為圓的逆時針旋轉角度,參4為圓的平均切分段數,最後一個參數是指定是否從圓分段起止點位置向圓中心連線,這裡不進行連線  

    ccDrawCircle( ccp(s.width/2,  s.height/2), 100, 0, 10, false);  

//繪制一個藍色圓,進行連線  

glLineWidth(2);  

ccDrawColor4B(0, 255, 255, 255);  

//這裡使用了一個宏CC_DEGREES_TO_RADIANS把角度值轉為弧度。轉動了90度,目的是為了讓中心連線垂直顯示。  

    ccDrawCircle( ccp(s.width/2, s.height/2), 50, CC_DEGREES_TO_RADIANS(90), 50, true);  

//繼續檢錯  

     CHECK_GL_ERROR_DEBUG();  

// 繪制多邊形線框。  

    ccDrawColor4B(255, 255, 0, 255);  

    glLineWidth(10);  

CCPoint vertices[] = { ccp(0,0), ccp(50,50), ccp(100,50), ccp(100,100), ccp(50,100) };  

//這裡繪制多邊形線框函數,使用上面的頂點數組做為多邊形線框的頂點位置,第二個參數為頂點數量,第三個參數指定是否首尾自動連接配接形成封閉線框。  

//注:其實這個函數拆成兩個函數比較好,一個是去掉最後一個參數的ccDrawPoly,用于繪制預設封閉的多邊形線框。另一個ccDrawLineList用于繪制線段列。  

    ccDrawPoly( vertices, 5, false);  

    //繪制實體多邊形  

    glLineWidth(1);  

    CCPoint filledVertices[] = { ccp(0,120), ccp(50,120), ccp(50,170), ccp(25,200), ccp(0,170) };  

    //這裡繪制内部填充指定色彩的多邊形  

    ccDrawSolidPoly(filledVertices, 5, ccc4f(0.5f, 0.5f, 1, 1 ) );  

    // 繪制封閉多邊形線框,這裡就是個三角形線框了。  

    ccDrawColor4B(255, 0, 255, 255);  

    glLineWidth(2);  

    CCPoint vertices2[] = { ccp(30,130), ccp(30,230), ccp(50,200) };  

    ccDrawPoly( vertices2, 3, true);  

        後面繪制貝塞爾曲線,要了解貝塞爾曲線,請在維基百科裡學習一下

        下面是繪制二次貝塞爾曲線,類似下圖,圖是從維基百科上找來的,恕我沒那個能力畫圖了暫拿來用講原理

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

//這個就是cocos2d-x2.0繪制二次貝塞爾曲線函數,三個參數分别如圖中P0,P1,P2,不過在咱們這個例子中,正好與之上下鏡像。最後一個是曲線構成所用的線段數,當然,線段數越多曲線越平滑。  

ccDrawQuadBezier(ccp(0,s.height), ccp(s.width/2,s.height/2), ccp(s.width,s.height), 50);  

//檢錯  

CHECK_GL_ERROR_DEBUG();  

        然後是繪制高階貝塞爾曲線,類似下圖

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

//前四個參數應該對應的是P0,P1,P3,P4,圖上的P2可以省去。最後一個是曲線構成所用的線段數。  

    ccDrawCubicBezier(ccp(s.width/2, s.height/2), ccp(s.width/2+30,s.height/2+50), ccp(s.width/2+60,s.height/2-50),ccp(s.width, s.height/2),100);  

再繼續

    //繪制黃色實心四邊形色塊。  

     CCPoint vertices3[] = {ccp(60,160), ccp(70,190), ccp(100,190), ccp(90,160)};  

    ccDrawSolidPoly( vertices3, 4, ccc4f(1,1,0,1) );  

    // 重置繪制狀态  

     glLineWidth(1);  

    ccDrawColor4B(255,255,255,255);  

    ccPointSize(1);  

//啟動場景  

void DrawPrimitivesTestScene::runThisTest()  

    //用new 建立一個DrawPrimitivesTest執行個體對象做為場景中要顯示的層。  

    CCLayer* pLayer = new DrawPrimitivesTest();  

    addChild(pLayer);  

    pLayer->release();  

    //替換目前正在運作的基它執行個體場景  

    CCDirector::sharedDirector()->replaceScene(this);  

好吧,運作一下,看圖:

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

      一個一個來對照代碼看一看,我們發現,其在調用ccPointSize進行點的大小設定時根本就不管用。 有點無語,可見這一版扣扣二弟放出來還是有點倉促~

      大家先看罷,吾上WC下,一會兒見。

      約過了五分鐘………..

      現在大家看懂照截圖看懂代碼了吧。那我們更深入一步吧。

      既然畫點有點問題咱就先看畫點。在ccDrawPoint函數調用處加斷點。F11進入CCDrawingPrimitives.cpp:

void ccDrawPoint( const CCPoint& point )  

    //Shader初始化函數,一會兒再解。  

    lazy_init();  

    //定義頂點變量,填充位置  

    ccVertex2F p;  

    p.x = point.x;  

    p.y = point.y;  

    //設定OpenGL在後面的渲染中用到頂點位置屬性。  

ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );  

//這裡對下面的渲染使用了Shader  

s_pShader->use();  

//設定Shader的輸入參數:最終矩陣結果值(世界x觀察x投影矩陣)。      

    s_pShader->setUniformForModelViewProjectionMatrix();  

    //設定Shader的輸入參數:色彩。取s_tColor.r位址做為float4參數值的位址傳入。實際Shader使用的是s_tColor的所有四個float值。  

s_pShader->setUniformLocationWith4fv(s_nColorLocation, (GLfloat*) &s_tColor.r, 1);  

//設定Shader的輸入參數:點大小  

    s_pShader->setUniformLocationWith1f(s_nPointSizeLocation, s_fPointSize);  

    //将p設定為使用的頂點位置參數  

    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, &p);  

//繪制頂點數組,參1為要繪制的圖形為點,參2為頂點起始索引,參3為頂點數量  

    glDrawArrays(GL_POINTS, 0, 1);  

    //渲染批次(DrawCall)統計  

    CC_INCREMENT_GL_DRAWS(1);  

       回頭看lazy_init,作者命名lazy_init的原因難道是說:我懶,初始化的相關處理就不每次寫了,放函數裡用多省心~。

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

      要看函數,先看檔案最上部作者定義的靜态變量。

static bool s_bInitialized = false; //用于标記是否初始化  

static CCGLProgram* s_pShader = NULL;  //Shader代碼程式  

static int s_nColorLocation = -1;       //Shader輸入色彩的變量位置索引  

static ccColor4F s_tColor = {1.0f,1.0f,1.0f,1.0f};  //色彩值  

static int s_nPointSizeLocation = -1;                   //Shader 輸入的大小的變量位置索引  

static GLfloat s_fPointSize = 1.0f;                 //大小值  

然後分析這個函數:

static void lazy_init( void )  

    //如果尚未初始化,則進行初始化,已經初始化則略過  

    if( ! s_bInitialized ) {  

        //通過字元串參數找到所對應Shader檔案的Shader代碼程式  

        s_pShader = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_Position_uColor);  

        //取得Shader對應變量"u_color"在程式片段中的位置索引,傳回給s_nColorLocation。  

        s_nColorLocation = glGetUniformLocation( s_pShader->getProgram(), "u_color");  

    //檢錯  

    //取得Shader對應變量"u_pointSize"在程式片段中的位置索引,傳回給s_nPointSizeLocation。  

        s_nPointSizeLocation = glGetUniformLocation( s_pShader->getProgram(), "u_pointSize");  

        //設定初始化完成  

        s_bInitialized = true;  

    }  

         lazy_init函數指明了目前這些繪制點,線,面所用的Shader為字元串變量kCCShader_Position_uColor所對應的Shader。在kCCShader_Position_uColor上按F12進入CCGLProgram.h,這裡定義了不少字元串變量:

#define kCCShader_PositionTextureColor            "ShaderPositionTextureColor"  

#define kCCShader_PositionTextureColorAlphaTest    "ShaderPositionTextureColorAlphaTest"  

#define kCCShader_PositionColor                    "ShaderPositionColor"  

#define kCCShader_PositionTexture                "ShaderPositionTexture"  

#define kCCShader_PositionTexture_uColor        "ShaderPositionTexture_uColor"  

#define kCCShader_PositionTextureA8Color        "ShaderPositionTextureA8Color"  

#define kCCShader_Position_uColor                "ShaderPosition_uColor"  

        Shader程式在哪呢?我們追查一下CCShaderCache::sharedShaderCache()->programForKey函數。進入到CCShaderCache.cpp檔案,這裡有一個CCShaderCache類,顧名思義,Shader緩沖。代碼不多,詳細解之:

#ifndef __CCSHADERCACHE_H__  

#define __CCSHADERCACHE_H__  

//使用到目錄相關處理類  

#include "cocoa/CCDictionary.h"  

//使用Cocos2d命名空間  

NS_CC_BEGIN  

//聲明使用類CCGLProgram  

class CCGLProgram;  

//由CCObject派生類CCShaderCache  

class CC_DLL CCShaderCache : public CCObject   

    //構造函數  

    CCShaderCache();  

    virtual ~CCShaderCache();  

    //傳回單件類執行個體指針  

    static CCShaderCache* sharedShaderCache();  

   //釋放所管理的所有Shader代碼片段  

    static void purgeSharedShaderCache();  

    //從相關檔案載入預設的一些Shader代碼片段  

    void loadDefaultShaders();  

   //從相關檔案重新載入預設的一些Shader代碼片段  

    void reloadDefaultShaders();  

   //從容器中查詢指定名稱的Shader代碼片段  

    CCGLProgram * programForKey(const char* key);  

    //将一個Shader代碼片段指定名稱放入容器  

    void addProgram(CCGLProgram* program, const char* key);  

private:  

    //初始化  

bool init();  

//按照頂點格式類型載入相應的Shader代碼片段  

    void loadDefaultShader(CCGLProgram *program, int type);  

    //目錄管理類  

    CCDictionary* m_pPrograms;  

NS_CC_END  

#endif /* __CCSHADERCACHE_H__ */  

Cpp檔案:

#include "CCShaderCache.h"  

#include "CCGLProgram.h"  

#include "ccMacros.h"  

#include "ccShaders.h"  

//枚舉,Shader代碼片段中的一些頂點格式類型  

enum {    

    kCCShaderType_PositionTextureColor, //頂點格式為位置+紋理UV+材質色  

    kCCShaderType_PositionTextureColorAlphaTest,    //頂點格式為頂點格式為位置+紋理UV+材質色+用于AlphaTest的ALPHA值  

    kCCShaderType_PositionColor,// 頂點格式為位置+材質色  

    kCCShaderType_PositionTexture, //頂點格式為位置+紋理UV   

    kCCShaderType_PositionTexture_uColor, //頂點格式為位置+紋理UV+材質色  

    kCCShaderType_PositionTextureA8Color, //頂點格式為位置+紋理UV+灰階  

    kCCShaderType_Position_uColor, //頂點格式為位置+材質色  

    kCCShaderType_MAX,  //枚舉結束值  

//靜态Shader緩沖指針  

static CCShaderCache *_sharedShaderCache = 0;  

//取得Shader緩沖單件執行個體指針  

CCShaderCache* CCShaderCache::sharedShaderCache()  

    if (!_sharedShaderCache) {  

        _sharedShaderCache = new CCShaderCache();  

        if (!_sharedShaderCache->init())  

        {  

            CC_SAFE_DELETE(_sharedShaderCache);  

        }  

    return _sharedShaderCache;  

//釋放  

void CCShaderCache::purgeSharedShaderCache()  

    //安全釋放并置空  

    CC_SAFE_RELEASE_NULL(_sharedShaderCache);  

CCShaderCache::CCShaderCache()  

: m_pPrograms(0)  

//析構  

CCShaderCache::~CCShaderCache()  

    CCLOGINFO("cocos2d deallocing 0x%X", this);  

    m_pPrograms->release();  

//初始化  

bool CCShaderCache::init()  

    //建立目錄管理器  

m_pPrograms = new CCDictionary();  

//從相關Shader檔案載入預設的Shader代碼片段  

    loadDefaultShaders();  

    return true;  

void CCShaderCache::loadDefaultShaders()  

    //建立一個Shader代碼片段  

CCGLProgram *p = new CCGLProgram();  

//通過類型枚舉值kCCShaderType_PositionTextureColor加載相應的Shader檔案到Shader代碼片段中,這裡是加載kCCShaderType_PositionTexture_uColor類型  

    loadDefaultShader(p, kCCShaderType_PositionTextureColor);  

    //将Shader代碼片段與代碼片段字元串名稱進行綁定存入容器  

    m_pPrograms->setObject(p, kCCShader_PositionTextureColor);  

    p->release();  

    //同上,建立Shader代碼片段并加載其它類型  

    p = new CCGLProgram();  

    loadDefaultShader(p, kCCShaderType_PositionTextureColorAlphaTest);  

    m_pPrograms->setObject(p, kCCShader_PositionTextureColorAlphaTest);  

   //建立第三種Shader代碼片段  

    loadDefaultShader(p, kCCShaderType_PositionColor);  

    m_pPrograms->setObject(p, kCCShader_PositionColor);  

   //建立第四種Shader代碼片段  

    loadDefaultShader(p, kCCShaderType_PositionTexture);  

    m_pPrograms->setObject(p, kCCShader_PositionTexture);  

   //建立第五種Shader代碼片段  

    loadDefaultShader(p, kCCShaderType_PositionTexture_uColor);  

    m_pPrograms->setObject(p ,kCCShader_PositionTexture_uColor);  

   //建立第六種Shader代碼片段  

    loadDefaultShader(p, kCCShaderType_PositionTextureA8Color);  

    m_pPrograms->setObject(p, kCCShader_PositionTextureA8Color);  

   //建立第七種Shader代碼片段  

    loadDefaultShader(p, kCCShaderType_Position_uColor);  

    m_pPrograms->setObject(p, kCCShader_Position_uColor);  

    p->release();      

//重新載入  

void CCShaderCache::reloadDefaultShaders()  

//通過字元串名稱找到對應的Shader代碼片段  

//  

CCGLProgram *p = programForKey(kCCShader_PositionTextureColor);      

//重置  

p->reset();  

    // 同上,重新載入其它類型的Shader代碼片段  

    p = programForKey(kCCShader_PositionTextureColorAlphaTest);  

    p->reset();      

    p = programForKey(kCCShader_PositionColor);  

    p->reset();  

    p = programForKey(kCCShader_PositionTexture);  

    p = programForKey(kCCShader_PositionTexture_uColor);  

    p = programForKey(kCCShader_PositionTextureA8Color);  

    p = programForKey(kCCShader_Position_uColor);  

    loadDefaultShader(p, kCCShaderType_Position_uColor);    

//按照頂點格式類型載入相應的Shader檔案,組成代碼片段  

void CCShaderCache::loadDefaultShader(CCGLProgram *p, int type)  

    switch (type) {  

        case kCCShaderType_PositionTextureColor:  

            //頂點格式為位置+紋理UV+材質色  

              //從“ccShader_PositionTextureColor_vert.h”和“ccShader_PositionTextureColor_frag.h”中為Shader代碼片段加載VS和PS  

p->initWithVertexShaderByteArray(ccPositionTextureColor_vert, ccPositionTextureColor_frag);  

            //将Shader代碼的輸入位置參數名稱與索引進行綁定  

            p->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);  

            //将Shader代碼的輸入色彩參數名稱與索引進行綁定  

            p->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);  

//将Shader代碼的輸入紋理坐标參數名稱與索引進行綁定  

            p->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);  

            break;  

        case kCCShaderType_PositionTextureColorAlphaTest:  

            //頂點格式為位置+紋理UV+材質色+用于AlphaTest的A通道  

           //從 p->initWithVertexShaderByteArray(ccPositionTextureColor_vert, ccPositionTextureColorAlphaTest_frag);  

           //将Shader代碼的輸入位置參數名稱與索引進行綁定   

           //将Shader代碼的輸入色彩參數名稱與索引進行綁定   

             //将Shader代碼的輸入玟理坐标參數名稱與索引進行綁定  

        case kCCShaderType_PositionColor:    

            //頂點格式為位置+材質色  

p->initWithVertexShaderByteArray(ccPositionColor_vert ,ccPositionColor_frag);  

            //将Shader代碼的輸入位置參數名稱與索引進行綁定 p->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);  

            //将Shader代碼的輸入色彩參數名稱與索引進行綁定p->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);  

        case kCCShaderType_PositionTexture:  

            //頂點格式為位置+紋理坐标p->initWithVertexShaderByteArray(ccPositionTexture_vert ,ccPositionTexture_frag);  

           //将Shader代碼的輸入位置參數名稱與索引進行綁定  

           //将Shader代碼的輸入色彩參數名稱與索引進行綁定  

        case kCCShaderType_PositionTexture_uColor:  

            //頂點格式為位置+紋理坐标p->initWithVertexShaderByteArray(ccPositionTexture_uColor_vert, ccPositionTexture_uColor_frag);  

            //将Shader代碼的輸入紋理坐标參數名稱與索引進行綁定  

        case kCCShaderType_PositionTextureA8Color:  

             //頂點格式為位置+灰階值p->initWithVertexShaderByteArray(ccPositionTextureA8Color_vert, ccPositionTextureA8Color_frag);  

//将Shader代碼的輸入色彩參數名稱與索引進行綁定   

        case kCCShaderType_Position_uColor:  

            p->initWithVertexShaderByteArray(ccPosition_uColor_vert, ccPosition_uColor_frag);      

            p->addAttribute("aVertex", kCCVertexAttrib_Position);      

        default:  

            //如果是其它列印錯誤  

            CCLOG("cocos2d: %s:%d, error shader type", __FUNCTION__, __LINE__);  

            return;  

    //将VS與PS進行連接配接  

p->link();  

//更新輸入參數  

    p->updateUniforms();  

//通過字元串鍵值查詢出相應的Shader代碼片段。  

CCGLProgram* CCShaderCache::programForKey(const char* key)  

    return (CCGLProgram*)m_pPrograms->objectForKey(key);  

//将一個新的Shader代碼片段設定字元串鍵值存儲到容器中  

void CCShaderCache::addProgram(CCGLProgram* program, const char* key)  

    m_pPrograms->setObject(program, key);  

         好了,從這個檔案我們可以知道。Shader緩沖類是對遊戲中用到的Shader檔案和代碼片段進行統一的管理。

        看明白了,自然找一下相應的Shader檔案。

        可以在ccShaders.cpp中的相應VS,PS的名稱上右鍵彈出菜單第一項打開相應的Shader檔案,也可以到Cocos2d-x解壓目錄下的cocos2dx\shaders目錄下找到這些h檔案,檔案不多,那既然畫點,線,面用到的Shader是"ShaderPosition_uColor",在CCShaderCache::loadDefaultShader函數中我們得知其對應的VS檔案是:ccShader_Position_uColor_vert.h,PS檔案是:ccShader_Position_uColor_frag.h

        注:作者用.h來做為擴充名,其實這個用什麼做擴充名都無所謂,總之是文本形式的檔案就OK!

我們找來看一下:ccShader_Position_uColor_vert.h:

attribute vec4 a_position;          //定義vec4接口變量a_position                       

uniform    mat4 u_MVPMatrix;       //傳入mat4參數u_MVPMatrix,World矩陣乘View矩陣乘Proj矩陣的累積矩陣          

uniform    vec4 u_color;           //傳入vec4參數u_color,用于色彩  

uniform float u_pointSize;         //傳入float 參數u_pointSize,用于點的大小                     

//如果使用的是OpenGL的ES版本,使用低精度的vec4變量 v_fragmentColor                                                     

#ifdef GL_ES                                          

varying lowp vec4 v_fragmentColor;                      

#else    

//如果使用的是OpenGL,使用高精度的vec4變量 v_fragmentColor                                                

varying vec4 v_fragmentColor;                          

#endif                                                  

//VS的入口函數                                                      

void main()                                              

{               

//頂點x矩陣u_MVPMatrix,将結果設定為VS傳回螢幕最終顯示位置                                      

gl_Position = u_MVPMatrix * a_position;   

//使用變量v_fragmentColor設定為VS輸出的頂點大小             

gl_PointSize = u_pointSize;  

//設定色彩變量值                          

    v_fragmentColor = u_color;                          

}                                                      

";  

ccShader_Position_uColor_frag.h:

//如果使用的是OpenGL的ES版本,float精度設為低精度                                      

#ifdef GL_ES                              

precision lowp float;                      

#endif                                      

//這裡vec4變量 v_fragmentColor,跟據OpenGL版本選擇使用精度。                      

varying vec4 v_fragmentColor;              

//PS的入口函數                           

void main()                                  

{                                          

    //使用變量v_fragmentColor設為Opengl的PS輸出色彩值  

gl_FragColor = v_fragmentColor;          

}                                          

        前邊我們說過,此例程有個BUG。雖然傳入了點的大小,但是實際卻沒有設定成功。作者應該是忘了在渲染前打開啟用VS頂點大小功能,回到DrawPrimitivesTest.cpp,把開啟和閉閉VS頂點大小的代碼加上:

// 設定點大小64  

//半透明的的藍色  

//glEnable(GL_POINT_SPRITE_ARB);加上此句為正方形,否則為圓形  

//此處啟用VS頂點大小功能  

glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);  

//繪制點  

ccDrawPoint( ccp(s.width / 2, s.height / 2) );    

//點大小為4  

//純青色  

//繪制四個點  

ccDrawPoints( points, 4);  

//此處關閉VS頂點大小功能  

    glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);  

//glDisable(GL_POINT_SPRITE_ARB);     

可以看到中心的半透明藍色圓點和左下方四個小青圓點。

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

       看完ccDrawPoint函數,我們重新進入 CCDrawingPrimitives.cpp看其它的繪制圖形函數:

//繪制多個點  

//參1為頂點位置數組  

//參2為頂點數量  

void ccDrawPoints( const CCPoint *points, unsigned int numberOfPoints )  

lazy_init();  

//Shader使用頂點位置屬性  

//後面的渲染應用Shader  

//設定Shader中的最終結果矩陣參數  

s_pShader->setUniformForModelViewProjectionMatrix();  

//設定Shader中的色彩參數,取s_tColor.r位址做為float4參數值的位址傳入。實際Shader使用的是s_tColor的所有四個float值。  

//設定Shader中的大小參數  

//這裡建立頂點位置數組,用于存儲指定數量的頂點位置。這段代碼放在這個位置有問題。看下一句分析。  

    ccVertex2F* newPoints = new ccVertex2F[numberOfPoints];  

//如果是32位的系統(Iphone和32位Windows),這裡似乎應該使用一個全局變量  

if( sizeof(CCPoint) == sizeof(ccVertex2F) )  

//将參數指向的頂點位置資料與位置屬性進行綁定  

        glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, points);  

    else  

//ccVertex2F* newPoints = new ccVertex2F[numberOfPoints]應該放在這裡更好。因為如果是32位系統,則根本沒有必要建立頂點數組。同樣把後面的CC_SAFE_DELETE_ARRAY(newPoints);放在這個else的大括号内。  

        // 如果是64位的系統,則周遊填充資料  

        for( unsigned int i=0; i<numberOfPoints;i++) {  

            newPoints[i].x = points[i].x;  

            newPoints[i].y = points[i].y;  

//将新建立的頂點位置數組與位置屬性進行綁定   

  glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, newPoints);  

    glDrawArrays(GL_POINTS, 0, (GLsizei) numberOfPoints);  

//釋放申請的頂點數組占用記憶體。各位看官,這裡可以研讨一下:我個人對在渲染時new這樣一個數組後又delete的做法是感覺比較不妥的,可能産生碎片。如果沒有多線程渲染,倒是不如直接在開始時就建立好一個固定長的數組,這裡直接使用。缺點當然是定長可能會有浪費,但是所有渲染函數可以共用,避免了申請記憶體的時間開銷,效率非常高,而且存的資料不多,占用不了太多的記憶體。  

    CC_SAFE_DELETE_ARRAY(newPoints);  

//統計渲染調用次數  

//畫線函數  

//參1:起點位置  

//參2:終點位置  

void ccDrawLine( const CCPoint& origin, const CCPoint& destination )  

//定義2個頂點的位置數組  

    ccVertex2F vertices[2] = {  

        {origin.x, origin.y},  

        {destination.x, destination.y}  

    };  

//設定後面的渲染使用Shader  

    s_pShader->setUniformLocationWith4fv(s_nColorLocation, (GLfloat*) &s_tColor.r, 1);  

    ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );  

    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);  

//繪制頂點數組,參1為要繪制的圖形為線段,參2為頂點起始索引,參3為頂點數量  

    glDrawArrays(GL_LINES, 0, 2);  

//繪制矩形線框  

//參1:左下角位置點  

//參2:右上角位置點  

void ccDrawRect( CCPoint origin, CCPoint destination )  

//繪制四條邊,這樣寫倒是簡單,不過效率不高,因為需要設多次渲染狀态并渲染調用多次。不如在這裡建立頂點數組調ccDrawPoly 效率高,設一次渲染狀态,調用一次渲染處理就OK了。  

    ccDrawLine(CCPointMake(origin.x, origin.y), CCPointMake(destination.x, origin.y));  

    ccDrawLine(CCPointMake(destination.x, origin.y), CCPointMake(destination.x, destination.y));  

    ccDrawLine(CCPointMake(destination.x, destination.y), CCPointMake(origin.x, destination.y));  

    ccDrawLine(CCPointMake(origin.x, destination.y), CCPointMake(origin.x, origin.y));  

//繪制實心矩形  

//參3:填充色彩  

void ccDrawSolidRect( CCPoint origin, CCPoint destination, ccColor4F color )  

//建立四個頂點的位置數組  

    CCPoint vertices[] = {  

        origin,  

        ccp(destination.x, origin.y),  

        destination,  

        ccp(origin.x, destination.y)  

//使用頂點位置數組做為參數調用繪制填充多邊形  

    ccDrawSolidPoly(vertices, 4, color );  

//繪制多線形線框  

//參1:頂點位置數組  

//參2:頂點數組中的頂點數量  

//參3:是否封閉,即是否首尾相連  

void ccDrawPoly( const CCPoint *poli, unsigned int numberOfPoints, bool closePolygon )  

//設定Shader中的頂點色彩參數  

//無奈,又把new放判斷外邊了。  

    ccVertex2F* newPoli = new ccVertex2F[numberOfPoints];  

    //32位系統,實際用不到newPoli ,将參數poli與頂點位置屬性綁定即可  

    if( sizeof(CCPoint) == sizeof(ccVertex2F) )  

        glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, poli);  

    {  

        //64位系統,填充一下資料  

            newPoli[i].x = poli[i].x;  

            newPoli[i].y = poli[i].y;  

//将newPoli與頂點位置屬性綁定  

        glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, newPoli);  

//是否首尾相連  

    if( closePolygon )  

        glDrawArrays(GL_LINE_LOOP, 0, (GLsizei) numberOfPoints);  

        glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) numberOfPoints);  

//釋放申請的頂點位置數組  

    CC_SAFE_DELETE_ARRAY(newPoli);  

//繪制填充色彩的多邊形  

//參1:頂點數組  

//參2:頂點數量  

//參3:色彩值  

void ccDrawSolidPoly( const CCPoint *poli, unsigned int numberOfPoints, ccColor4F color )  

    s_pShader->setUniformLocationWith4fv(s_nColorLocation,(GLfloat*) &s_tColor.r, 1);  

    //不說了,你懂的  

        // Mac on 64-bit  

        for( unsigned int i=0; i<numberOfPoints;i++)  

            newPoli[i] = vertex2( poli[i].x, poli[i].y );  

    }      

//這裡參數1改為GL_TRIANGLE_FAN,即按扇面順序方式繪制三角形。  

    glDrawArrays(GL_TRIANGLE_FAN, 0, (GLsizei) numberOfPoints);  

//你懂的  

//繪制圓,在畫圓算法中,圓其實是由多個小線段連線構成的封閉多邊形線框。段數直多,就越像圓。  

//參1:圓心位置  

//參2:半徑  

//參3:圓在逆時針方向的轉動角度  

//參4:段數  

//參5:是否在段起止點處向圓心連線  

void ccDrawCircle( const CCPoint& center, float radius, float angle, unsigned int segments, bool drawLineToCenter)  

//這裡設變量additionalSegment為增加段的數量,預設為1。即用來做最後尾部與首部相連以形成封閉線框的線段。  

int additionalSegment = 1;  

//如果在段起止點處向圓心連線,就再加一條線段。  

    if (drawLineToCenter)  

        additionalSegment++;  

    //通過圓周的弧度除以段數計算每個段所跨的弧度  

 const float coef = 2.0f * (float)M_PI/segments;  

//申請相應的頂點數組,納悶了這裡為什麼不用ccVertex2F而使用GLfloat來存頂點的位置,因為一個頂點的位置是二維點,有x,y兩個float值才能表示。是以這裡在計算記憶體大小時乘2,段數值它這裡用了最大可能情況值(segments + 2),也可以改為segments + additionalSegment,這樣如果不在段起止點處向圓心連線,記憶體申請會小一點,但後面儲存圓心也需要做改動處理。  

    GLfloat *vertices = (GLfloat*)calloc( sizeof(GLfloat)*2*(segments+2), 1);  

    if( ! vertices )  

        return;  

//for循環每一個段設定頂點位置  

    for(unsigned int i = 0;i <= segments; i++) {  

        float rads = i*coef;  

        GLfloat j = radius * cosf(rads + angle) + center.x;  

        GLfloat k = radius * sinf(rads + angle) + center.y;  

        vertices[i*2] = j;  

        vertices[i*2+1] = k;  

//存儲圓心,這裡也可以改動一下效率更好,判斷一下additionalSegment是否大于1才處理。  

    vertices[(segments+1)*2] = center.x;  

    vertices[(segments+1)*2+1] = center.y;  

//你懂的,親~  

    s_pShader->use();  

glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);  

//這裡參數1指定按GL_LINE_STRIP順序方式繪制線段。    

glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segments+additionalSegment);  

//你懂的~  

    free( vertices );  

//繪制二次貝塞爾曲線  

//參1:起點  

//參2:控制點  

//參3:結束點  

//參4:構成曲線的線段數  

void ccDrawQuadBezier(const CCPoint& origin, const CCPoint& control, const CCPoint& destination, unsigned int segments)  

//建立頂點數組  

    ccVertex2F* vertices = new ccVertex2F[segments + 1];  

//周遊每一段,用計算公式計算點的位置  

    float t = 0.0f;  

    for(unsigned int i = 0; i < segments; i++)  

        vertices[i].x = powf(1 - t, 2) * origin.x + 2.0f * (1 - t) * t * control.x + t * t * destination.x;  

        vertices[i].y = powf(1 - t, 2) * origin.y + 2.0f * (1 - t) * t * control.y + t * t * destination.y;  

        t += 1.0f / segments;  

//目标點  

    vertices[segments].x = destination.x;  

    vertices[segments].y = destination.y;  

//使用Shader進行渲染  

    glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segments + 1);  

    CC_SAFE_DELETE_ARRAY(vertices);  

//繪制預設曲率的基數樣條。關于基數樣條可以參看:http://technet.microsoft.com/zh-cn/4cf6we5y(v=vs.85)  

//基數樣條是一連串單獨的曲線,這些曲線連接配接起來形成一條較大的曲線。 樣條由點的數組和張力參數指定。 基數樣條平滑地經過數組中的每個點;曲線的陡度上沒有尖角和突然的變化。 下面的插圖顯示了一組點和經過這一組點中每一點的基數樣條。   

//參2:構成曲線的線段數  

void ccDrawCatmullRom( CCPointArray *points, unsigned int segments )  

    ccDrawCardinalSpline( points, 0.5f, segments );  

//繪制可指定曲率的基數樣條  

//參2:曲率  

//參3:構成曲線的線段數  

void ccDrawCardinalSpline( CCPointArray *config, float tension,  unsigned int segments )  

    unsigned int p;  

    float lt;  

    float deltaT = 1.0f / config->count();  

    for( unsigned int i=0; i < segments+1;i++) {  

        float dt = (float)i / segments;  

        // border  

        if( dt == 1 ) {  

            p = config->count() - 1;  

            lt = 1;  

        } else {  

            p = dt / deltaT;  

            lt = (dt - deltaT * (float)p) / deltaT;  

        // Interpolate  

        CCPoint pp0 = config->getControlPointAtIndex(p-1);  

        CCPoint pp1 = config->getControlPointAtIndex(p+0);  

        CCPoint pp2 = config->getControlPointAtIndex(p+1);  

        CCPoint pp3 = config->getControlPointAtIndex(p+2);  

        CCPoint newPos = ccCardinalSplineAt( pp0, pp1, pp2, pp3, tension, lt);  

        vertices[i].x = newPos.x;  

        vertices[i].y = newPos.y;  

    s_pShader->setUniformForModelViewProjectionMatrix();      

    s_pShader->setUniformLocationWith4fv(s_nColorLocation, (GLfloat*)&s_tColor.r, 1);  

在本例程中,沒有展示基數樣條,我稍做了下改動,在繪制圖形的函數加入了:

    //這裡建立5個頂點  

     CCPointArray*  tpSplinePtArray = CCPointArray::arrayWithCapacity(5);  

    //填充頂點  

    tpSplinePtArray->addControlPoint(ccp(0, 0));  

    tpSplinePtArray->addControlPoint(ccp(60,100));  

    tpSplinePtArray->addControlPoint(ccp(250,200));  

    tpSplinePtArray->addControlPoint(ccp(350,100));  

    tpSplinePtArray->addControlPoint(ccp(450,280));  

//用預設曲率繪制段數為50的綠色基數樣條  

ccDrawColor4B(0,255,0,255);  

    ccDrawCatmullRom(tpSplinePtArray,50);     

//重置繪制參數值  

glLineWidth(1);  

運作後可以看到多了一條綠色基數樣條。  

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

然後繼續:

//繪制高階貝賽爾曲線  

//參2:控制點1  

//參3:控制點2  

//參4:結束度  

//參5:構成曲線的線段數  

void ccDrawCubicBezier(const CCPoint& origin, const CCPoint& control1, const CCPoint& control2, const CCPoint& destination, unsigned int segments)  

    float t = 0;  

        vertices[i].x = powf(1 - t, 3) * origin.x + 3.0f * powf(1 - t, 2) * t * control1.x + 3.0f * (1 - t) * t * t * control2.x + t * t * t * destination.x;  

        vertices[i].y = powf(1 - t, 3) * origin.y + 3.0f * powf(1 - t, 2) * t * control1.y + 3.0f * (1 - t) * t * t * control2.y + t * t * t * destination.y;  

//設定靜态色彩變量值s_tColor,因為很多繪制圖形函數都用到這個值做為色彩參數傳入Shader,故要更改色彩,在渲染前調用此函數進行設定即可。這裡參數分别代表r,g,b,a,用0.0~1.0來表示0~255的值  

void ccDrawColor4F( GLfloat r, GLfloat g, GLfloat b, GLfloat a )  

    s_tColor.r = r;  

    s_tColor.g = g;  

    s_tColor.b = b;  

    s_tColor.a = a;  

//設定代表頂點大小的靜态變量值,用于渲染點時傳入Shader  

void ccPointSize( GLfloat pointSize )  

    s_fPointSize = pointSize * CC_CONTENT_SCALE_FACTOR();  

//設定靜态色彩變量值s_tColor,這裡參數用0~255。  

void ccDrawColor4B( GLubyte r, GLubyte g, GLubyte b, GLubyte a )  

    s_tColor.r = r/255.0f;  

    s_tColor.g = g/255.0f;  

    s_tColor.b = b/255.0f;  

    s_tColor.a = a/255.0f;  

另外,改動兩個Cocos2d-x的底層代碼:在cocos2dx目錄的CCDrawingPrimitives.cpp中

增加線寬:左邊是原代碼,右邊是改後代碼

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

 優化畫圓

Cocos2d-x2.0 -- 從點,線,面學起Cocos2d-x  2.0 -- 從 點,線,面學起

繼續閱讀