1. 程式人生 > >如何使用CCRenderTexture創建動態紋理 Cocos2d-x 2 1 4

如何使用CCRenderTexture創建動態紋理 Cocos2d-x 2 1 4

world texture load ack 結果 cos cse back ()

本文實踐自 Ray Wenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在這裏使用Cocos2D-x 2.1.4進行學習和移植。在這篇文章,將會學習到如何創建實時紋理、如何用Gimp創建無縫拼接紋理、如何混合陰影和光照到紋理上以顯現逼真效果、如何創建條紋紋理、如何設置紋理重復等等。

步驟如下:
1.新建Cocos2d-win32工程,工程名為"TinySeal",勾選"Box2D"選項(後續文章會使用到),勾選"Simple Audio Engine in Cocos Denshion

"選項;
2.打開HelloWorldScene.cpp文件,在添加如下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CCSprite *HelloWorld::spriteWithColor(ccColor4F bgColor, float textureWidth, float textureHeight)
{
// 1: Create new CCRenderTexture
CCRenderTexture *rt = CCRenderTexture::create(textureWidth, textureHeight);

// 2: Call CCRenderTexture:begin
rt->beginWithClear(bgColor.r, bgColor.g, bgColor.b, bgColor.a);

// 3: Draw into the texture
// You‘ll add this later

// 4: Call CCRenderTexture:end
rt->end();

// 5: Create a new Sprite from the texture
return CCSprite::createWithTexture(rt->getSprite()->getTexture());
}

正如你所見,這5個步驟是用來創建動態紋理的,具體描述如下:
①.創建一個新的CCRenderTexture:指定所要創建紋理的寬度和高度。
②.調用CCRenderTexture的begin方法:設置OpenGL以便之後的任何圖形繪制都在CCRenderTexture上,而不是屏幕上。
③.繪制紋理:使用原始的OpenGL命令來繪制,或通過調用現有的Cocos2D對象的visit方法。
④.調用CCRenderTexture的end方法:渲染紋理,關閉繪制到紋理上。
⑤.以紋理創建一個新的精靈:以CCRenderTexture的getSprite()->getTexture()來創建一個新的精靈。
註意這裏不是調用CCRenderTexture:begin方法,而是調用一個更方便的方法beginWithClear,可以在繪制之前,用特定的顏色來清除紋理。
3.接著打開HelloWorldScene.h文件,添加如下代碼:

1
2
private:
cocos2d::CCSprite *_background;

打開HelloWorldScene.cpp文件,在構造函數裏添加如下代碼:

1 _background = NULL;

修改init函數為如下:

1
2
3
4
5
6
7
8
9
10
11
12
bool HelloWorld::init()
{
bool bRet = false;
do
{
CC_BREAK_IF(! CCLayer::init());

bRet = true;
} while (0);

return bRet;
}

添加以下方法:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
ccColor4F HelloWorld::randomBrightColor()
{
while (true)
{
float requiredBrightness = 192;
ccColor4B randomColor = ccc4(rand() % 255,
rand() % 255,
rand() % 255,
255);
if (randomColor.r > requiredBrightness ||
randomColor.g > requiredBrightness ||
randomColor.b > requiredBrightness)
{
return ccc4FFromccc4B(randomColor);
}
}
}

void HelloWorld::genBackground()
{
if (_background)
{
_background->removeFromParentAndCleanup(true);
}

ccColor4F bgColor = this->randomBrightColor();
_background = this->spriteWithColor(bgColor, 512, 512);

CCSize winSize = CCDirector::sharedDirector()->getWinSize();
_background->setPosition(ccp(winSize.width / 2, winSize.height / 2));
this->addChild(_background, -1);
}

void HelloWorld::onEnter()
{
CCLayer::onEnter();
this->genBackground();
this->setTouchEnabled(true);
}

void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
this->genBackground();
}

