日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用CCRenderTexture来创建动态纹理

發布時間:2023/12/16 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用CCRenderTexture来创建动态纹理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Ray Wenderlich

這篇文章還可以在這里找到 英語

如何使用CCRenderTexture來創建動態紋理

對于事先制作好的背景圖片,我想你已經非常熟知如何把它們添加進游戲里面了。但是,你有沒有想過動態地創建背景圖片并且修改它們的顏色,梯度(gradients)及實時改變效果呢?
如果你玩過appstore上面的 Tiny Wings by Andreas Illiger , 實際上你已經體驗過上面提及的想法了。

在這個系列的教程中,你將會學習到所有這些技術要點:

  • 如何用代碼實時動態創建紋理
  • 如何使用Gimp創建無縫拼接的紋理
  • 如何混合陰影和光照來增強現實效果
  • 如何創建帶狀紋理
  • 如何制作重復紋理
  • 還有更多!

這個教程是基于 Sergey Tikhonov 在cocos2d論壇里面討論 investigation of Tiny Wings 時所制作的一個樣例工程來制作的。

 Sergey在demo工程上面做了一件非常好的工作,因此,為了不重復發明輪子,我將把他的代碼轉換成一個系列的教程,同時還添加了一些很酷的特性。
這個教程假設你對于cocos2d已經非常熟悉了。如果你對cocos2d還很陌生的話,建議你先看看我博客上面的其它 Cocos2D 教程 on this site.

使用CCRenderTexture來動態創建紋理

OTiny Wings有一個很酷的特性就是,每一天都會改變紋理,就如同下面的游戲截屏一下:

但是,你怎么在cocos2d里面來動態創建紋理呢?好吧,其實有一個很酷的類,叫CCRenderTexture,它允許你來動態創建紋理,并且可以在游戲中重用這些紋理。

