本節所用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繪制二次貝塞爾曲線函數,三個參數分别如圖中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();
然後是繪制高階貝塞爾曲線,類似下圖
//前四個參數應該對應的是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);
好吧,運作一下,看圖:
一個一個來對照代碼看一看,我們發現,其在調用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的原因難道是說:我懶,初始化的相關處理就不每次寫了,放函數裡用多省心~。
要看函數,先看檔案最上部作者定義的靜态變量。
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);
可以看到中心的半透明藍色圓點和左下方四個小青圓點。
看完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);
運作後可以看到多了一條綠色基數樣條。
然後繼續:
//繪制高階貝賽爾曲線
//參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中
增加線寬:左邊是原代碼,右邊是改後代碼
優化畫圓