randomBrightColor是一個輔助方法,用來創建隨機顏色。註意到這裏是使用ccc4B,所以可以指定R/G/B/A值在0-255範圍內,並且確保至少其中之一大於192,這樣就不會得到較深的顏色。然後再將顏色轉換成ccc4F。genBackground方法調用spriteWithColor方法,然後把創建的精靈加到屏幕中央。onEnter方法調用genBackground,並且開啟觸摸,這樣觸摸屏幕就可以重新生成另一個隨機背景。編譯運行,每次運行程序或觸摸屏幕都會有不同顏色的背景,如下圖所示:
技術分享圖片
4.創建噪音紋理。用一些噪音來進行修飾,這樣會使得它們看起來像是有陰影和光照。可以編寫一些代碼生成動態噪音,但是使用一些預先做好的噪音,這將更容易,性能也更高,這也是這裏所將要介紹的。一種簡單的方法來創建隨機噪音,是通過一個免費的圖像編輯程序,名為Gimp。在這裏,可以下載已經制作好的圖片(下載),跳過這部分。也可以下載Gimp(下載,當前2.8.4版本),動手制作。啟動Gimp程序,菜單欄→“文件”→“新建”,創建一張512x512大小的圖像。菜單欄→“濾鏡”→“繪制”→“雲彩”→“純色噪音”,如果需要的話,可以在這裏調整參數,然後點擊“確定”。這樣就會得到看起來像下圖那樣的圖片:
技術分享圖片
將會使用這張圖片乘以紋理的顏色。因此,圖片中的白色部分,將使原來的顏色顯現出來,而黑色的部分,將使原來的顏色變得更暗。目前,圖片中有太多的黑色部分,這會影響到我們所需要的效果。所以要減少黑色部分的數量,菜單欄→“顏色”→“色階”,拖動“輸出色階”最左邊的滑塊到右邊一些,這裏調整為160,如下圖所示:
技術分享圖片
將會看到圖片變得明亮許多,如下圖所示:
技術分享圖片
噪音紋理需要制作成無縫拼接,這樣才能創建拼接完整的重復紋理。菜單欄→“濾鏡”→“映射”→“無縫處理”即可。最後保存文件,菜單欄→“文件”→“導出到”,名稱為Noise.png,保存到工程Resources文件夾下。
5.應用噪音紋理。把這張噪音圖片應用到用CCRenderTexture所創建的紋理上。在spriteWithColor函數裏面,第3步註釋後面添加如下代碼:

1
2
3
4
5
CCSprite *noise = CCSprite::create("Noise.png");
ccBlendFunc blendFunc = {GL_DST_COLOR, GL_ZERO};
noise->setBlendFunc(blendFunc);
noise->setPosition(ccp(textureWidth / 2, textureHeight / 2));
noise->visit();

這裏用噪音紋理來創建精靈,將它放置在渲染紋理的中心,然後調用visit方法。這個visit方法就是一系列的OpenGL ES命令調用,來繪制紋理。ccBlendFunc的第一個常量(GL_DST_COLOR)指定如何乘以輸入/源(也就是噪音紋理)的顏色,第二個常量(GL_ZERO)指定如何乘以現有的/目標(也就是上面的純色紋理)的顏色。具體如下:

  • 現有的顏色乘以GL_ZERO,意味著現有的顏色被清除掉。
  • 噪音紋理的顏色乘以GL_DST_COLOR,而GL_DST_COLOR意味著現有的顏色,因此噪音紋理的顏色乘以現有的顏色。所以噪音中白色部分越多,那麽對應現有的顏色將會越明顯,而噪音中黑色部分越多,則對應現有的顏色越暗。
  • 上述兩種顏色進行相加,由於第一種顏色為零,所以真正重要的是第二種顏色結果。
這些混合常量令人迷惑,幸運的是有一個免費的在線工具,可以可視化的看到這些混合常量搭配的效果。編譯運行,將可以看到紋理上的一些陰影效果,如下圖所示:
技術分享圖片
6.添加漸變到紋理上。為了使紋理看起來更好些,添加一個從上到下的漸變,紋理越往下越暗。雖然可以通過Gimp修改噪音文件達到效果,但也可以用代碼做到,這樣使得事情更加動態,而且容易修改。基本的想法是,在紋理之上繪制一個黑色矩形,它的頂部完全透明,而底部不透明,這將使得紋理頂部不變,而底部逐漸變暗下去。要做到這一點,需要用到一些OpenGL命令。在spriteWithColor函數裏面,創建噪音精靈之前,添加如下代碼:

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
this->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionColor));

CC_NODE_DRAW_SETUP();

// 3: Draw into the texture
float gradientAlpha = 0.7f;
CCPoint vertices[4];
ccColor4F colors[4];
int nVertices = 0;

vertices[nVertices] = CCPointMake(0, 0);
colors[nVertices++] = ccc4f(0, 0, 0, 0);
vertices[nVertices] = CCPointMake(textureWidth, 0);
colors[nVertices++] = ccc4f(0, 0, 0, 0);
vertices[nVertices] = CCPointMake(0, textureHeight);
colors[nVertices++] = ccc4f(0, 0, 0, gradientAlpha);
vertices[nVertices] = CCPointMake(textureWidth, textureHeight);
colors[nVertices++] = ccc4f(0, 0, 0, gradientAlpha);

ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color);

glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, colors);
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);