使用 CCRenderTexture非常簡單 – 你只需要做以下5步就行了:

  • 創建一個新的CCRenderTexture. 這里,你可以指定將要創建的紋理的寬度和高度。.
  • 調用 CCRenderTexture:begin. 這個方法會啟動OpenGL,并且接下來,任何繪圖的命令都會渲染到CCRenderTexture里面去,而不是畫到屏幕上。.
  • 繪制紋理. 你可以使用原始的OpenGL調用來繪圖,或者你也可以使用cocos2d對象里面已經定義好的visit方法。(這個visit方法就會調用一些opengl命令來繪制cocos2d對象)
  • 調用 CCRenderTexture:end. 這個方法會渲染紋理,并且會關閉渲染至CCRenderTexture的通道。.
  • 從生成的紋理中創建一個sprite.你現在可以用CCRenderTexture的sprite.texture屬性來輕松創建新的精靈了。
  • Note that you can repeat steps 1-3 to continually add/modify the texture over time. For example this might be handy to implement a drawing app. However for this tutorial, we just need to do the drawing once and then we’re done.
    注意你可以重復一到三部來持續的增加或修改紋理。比如這可以很方便的實現一個繪圖應用程序。然后在這篇教程中,我們只需繪畫一次。

    讓我們來實踐一下具體這個過程是如何工作的,我們先來創建一個簡單的單色的紋理。

    但是,首先,你需要一個新的工程!因此,打開Xcode,點擊FileNewNew Project,然后選擇 iOScocos2dcocos2d_box2d template。盡管這個教程并沒有使用到box2d,但是,我們后續的教程會用到,所以,我在這里就提前先建立好。把工程命名為TinySeal,點擊Next,然后選擇磁盤上的一個文件夾并點擊Create。

    然后,打開HelloWorldLayer.h,并用以下內容替換之:

    #import "cocos2d.h"@interface HelloWorldLayer : CCLayer {CCSprite * _background; }+(CCScene *) scene;@end

    這里移除掉了”Hello,World“box2d的代碼,并且往里面添加了一個實例變量來追蹤我們將要動態創建的背景。
    接下來,打開HelloWorldLayer.m,并把它替換成下面的樣子:(只需要把box2d相關代碼全部刪除并且得到一個空的場景就可以了)  

    #import "HelloWorldLayer.h"@implementation HelloWorldLayer+(CCScene *) scene {CCScene *scene = [CCScene node];HelloWorldLayer *layer = [HelloWorldLayer node];[scene addChild: layer];return scene; }-(id) init {if((self=[super init])) { }return self; } @end

    你現在可以編譯并運行一下,你將會看到一個黑色的屏幕。
    接下來,在init方法的上面添加下面的新方法:

    -(CCSprite *)spriteWithColor:(ccColor4F)bgColor textureSize:(float)textureSize {// 1: Create new CCRenderTextureCCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:textureSize height:textureSize];// 2: Call CCRenderTexture:begin[rt beginWithClear:bgColor.r g:bgColor.g b:bgColor.b a:bgColor.a];// 3: Draw into the texture// We'll add this later// 4: Call CCRenderTexture:end[rt end];// 5: Create a new Sprite from the texturereturn [CCSprite spriteWithTexture:rt.sprite.texture];}

    你可以看到,這里動態創建紋理所采用的五個步驟和我們之前討論的一模一樣。
    注意,我們這里不是調用的CCRenderTexture:begin方法,而是調用另外一個較方便的方法beginWithClear:g:b:a:,這個方法可以用給定的顏色來清除紋理的背景,相當于設置畫布的顏色。
    我們還沒有在畫布上畫任何東西–目前為止,我們僅僅是看看純色紋理看起來是什么樣子.
    把上面所討論的一起打包起來,并且添加一些新的方法,然后把init方法改成下面的樣子:

    - (ccColor4F)randomBrightColor {while (true) {float requiredBrightness = 192;ccColor4B randomColor = ccc4(arc4random() % 255,arc4random() % 255, arc4random() % 255, 255);if (randomColor.r > requiredBrightness || randomColor.g > requiredBrightness ||randomColor.b > requiredBrightness) {return ccc4FFromccc4B(randomColor);} }}- (void)genBackground {[_background removeFromParentAndCleanup:YES];ccColor4F bgColor = [self randomBrightColor];_background = [self spriteWithColor:bgColor textureSize:512];CGSize winSize = [CCDirector sharedDirector].winSize;_background.position = ccp(winSize.width/2, winSize.height/2); [self addChild:_background z:-1];}-(id) init {if((self=[super init])) { [self genBackground];self.isTouchEnabled = YES; }return self; }- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {[self genBackground];}

    randomBrightColor方法是一個輔助方法,用來創建一種隨機顏色。注意,這里使用ccc4B(因此,我們能夠在0-255的范圍內指定R/G/B/A值),同時確保至少有一個顏色分量是大于192的,這樣的話,我們就不會得到較暗的顏色。
      然后,genBackground調用我們之前寫的spriteWithColor方法,同時把它加屏幕中央。
      至于init函數,它調用genBackground方法,同時激活touches事件,這樣的話,你就可以通過點擊屏幕來獲得另外的隨機背景了。
      編譯并運行,這樣你每一次點擊屏幕,你都可以得到一張不同的單色背景圖片啦!

    往紋理里面加點噪聲

    在Tiny Wings里面,你可能已經注意到了,那紋理并不只是純粹的單色—而是被一些噪聲所修飾了一下,這樣,看起來的話就會有陰影和光照。

    你可以用代碼來動態地生成了一些噪聲,但是,如果使用一些預先制作的噪聲的話,那樣速度會更快一些–這也是我接下來將要在教程里所使用的方法:

    一種比較簡單的方法,就是使用一個免費的圖片處理程序,叫做 Gimp. 接下來這一部分將向您展示如何動手制作這樣一個噪聲紋理,如果你不樂意自己動手制作那張蒙板,可以用我提供的這張。

    當然,你也可以下載我已經做好了的 噪聲圖片并且把它添加到工程里面去,同時可以跳過接下來的部分了:)

    如果你想繼續,那么先下載Gimp,并且安裝好。
    安裝好之后,打開Gimp,選擇FileNew,然后創建一張新的512*512的圖片。然后選擇 FilterRenderCloudsSolidNoise,并且可以調節一些參數來看看效果,調好之后就點擊Ok吧。
      你可以會得到和下圖差不多的圖片:

    我們將使用這張圖片去乘以紋理的顏色。因此,噪聲圖片中白色的部分,紋理中的顏色就會被顯示出來,而噪聲圖片中黑色的地方,紋理圖片對應部位的顏色就會被變暗。
    上圖中可以看出,黑色部分占太多了,這樣會影響我們的原圖效果。因此,我們要把黑色的部位去掉一些,選擇 ColorsLevels,然后”Output Levels“的最左邊的滑動條,把它拖到右邊去一點,這樣我們的噪聲圖片就會明亮一些啦。

    當你做完之后就點擊ok,然后,還有最后一步,這個噪聲圖片紋理要制作成無縫拼接的,因此,這樣我們才能制作無限滾動的背景。
    Gimp使用得這個功能變得非常簡單。只需要點擊FiltersMapMake Seamless就行了!
    使用FileSave As來保存你的圖片為Noise.png。然后使用Finder打開并拖到TinySeals工程中去。確保“Copy items into destination group’s folder”被復選中,然后點擊Finish。
    恭喜你,現在你有一張噪聲紋理了,位下來,你可以使用它來創建更加真實的紋理效果了!
    現在,你知道怎么動態創建紋理了,并且你可以使用Gimp的濾鏡來制作一些不同的效果!

    往紋理中添加噪聲

    現在,你有一張帶噪聲的圖片了,接下來,讓我們把它添加到CCRenderTexture里去吧。

    在spriteWithColor:textureSize方法里面,我們在注釋中所示的第3步,添加下列代碼:

    CCSprite *noise = [CCSprite spriteWithFile:@"Noise.png"]; [noise setBlendFunc:(ccBlendFunc){GL_DST_COLOR, GL_ZERO}]; noise.position = ccp(textureSize/2, textureSize/2); [noise visit];

    這里使用噪聲紋理來創建一個精靈,然后把它放置在render texture的中心位置,并且調用visit方法。這個visit方法就是一系列的opengl調用,且這些調用是用來畫這個紋理的。
      這里只有一個詭異的地方—-就是”setBlendFunc“方法。這玩意兒倒底干嗎的呢?
      好吧,這第一個常量(GL_DST_COLOR)指定如何乘以輸入圖像/源圖像的顏色數據(也就是指噪聲圖像),第二個參數(GL_ZERO)指定如何乘以已經存在的圖像/目標圖像顏色數據(實際上就是之前的單色紋理)。
      因此,闡述一下:

    • 已經存在的顏色乘以GL_ZERO,那意味著已經存在的顏色將會被清除掉。
    • 噪聲紋理顏色乘以GL_DST_COLOR。而GL_DST_COLOR意味著已經存在的顏色,因此噪聲顏色會乘以已經存在的顏色。所以,如果噪聲中白色越多,那么對應位置的源圖像位置顯示的顏色也就越亮。如果噪聲顏色中越多黑色,那么源圖像的顏色就會變得越暗。
    • 上面提及的兩種顏色疊加到一起,但是,由于指點一個參數是0,所以,實際上還是看第二個參數的影響。

    順便提一下,我發現這些混合常量非常容易使人迷惑,但是,我們很幸運,這里有 一款免費的在線工具 可以用來動態地配置每種常量的效果。

     不管怎么說,這就是你所需要的全部東西啦!編譯并運行你的代碼,現在,你可以看到紋理的一些陰影效果了。注意,這個在模擬器上看起來效果通常不是很好,你可能需要在實際的設備上嘗試一下會比較好。

    往紋理中添加梯度

    為了使紋理看起來更好看,讓我們從上至下添加一些梯度吧,具體的效果是紋理從上到下,會變得越來越暗。
      我們也可以使用Gimp來修改我們的噪聲圖片,但是,這一次,我們可以在代碼中來完成,這樣會變得更加動態,并且可以很容易地修改。
      基本的想法就是,我們在紋理的頂部畫一個黑色的矩形,但是,它將會是上部完全透明的,并且底部完全不透明。這樣會使得上面比較亮,而慢慢得往下變暗。
      為了實現這個目標,我們需要使用一些opengl命令。如果你對opengl不熟悉的話,也不用擔心—我會幫你寫好這些代碼,并且在高層向你解釋具體是如何工作的。在教程的結尾,你會得到一些參考資料,通過它們你可以獲得更多的信息。
      在spriteWithColor:textureSize里面,在創建噪聲精靈之前,添加下列代碼:

    glDisable(GL_TEXTURE_2D); glDisableClientState(GL_TEXTURE_COORD_ARRAY);float gradientAlpha = 0.7; CGPoint vertices[4]; ccColor4F colors[4]; int nVertices = 0;vertices[nVertices] = CGPointMake(0, 0); colors[nVertices++] = (ccColor4F){0, 0, 0, 0 }; vertices[nVertices] = CGPointMake(textureSize, 0); colors[nVertices++] = (ccColor4F){0, 0, 0, 0}; vertices[nVertices] = CGPointMake(0, textureSize); colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha}; vertices[nVertices] = CGPointMake(textureSize, textureSize); colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};glVertexPointer(2, GL_FLOAT, 0, vertices); glColorPointer(4, GL_FLOAT, 0, colors); glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D);

     第一件事情就是禁用GL_TEXTURE_2D 和 GL_TEXTURE_COORD_ARRA兩個opengl狀態,因為,我們接下來只需要繪制顏色—這與紋理無關。
      一個比較奇怪的事就是,這里繪制紋理的時候,左上角是坐標原點(0,0)—而不是我們在cocos2d里面使用的,0,0點在左下角。
      因此,接下來,我們定義紋理的四個頂點坐標左上、右上、左下、右下–并且同時指定每一個點的顏色值。
      你可能會奇怪,為什么頂點會以這樣的順序被定義出來。但是因為,我們將按下圖的方式來畫兩個三角形,當然這兩個三角形拼成了一個矩形:

     我們的將使用GL_TRIANGLE_STRIP來畫這些三角形,那意味著,頂點數組中的前三個頂點組成第一個三角形,而第二個三角形就是前面2,3號頂點,再加上下一個新的頂點組成第二個三角形。
      因此,第一個三角形是由頂點v0,v1,v2組成,而第二個則由v1,v2,v3組成。
      在定義完頂點數組和顏色數組以后,我們把它們傳給glVertexPointer opengl調用(每一個頂點用2個浮點數標識)和glColorPointer調用(每個顏色值指定4個浮點數),然后調用glDrawArrays,并且指定 GL_TRIANGLE_STRIP為參數來繪制三角形帶。
      最后,我們重新激活GL_TEXTURE_COORD_ARRAY 和 GL_TEXTURE_2D兩個狀態,這樣做的話是保持和之前調用的對稱,并且不會破壞opengl狀態機。
      編譯并運行代碼,然后你會看到一個整齊的帶有梯度的紋理啦!

    創建帶狀紋理

     在我們開始寫代碼來繪制帶狀紋理之前,讓我們先花上一分鐘時間來想一想我們的策略。
      我們將從指定紋理為一種顏色開始(如下圖的藍色部分),然后,我們將以對角線的方向畫一些三角帶子(如下圖綠色部分),如圖所示:

    注意,因為帶子是呈對角線的,我們實際上將要在紋理的邊界之外就繪制這條帶子。(如上圖v0,v1,v2所示)
      還有一點需要注意,為了獲得一個比較好的45度的效果,我們將把V0偏移原點texture size的距離。
      好了,讓我們來看看實際的代碼是什么樣子的吧。在init的方法上面添加一個新的方法,如下所示:

    -(CCSprite *)stripedSpriteWithColor1:(ccColor4F)c1 color2:(ccColor4F)c2 textureSize:(float)textureSize stripes:(int)nStripes {// 1: Create new CCRenderTextureCCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:textureSize height:textureSize];// 2: Call CCRenderTexture:begin[rt beginWithClear:c1.r g:c1.g b:c1.b a:c1.a];// 3: Draw into the texture // Layer 1: StripesglDisable(GL_TEXTURE_2D);glDisableClientState(GL_TEXTURE_COORD_ARRAY);glDisableClientState(GL_COLOR_ARRAY);CGPoint vertices[nStripes*6];int nVertices = 0;float x1 = -textureSize;float x2;float y1 = textureSize;float y2 = 0;float dx = textureSize / nStripes * 2;float stripeWidth = dx/2;for (int i=0; i<nStripes; i++) {x2 = x1 + textureSize;vertices[nVertices++] = CGPointMake(x1, y1);vertices[nVertices++] = CGPointMake(x1+stripeWidth, y1);vertices[nVertices++] = CGPointMake(x2, y2);vertices[nVertices++] = vertices[nVertices-2];vertices[nVertices++] = vertices[nVertices-2];vertices[nVertices++] = CGPointMake(x2+stripeWidth, y2);x1 += dx;}glColor4f(c2.r, c2.g, c2.b, c2.a);glVertexPointer(2, GL_FLOAT, 0, vertices);glDrawArrays(GL_TRIANGLES, 0, (GLsizei)nVertices);glEnableClientState(GL_COLOR_ARRAY);glEnableClientState(GL_TEXTURE_COORD_ARRAY);glEnable(GL_TEXTURE_2D);// Layer 2: Noise CCSprite *noise = [CCSprite spriteWithFile:@"Noise.png"];[noise setBlendFunc:(ccBlendFunc){GL_DST_COLOR, GL_ZERO}];noise.position = ccp(textureSize/2, textureSize/2);[noise visit];// 4: Call CCRenderTexture:end[rt end];// 5: Create a new Sprite from the texturereturn [CCSprite spriteWithTexture:rt.sprite.texture];}

    這個方法的大部分內容都是重溫如何創建一個CCRenderTexture,只有創建帶狀層的代碼是新加的。所以讓我們來討論一下。
      它首先創建了一個頂點數組–對于每一條帶子,我們需要6個頂點–3個頂點*2個三角形。我們這里不能使用三角形帶,因為,帶子不是鄰接在一起的。
      第一個頂點的坐標是 (-textureSize, textureSize),上面的圖示中也可以看出來,接下來一個頂點就是 (-textureSize+stripWidth, textureSize).第三個頂點是(0,0),而第四個頂點是(stripeWidth, textureSize).這些就是我們畫一個帶子所需要的全部頂點,而且每一次我們往前遞增兩個帶子的長度,并且繼續,直至所有的帶子都制作完成。
      現在,你可以把genBackground修改成下面的樣子了:

    - (void)genBackground {[_background removeFromParentAndCleanup:YES];ccColor4F bgColor = [self randomBrightColor];ccColor4F color2 = [self randomBrightColor];//_background = [self spriteWithColor:bgColor textureSize:512];int nStripes = ((arc4random() % 4) + 1) * 2;_background = [self stripedSpriteWithColor1:bgColor color2:color2 textureSize:512 stripes:nStripes];self.scale = 0.5;CGSize winSize = [CCDirector sharedDirector].winSize;_background.position = ccp(winSize.width/2, winSize.height/2); [self addChild:_background];}

    這里調用新的方法,同時修改層的scale為0.5,這樣的話就能看到512*512的紋理的全貌。
      編譯并運行,現在,你每點擊一下屏幕都可以得到一張不同的帶狀的紋理了!

    重復背景

    對于帶狀背景和梯度背景,我們都希望它比實際定義的512*512的區域要大。
      一種比較簡單的方式就是我們創建多個CCSprites,然后把它們鏈接在一起。但是,那看起來太瘋狂了,因為,我們有更加簡單的方法—我們可以設置紋理并讓它們重復。
      打開HelloWorldLayer.m,并作如下修改:

    // Add to genBackground, right BEFORE the call to addChild ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT}; [_background.texture setTexParameters:&tp];// Add to bottom of init [self scheduleUpdate];// Add after init - (void)update:(ccTime)dt {float PIXELS_PER_SECOND = 100;static float offset = 0;offset += PIXELS_PER_SECOND * dt;CGSize textureSize = _background.textureRect.size;[_background setTextureRect:CGRectMake(offset, 0, textureSize.width, textureSize.height)];}

    這里最重要的就是紋理參數:

    • GL_LINEAR 是說“當顯示紋理時,顯示的大小大于或者小于原紋理的尺寸時,使用鄰近像素點來插值補點”
    • GL_REPEAT是說“如果你想顯示紋理邊界之外的像素點時,把它旁邊的紋理像素點平鋪過去”

     同時,調用一個update定時器,更新紋理的x軸坐標,使前不斷地前移。這樣會使得紋理看起來隨著時間不斷地往前移動。
      編譯并運行代碼,這時你可以看到一個連續滾動的無限重復的背景紋理了!你也可以用帶有梯度的紋理來嘗試一下,一樣可以跑起來。

    免費的光照

    如果你看一看Tiny Wings的山丘紋理實現,你會發現山的頂部有一些光照,同時從上到下還有一些梯度,并且這一切看起來非常棒!因此, 讓我們修改我們前面的帶狀紋理。
      在stripedSpriteWithColor1方法中,緊接著glDrawArrays之后,添加下面代碼:

    // layer 2: gradient glEnableClientState(GL_COLOR_ARRAY);float gradientAlpha = 0.7; ccColor4F colors[4]; nVertices = 0;vertices[nVertices] = CGPointMake(0, 0); colors[nVertices++] = (ccColor4F){0, 0, 0, 0 }; vertices[nVertices] = CGPointMake(textureSize, 0); colors[nVertices++] = (ccColor4F){0, 0, 0, 0}; vertices[nVertices] = CGPointMake(0, textureSize); colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha}; vertices[nVertices] = CGPointMake(textureSize, textureSize); colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};glVertexPointer(2, GL_FLOAT, 0, vertices); glColorPointer(4, GL_FLOAT, 0, colors); glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);// layer 3: top highlight float borderWidth = textureSize/16; float borderAlpha = 0.3f; nVertices = 0;vertices[nVertices] = CGPointMake(0, 0); colors[nVertices++] = (ccColor4F){1, 1, 1, borderAlpha}; vertices[nVertices] = CGPointMake(textureSize, 0); colors[nVertices++] = (ccColor4F){1, 1, 1, borderAlpha};vertices[nVertices] = CGPointMake(0, borderWidth); colors[nVertices++] = (ccColor4F){0, 0, 0, 0}; vertices[nVertices] = CGPointMake(textureSize, borderWidth); colors[nVertices++] = (ccColor4F){0, 0, 0, 0};glVertexPointer(2, GL_FLOAT, 0, vertices); glColorPointer(4, GL_FLOAT, 0, colors); glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);

    這段代碼應該又是重新溫習一遍啦,但是,第一部分和我們之前創建的梯度背景差不多,而第二部分創建光照的時候,我們從上至下添加,使之有太陽曬著的感覺。
      編譯并運行代碼,現在,你應該發現我們的帶狀紋理更加逼真了,有梯度變化又有光照效果!

    總結

    以上是生活随笔為你收集整理的使用CCRenderTexture来创建动态纹理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。