繪制紋理時,左上角是(0,0)點,而不是Cocos2D中的左下角。因此,以上代碼首先定義了紋理的四個頂點,順序為左上、右上、左下、右下,還定義了每個點的顏色。以這樣的順序來繪制頂點,是因為將繪制兩個三角形來形成一個矩形,如下圖所示:
技術分享圖片
使用GL_TRIANGLE_STRIP來繪制這些三角形,這意味著,第一個三角形由頂點數組中的前三個頂點組成,其余的三角形由前一個三角形的後兩個頂點和下一個頂點組成。因此,這裏第一個三角形由V0、V1、V2組成,第二個三角形由V1、V2、V3組成。定義完頂點數組和顏色數組之後,傳遞頂點數組和kCCVertexAttrib_Position常量給glVertexAttribPointer,同樣傳遞顏色數組和kCCVertexAttrib_Color常量給glVertexAttribPointer。編譯運行,可以看到一個整齊的漸變紋理,如下圖所示:
技術分享圖片
7.創建條紋紋理。將以一種顏色(如藍色)著色紋理開始,然後繪制一些條紋(如綠色)斜穿過它,如下圖所示:
技術分享圖片
註意,因為條紋是斜向的,實際上要在紋理的邊界之外就開始繪制,並且在紋理的邊界之外繼續繪制一些條紋。另外還要註意,為了得到一個不錯的45度角,把V0偏移紋理的高度,這樣兩邊都是紋理高度,因此,可以得到一個45度角。添加一個新的方法,代碼如下:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
CCSprite * HelloWorld::spriteWithColor1(ccColor4F c1, ccColor4F c2, float textureWidth, float textureHeight, int nStripes)
{
// 1: Create new CCRenderTexture
CCRenderTexture *rt = CCRenderTexture::create(textureWidth, textureHeight);

// 2: Call CCRenderTexture:begin
rt->beginWithClear(c1.r, c1.g, c1.b, c1.a);

// 3: Draw into the texture
this->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionColor));
CC_NODE_DRAW_SETUP();

// Layer 1: Stripes
CCPoint *vertices = new CCPoint[nStripes * 6];
ccColor4F *colors = new ccColor4F[nStripes * 6];

int nVertices = 0;
float x1 = -textureHeight;
float x2;
float y1 = textureHeight;
float y2 = 0;
float dx = textureWidth / nStripes * 2;
float stripeWidth = dx / 2;
for (int i = 0; i < nStripes; ++i)
{
x2 = x1 + textureHeight;

vertices[nVertices] = ccp(x1, y1);
colors[nVertices++] = ccc4f(c2.r, c2.g, c2.b, c2.a);

vertices[nVertices] = ccp(x1 + stripeWidth, y1);
colors[nVertices++] = ccc4f(c2.r, c2.g, c2.b, c2.a);

vertices[nVertices] = ccp(x2, y2);
colors[nVertices++] = ccc4f(c2.r, c2.g, c2.b, c2.a);

vertices[nVertices] = vertices[nVertices - 2];
colors[nVertices++] = ccc4f(c2.r, c2.g, c2.b, c2.a);

vertices[nVertices] = vertices[nVertices - 2];
colors[nVertices++] = ccc4f(c2.r, c2.g, c2.b, c2.a);

vertices[nVertices] = ccp(x2 + stripeWidth, y2);
colors[nVertices++] = ccc4f(c2.r, c2.g, c2.b, c2.a);
x1 += dx;
}

ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color);
glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_TRUE, 0, colors);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)nVertices);

CC_SAFE_DELETE_ARRAY(vertices);
CC_SAFE_DELETE_ARRAY(colors);

// Layer 4: Noise
CCSprite *noise = CCSprite::create("Noise.png");
ccBlendFunc blendFunc = {GL_DST_COLOR, GL_ZERO};
noise->setBlendFunc(blendFunc);
noise->setPosition(ccp(textureWidth / 2, textureHeight / 2));
noise->visit();

// 4: Call CCRenderTexture:end
rt->end();

// 5: Create a new Sprite from the texture
return CCSprite::createWithTexture(rt->getSprite()->getTexture());
}
這個方法的大部分內容是回顧如何創建一個CCRenderTexture,但是創建條紋層的代碼是新的,這部分代碼,它首先創建了頂點數組和顏色數組,對於每個條紋,需要6個頂點,即3個頂點乘以2個三角形。不能使用GL_TRIANGLE_STRIP,因為條紋不相鄰。顏色數組存儲三角形的每個頂點顏色值。第一個頂點位於(-textureHeight, textureHeight),正如上圖所看到的。下一個頂點位於(-textureHeight+stripWidth, textureHeight),第三個頂點位於(0, 0),而第四個頂點位於(stripeWidth,0)。這些是一個條紋所需要的頂點,每次往前遞增條紋的兩倍寬度,並且持續這樣直到所有條紋完成。修改genBackground函數為如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void HelloWorld::genBackground()
{
if (_background)
{
_background->removeFromParentAndCleanup(true);
}

ccColor4F bgColor = this->randomBrightColor();
ccColor4F color2 = this->randomBrightColor();
//_background = this->spriteWithColor(bgColor, 512, 512);
int nStripes = ((rand() % 4) + 1) * 2;
_background = this->spriteWithColor1(bgColor, color2, 512, 512, nStripes);

this->setScale(0.5f);

CCSize winSize = CCDirector::sharedDirector()->getWinSize();
_background->setPosition(ccp(winSize.width / 2, winSize.height / 2));
this->addChild(_background);
}

這裏調用新的方法,並且還設置了層的縮放為0.5,使其更容易地看到整個紋理。編譯運行,每當觸摸屏幕都可以看到隨機生成的條紋紋理,如下圖所示:
技術分享圖片
8.重復背景。對於條紋背景和漸變背景,都希望能夠平鋪整個區域空間,這可能比紋理更寬。一種簡單的方式是創建多個CCSprites,讓它們鏈接在一起。但那太瘋狂了,因為還有一種更簡單的方式,設置紋理重復。修改genBackground函數,在addChild之前添加如下代碼:

1
2
ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
_background->getTexture()->setTexParameters(&tp);

onEnter函數底端,添加如下代碼:

1 this->scheduleUpdate();

添加如下方法:

1
2
3
4
5
6
7
8
9
void HelloWorld::update(float dt)
{
float PIXELS_PER_SECOND = 100;
static float offset = 0;
offset += PIXELS_PER_SECOND * dt;

CCSize textureSize = _background->getTextureRect().size;
_background->setTextureRect(CCRectMake(offset, 0, textureSize.width, textureSize.height));
}

這裏最重要的部分就是紋理的參數:

  • GL_LINEAR:當以比原始大小更小或者更大的尺寸,來顯示紋理時,采用鄰近像素的加權平均值。
  • GL_REPEAT:當需要顯示紋理邊界之外時,顯示的是平鋪紋理。
此外,安裝一個定時器,定時更新紋理的可見部分,使其沿著X軸不斷地向前移動。這會使得紋理隨著時間的推移,不斷地重復。編譯運行,可以看到一個不斷滾動和重復的紋理,如下圖所示:
技術分享圖片
也可以用漸變紋理來嘗試一下,也是如此。
9.光照效果。為了使它看起來更佳,讓它在頂部有一些輕微的光照,並且還有從上到下的漸變。修改前面的條紋紋理,在spriteWithColor1函數裏面,glDrawArrays代碼之後,添加如下代碼:
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
31
32
33
34
35
36
37
38
39
40
41
42
// Layer 2: gradient
float gradientAlpha = 0.7f;
nVertices = 0;

vertices[nVertices] = ccp(0, 0);
colors[nVertices++] = ccc4f(0, 0, 0, 0);

vertices[nVertices] = ccp(textureWidth, 0);
colors[nVertices++] = ccc4f(0, 0, 0, 0);

vertices[nVertices] = ccp(0, textureHeight);
colors[nVertices++] = ccc4f(0, 0, 0, gradientAlpha);

vertices[nVertices] = ccp(textureWidth, textureHeight);
colors[nVertices++] = ccc4f(0, 0, 0, gradientAlpha);

glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_TRUE, 0, colors);
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);

// Layer 3: top highlight
float borderHeight = textureHeight / 16;
float borderAlpha = 0.3f;
nVertices = 0;

vertices[nVertices] = ccp(0, 0);
colors[nVertices++] = ccc4f(1, 1, 1, borderAlpha);

vertices[nVertices] = ccp(textureWidth, 0);
colors[nVertices++] = ccc4f(1, 1, 1, borderAlpha);

vertices[nVertices] = ccp(0, borderHeight);
colors[nVertices++] = ccc4f(0, 0, 0, 0);

vertices[nVertices] = ccp(textureWidth, borderHeight);
colors[nVertices++] = ccc4f(0, 0, 0, 0);

glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_TRUE, 0, colors);
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);
以上代碼第一部分創建漸變背景,第二部分在條紋頂部增加一個光照,使它看起來像是陽光照射在上面一樣。編譯運行,現在可以看到條紋更加逼真,因為帶有漸變和光照效果,如下圖所示:

技術分享圖片
參考資料:
1.How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.Xhttp://www.raywenderlich.com/33266/how-to-create-dynamic-textures-with-ccrendertexture-in-cocos2d-2-x
2.(譯)如何使用CCRenderTexture來創建動態紋理 http://www.cnblogs.com/zilongshanren/archive/2011/07/01/2095479.html

非常感謝以上資料,本例子源代碼附加資源下載地址:http://download.csdn.net/detail/akof1314/5664815
如文章存在錯誤之處,歡迎指出,以便改正。

再分享一下我老師大神的人工智能教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!http://www.captainbed.net

如何使用CCRenderTexture創建動態紋理 Cocos2d-x 2 1 4