青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 132  文章 - 51  trackbacks - 0
<2012年8月>
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用鏈接

留言簿(7)

隨筆分類

隨筆檔案

文章分類

文章檔案

cocos2d-x

OGRE

OPenGL

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

原文地址:http://archive.cnblogs.com/a/2429576/

今天我們來實(shí)現(xiàn)一個(gè)功能,我們來給精靈加一個(gè)遮罩,不過略微有點(diǎn)不同的是,我們的精靈的遮罩不是固定的,可以隨著手指的移動(dòng),實(shí)現(xiàn)動(dòng)態(tài)的遮擋精靈的不同部分。像上一篇教程一樣,我們還是簡(jiǎn)陋的實(shí)現(xiàn)我們的主要功能,只給出解決問題的主要思路和方法,更豐富出彩的功能,需要你自己開動(dòng)腦筋去實(shí)現(xiàn)。

      我們這篇教程所涉及的知識(shí),基本上都來自子龍山人譯:的怎么用cocos2d 2.0實(shí)現(xiàn)精靈的遮罩raywenderlich博客團(tuán)隊(duì)成員的另一篇文章,我們所做的功能,只不過是調(diào)整一些方法而已。再次感謝子龍山人,幫我們翻譯這么好的文章,同樣也感謝ray wenderlich的團(tuán)隊(duì),寫這么好的文章分享給我們,兩位都是我們ios程序員的福音呀哈哈!!

介紹

      首先,我們這篇文章需要用到cocos2d 2.x(就是我們所說的cocos2d 2.0的一個(gè)更新版),這是因?yàn)閏ocos2d 2.x是不同于cocos2d 1.0的,1.0版的cocos2d用的是openGL-ES1.0,而2.0版本的用的則是openGL-ES2.0,而我們這篇文章實(shí)現(xiàn)的根基就是openGL-ES2.0,在openGL-ES2.0是可以使用自己的著色器的(shader),我們的核心內(nèi)容就是和shader打交道。如果你還沒有安裝cocos2d 2.x的話,你可以到cocos2d-iphone的官方網(wǎng)站去下載最新的版本,然后解壓下載下來的壓縮包,然后打開mac里的finder(相當(dāng)于windows的我的電腦),按蘋果的徽標(biāo)鍵+shift+U來打開常用工具目錄,然后打開我們的終端,在終端我們敲入字符cd,空格,然后把我們解壓好的文件夾拖到終端上來,它會(huì)自動(dòng)為我們填上這個(gè)文件夾的具體目錄,回車,這樣我們就已經(jīng)進(jìn)入了我們的cocos2d 2.x的安裝源的根目錄,再敲入以下命令:./install-templates.sh -f -u,回車,很快我們的cocos2d 2.x就裝好了,這時(shí)你打開xcode,會(huì)發(fā)現(xiàn)多了一個(gè)cocos2d 2.x的模板選項(xiàng),這就是我們想要的。(其實(shí)對(duì)于安裝來說,個(gè)人更推薦用git的方式,具體方式參考怎么用cocos2d 2.0實(shí)現(xiàn)精靈的遮罩這篇文章,我就不再贅述了)。

開始

     好了,如果你已經(jīng)安裝好了cocos2d 2.0的話,我們就可以開始了。打開xcode,選擇new project,選擇我們新添加的cocos2d 2.x模板,在右側(cè)的面板里選擇cocos2d ios

點(diǎn)擊下一步,把我們的項(xiàng)目命名為:MoveMask,然后選擇一個(gè)地方保存你的項(xiàng)目,最后點(diǎn)創(chuàng)建。

      接下來,我們要使用ARC(自動(dòng)引用記數(shù))功能,使用ARC可以讓我們不用過多的操心內(nèi)存管理的問題,并且基本上不會(huì)出現(xiàn)內(nèi)存泄漏等我們程序員最頭疼的內(nèi)存問題。雖然cocos2d是不支持ARC功能的,但是實(shí)際上你是可以為你的cocos2d項(xiàng)目使用這個(gè)功能的:

     在xcode的菜單欄選擇Edit\Refactor\Convert to Objective-C ARC,在打開的對(duì)話框中,確保展開MoveMask.app左邊的小三角

如上圖,把其它文件前面的勾都取消掉,只留下main.m,Appdelegate.m和HelloWorldLayer.m,點(diǎn)擊check,然后在后面的幾個(gè)對(duì)話框中都選擇確定,這其中你可能會(huì)被問到是否為你的代碼創(chuàng)建一個(gè)快照,你選創(chuàng)建和不創(chuàng)建都行,不過一般情況下還是創(chuàng)建一個(gè)比較好,這樣當(dāng)你的代碼在遇到不可逆轉(zhuǎn)的錯(cuò)誤的時(shí)候可以恢復(fù)。好了,現(xiàn)在你就可以在你的cocos2d程序里用ARC了,是不是很簡(jiǎn)單。

      現(xiàn)在編譯運(yùn)行我們的項(xiàng)目,你會(huì)發(fā)現(xiàn)在一個(gè)錯(cuò)誤:(我后來又試了幾次,它又不出錯(cuò)了,呵呵,總之如果有你在試的時(shí)候有同樣的錯(cuò)誤的話,可以和我用同樣的方式解決它,如果沒出錯(cuò)更好,直接進(jìn)行后面的操作)

 

 

      錯(cuò)誤提示告訴我們,我們的window這個(gè)屬性是一個(gè)需要被保持的對(duì)象,可是我們卻可能會(huì)釋放它,換句話說,我們的ARC系統(tǒng)沒有為我們做好這個(gè)window屬性的管理工作,它應(yīng)該是一個(gè)強(qiáng)引用的,可是我們卻沒有給它分配strong屬性。(我并不知道為什么造成了這個(gè)錯(cuò)誤,如果你知道,請(qǐng)留言告訴我,謝謝!),不過管它呢,我們來修正這個(gè)錯(cuò)誤,在appDelegate.h文件中,為我們的windown屬性加上strong聲明。好了,再次編譯我們的項(xiàng)目,如果一切正常的話,你會(huì)看到下面的畫面:

進(jìn)入正題

一切都準(zhǔn)備就緒了,我們來實(shí)現(xiàn)我們的功能吧。

     哦,對(duì)不起,在我們還沒有寫我們的代碼之前,我需要確定一件事,你知道shader是什么嗎?shader(著色器)是一個(gè)用來執(zhí)行渲染效果的小程序。它是由圖形處理單元執(zhí)行的,在移動(dòng)設(shè)備上,有兩種著色器:

    1.vertex shader,這個(gè)叫頂點(diǎn)著色器,當(dāng)每一個(gè)頂點(diǎn)被渲染的時(shí)候,都會(huì)調(diào)用這個(gè)著色器,所以當(dāng)我們渲染一個(gè)精靈的時(shí)候,由于我們的精靈都是四個(gè)頂點(diǎn)的,所以我們的頂點(diǎn)著色器會(huì)被調(diào)用四次,它被用來計(jì)算我們的精靈每一個(gè)頂點(diǎn)的顏色和一些其它屬性。

    2.fragment shader,我們也可以叫它片源著色器(也可以說是像素著色器),這個(gè)著色器會(huì)在每個(gè)像素被渲染的時(shí)候被調(diào)用,也就是說,如果我們?cè)趇phone上顯示一個(gè)全屏的圖片的話,這個(gè)著色器會(huì)被執(zhí)行480*320次。

   這兩個(gè)著色器不能單獨(dú)使用,它們必須成對(duì)的使用,一對(duì)兒頂點(diǎn)和片源著色器叫做一個(gè)著色程序,它們通常是按下面的方式工作的:

    頂點(diǎn)著色器首先確定每一個(gè)顯示到屏幕上的頂點(diǎn)的屬性,然后這些頂點(diǎn)組成的區(qū)域被化分成一系列的像素,這些像素的每一個(gè)都會(huì)調(diào)用一次片源著色器,最后這些經(jīng)過處理的像素顯示在屏幕上。

   讓我們先看一對(duì)簡(jiǎn)單的著色程序吧,首先是頂點(diǎn)著色器:

//1
attribute vec4 a_position;
attribute vec2 a_texCoord;
 
//2
uniform mat4 u_MVPMatrix;
 
//3
#ifdef GL_ES
varying mediump vec2 v_texCoord;
#else
varying vec2 v_texCoord;
#endif
 
//4
void main()
{
//5
  gl_Position = u_MVPMatrix * a_position;
//6
  v_texCoord = a_texCoord;
}

 

  每一個(gè)著色程序都必需接受輸入數(shù)據(jù),并產(chǎn)生輸出數(shù)據(jù)。在這個(gè)頂點(diǎn)著色器里,我們有三個(gè)輸入數(shù)據(jù),一個(gè)是保存每個(gè)頂點(diǎn)的位置,一個(gè)是保存每個(gè)頂點(diǎn)對(duì)應(yīng)的紋理坐標(biāo),最后還有一個(gè)是用于整個(gè)精靈的位置、縮放、旋轉(zhuǎn)的矩陣,這些數(shù)據(jù)都會(huì)在這個(gè)著色器被調(diào)用之前被傳進(jìn)來。我們還有兩個(gè)輸出數(shù)據(jù)在這個(gè)著色器程序里,一個(gè)決定了每個(gè)頂點(diǎn)的最終屏幕坐標(biāo),一個(gè)決定了每個(gè)頂點(diǎn)的最終紋理坐標(biāo)。下面我們會(huì)講到,這些輸出數(shù)據(jù)是被片源著色器怎么使用的。

     現(xiàn)在我們先來一點(diǎn)點(diǎn)地認(rèn)真看一下這個(gè)頂點(diǎn)著色器:

     注釋1,我們定義這個(gè)著色器的輸入數(shù)據(jù),我們定義了兩個(gè)數(shù)據(jù)結(jié)構(gòu)變量。一個(gè)vec4類型,表明這個(gè)結(jié)構(gòu)有4個(gè)float型數(shù)據(jù)組成,它用來存儲(chǔ)頂點(diǎn)的位置。(你可能會(huì)奇怪,我們的2維世界的坐標(biāo)怎么要用4個(gè)float來存呢?兩個(gè)不就夠了嗎?這個(gè)牽扯的數(shù)學(xué)問題就比較復(fù)雜了,簡(jiǎn)單地說,是因?yàn)槲覀內(nèi)绻獙?duì)我們的精靈進(jìn)行縮放、旋轉(zhuǎn)、移動(dòng)等操作的話,從圖形學(xué)上來說的話,是要做矩陣的乘法的,保存我們轉(zhuǎn)換信息的這些矩陣都是4*4的,所以我們的位置也要是4位的才能進(jìn)行操作,具體4元數(shù)了什么的,那都太高級(jí)了,我也不清楚,如果你感興趣的話,可以自己查這方面的資料)另一個(gè)是vec2類型,表明它有兩個(gè)float型數(shù)據(jù)組成,它用來存儲(chǔ)我們的紋理坐標(biāo)。很重要的一點(diǎn)是,這個(gè)attribute關(guān)鍵字,它告訴編譯器,被這個(gè)關(guān)鍵字修飾的變量是一個(gè)輸入變量,這個(gè)變量的值要從每個(gè)頂點(diǎn)的數(shù)據(jù)結(jié)構(gòu)中獲得。(具體獲得的地方在后面的代碼中用到時(shí)我們會(huì)指出)

     注釋2,當(dāng)你需要一些額外的數(shù)據(jù)傳入到著色器的話,你需要把你的變量用uniform關(guān)鍵字聲明。(注意uniform和attribute的區(qū)別,雖然都是輸入數(shù)據(jù),但是它們不同,attribute修飾的變量的值,是通過頂點(diǎn)數(shù)據(jù)結(jié)構(gòu)傳入的,而uniform修飾的變量的值,是我們?cè)诖a里傳入的)這里我們聲明了一個(gè)mat4類型,這是一個(gè)4*4的矩陣,如果你線性代數(shù)一竅不通的話,你只要記住這個(gè)矩陣是我們用來移動(dòng)、縮放和旋轉(zhuǎn)的就行了。(說到線性代數(shù)了我就不得不說下我的大學(xué)里的高數(shù)課了,在講線性代數(shù)的時(shí)候,我們的教育只告訴我們這些知識(shí)的用法,就是只告訴你怎么進(jìn)行運(yùn)算,完全不告訴你它能干什么,所以學(xué)的時(shí)候,覺得這是什么玩意兒呀,有什么用呀,根本就不知道它能干什么,我怎么可能會(huì)去好好學(xué)它?!結(jié)果我的線性代數(shù)在考試的時(shí)候差點(diǎn)就掛科了,而且,我現(xiàn)在完全不記得它的用法了……。如果我在學(xué)的時(shí)候就能知道它是干什么的,結(jié)合它的實(shí)際用途去學(xué)習(xí)理解,我相信我會(huì)學(xué)好它的,不過貌似這是我們的教育的通病……)

      注釋3,這頂點(diǎn)著色器需要輸出一些數(shù)據(jù)到片源著色器,這樣它們才能協(xié)同操作,要實(shí)現(xiàn)輸出數(shù)據(jù)到片源著色器,你需要用varying關(guān)鍵字來聲明你的變量,這個(gè)最酷的事情是,varying變量的值是插值的,就是說如果,頂點(diǎn)A的坐標(biāo)是定義成varying的,并設(shè)置值為0,頂點(diǎn)B坐標(biāo)也是varying的,并被設(shè)置值為1的話,在片源著色器里,會(huì)自動(dòng)把頂點(diǎn)A和頂點(diǎn)B中間的那個(gè)像素的坐標(biāo)設(shè)置為0.5,每一個(gè)片源都是從頂點(diǎn)數(shù)據(jù)插值計(jì)算得到的。我們還看到在條件編譯里有一個(gè)mediump 前綴,這個(gè)是用來決定計(jì)算的精度的,可用的前綴還有highp和lowp.它們分別代表高、中、低三個(gè)級(jí)別的精度,精度越高,數(shù)據(jù)越精確,不過計(jì)算起來也會(huì)更慢。

     注釋4,每個(gè)著色器都要有一個(gè)main函數(shù)

     注釋5,gl_Position是頂點(diǎn)著色器的內(nèi)置變量,決定每一個(gè)頂點(diǎn)的最終位置,這個(gè)著色器里,是把這個(gè)輸入的頂點(diǎn)坐標(biāo)a_psoition乘以這個(gè)輸入的變形矩陣u_MVPMatrix(這個(gè)矩陣是由cocos2d框架自動(dòng)傳進(jìn)來的,我們不用操心這個(gè),它決定了精靈的移動(dòng)、縮放和旋轉(zhuǎn)) 。

     注釋6,我們把輸入的紋理坐標(biāo)a_texCoord不做任何改變賦值給varying變量V_texCoord,以便傳遞給片源著色器。

下面我們接著看和這個(gè)頂點(diǎn)著色器配合工作的片源著色器的代碼:

//1
#ifdef GL_ES
precision mediump float;
#endif
 
//2
varying vec2 v_texCoord;
//3
uniform sampler2D u_texture;
 
void main()
{
//4
  gl_FragColor =  texture2D(u_texture, v_texCoord);
}

  注釋1,我們強(qiáng)制設(shè)置在這個(gè)片源著色器里對(duì)float數(shù)的計(jì)算精度為中等精度。

      注釋2,任何在頂點(diǎn)著色器里的輸出變量在片源著色器里都要被定義為輸入變量,所以在這里再次定義這個(gè)紋理坐標(biāo)變量

      注釋3,片源著色器也可以有自己的uniform變量,這種變量是從代碼里傳過來的常量,cocos2d會(huì)傳入紋理到一個(gè)uniform變量,所以這里定義了一個(gè)用uniform聲明的sampler2D類型的變量,它只是一個(gè)正常的紋理。

      注釋4,像頂點(diǎn)著色器的gl_Position一樣,這個(gè)gl_FragColor也是一個(gè)內(nèi)置變量,它決定了每一個(gè)像素的最終的顏色。這里只是通過頂點(diǎn)著色器得到的紋理坐標(biāo),獲得紋理的相應(yīng)坐標(biāo)的正常顏色。

      如果你還想了解它們是怎么一起工作的話,這個(gè)著色器在CCGrid.m里被調(diào)用,你可以打開這個(gè)文件去看看,在初始化方法里有下面代碼:

self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTexture];

  這句代碼從著色器緩存中加載這個(gè)著色程序。如果你好奇,你可以看看CCShaderCache文件,看看這個(gè)著色器是怎么編譯和存儲(chǔ)的。然后再blit方法里,它傳遞變量到shader,并運(yùn)行這個(gè)著色程序。

-(void)blit
{
   //1 
  NSInteger n = gridSize_.x * gridSize_.y;
 
    //2 Enable the vertex shader's "input variables" (attributes)
    ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords );
 
    // 3 Tell Cocos2D to use the shader we loaded earlier
    [shaderProgram_ use];
 
    //4 Tell Cocos2D to pass the CCNode's position/scale/rotation matrix to the shader
    [shaderProgram_ setUniformForModelViewProjectionMatrix];
 
    //5 Pass vertex positions
    glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, 0, vertices);
 
    //6 Pass texture coordinates
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, texCoordinates);
 
    //7 Draw the geometry to the screen (this actually runs the vertex and fragment shaders at this point)
    glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices);
 
    //8 Just stat keeping here
    CC_INCREMENT_GL_DRAWS(1);
}

  注釋1,這個(gè)是特定于這個(gè)CCGrid.m類的,計(jì)算網(wǎng)格數(shù)的,我們不討論它

      注釋2,這個(gè)是啟用頂點(diǎn)著色器的輸入變量的,這時(shí)我們啟用了兩個(gè)kCCVertexAttribFlag_Position和kCCVertexAttribFlag_TexCoords,實(shí)際上就是我們的頂點(diǎn)著色器里的a_Position和a_texCoord。這樣我們能才使用這兩個(gè)輸入變量。

      注釋3,是告訴cocos2d我們要使用我們剛才在init方法里面加載的著色程序。

      注釋4,告訴cocos2d傳入我們的CCNode的移動(dòng)、縮放、旋轉(zhuǎn)矩陣給shader,實(shí)際上就是為我們的頂點(diǎn)著色器里的輸入變量u_MVPMatrix賦值。

      注釋5,傳遞頂點(diǎn)數(shù)據(jù)結(jié)構(gòu)的位置數(shù)據(jù)給頂點(diǎn)著色器,實(shí)際上就是為頂點(diǎn)著色器里的輸入變量a_Posigion賦值。

      注釋6,和注釋5差不多,它是給頂點(diǎn)著色器里的a_texCoord賦值的

      注釋7,它是真正的openGL_ES繪制方法,把我們的精靈進(jìn)行繪制。這個(gè)方法的第一個(gè)參數(shù)是GL_TRIANGLES表明,我們要繪制的圖元是三角形,第二個(gè)參數(shù)是要繪制的圖元的數(shù)量乘以每個(gè)圖元的頂點(diǎn)數(shù),這里是6表明,我們要繪制2個(gè)三角形,每個(gè)三角形有3個(gè)頂點(diǎn),很顯然兩個(gè)三角形可以合成一個(gè)矩形,這個(gè)矩形就是我們的精靈,第三個(gè)參數(shù)是頂點(diǎn)索引數(shù)據(jù)的類型,最后一個(gè)參數(shù)就是指向索引存儲(chǔ)位置的指針。

      注釋8,這個(gè)我們不討論了。(因?yàn)槲乙膊恢浪歉墒裁吹模孟袷潜3诌f增式的繪圖的,我個(gè)人猜測(cè)有可能跟精靈的zOrder屬性有關(guān),我沒有看這它的代碼,對(duì)它的看法都是瞎猜的,不過這個(gè)真的不影響我們使用,所以我才沒看它的代碼的,只要保持這句話是這個(gè)樣子就行了哈哈)

      還沒有開始真正的寫我們的代碼呢,就先灌輸了這么一大堆東西給你,真的不好意思,不過這個(gè)對(duì)于理解我們的這個(gè)項(xiàng)目來說是真的很有必要的,所以我才對(duì)這些知識(shí)進(jìn)行了解釋,其實(shí)這些知識(shí)點(diǎn)的內(nèi)容基本上就是直接譯自raywenderlich博客團(tuán)隊(duì)的這篇:How To Create Cool Effects with Custom Shaders in OpenGL ES 2.0 and Cocos2D 2.X了,我本來想以自己的觀點(diǎn)表達(dá)這些知識(shí)內(nèi)容的,可是,原文表達(dá)的實(shí)在是太好了,所以不自覺的基本上就變成這個(gè)原文的譯文了呵呵

好了,讓我們干自己的活兒吧

       現(xiàn)在你已經(jīng)具備了編寫sahder的能力,那我可以說你擁有了你對(duì)你自己的精靈的完全的控制權(quán),你想呀,你的精靈的每一個(gè)像素你都能控制,還有什么是你不能做的效果呢?(當(dāng)然說著簡(jiǎn)單,真正好的效果是需要扎實(shí)的圖形學(xué)知識(shí)的)

      下面我們創(chuàng)建一個(gè)ccsprite類的子類,命名為MaskedSprite.       

       選擇File\New\New File,然后選擇iOS\Cocoa Touch\Objective-C class,再點(diǎn)擊Next,然后輸入CCSprite為subclass,接著,點(diǎn)Next,把新的文件命名為MaskedSprite.m,最后點(diǎn)擊Save。

     然后把MaskedSprite.h替換成下面的內(nèi)容:

#import "cocos2d.h"

@interface MaskedSprite : CCSprite {
    CCTexture2D * _maskTexture;
    GLuint _maskLocation;
    
    CGPoint offsetWithPosition;
    GLuint _offsetLocation;
}

-(void) postOffsetToShader:(CGPoint)aOffset;

@end

 

  這里我們定義了一個(gè)變量_maskTexture來跟蹤我們的遮罩紋理,一個(gè)變量_maskLocation來追蹤遮罩紋理的uniform位置(稍后會(huì)在我們的著色器中看到),一個(gè)變量offsetWithPosition來存儲(chǔ)我們的坐標(biāo)的偏差,最后一個(gè)變量_offsetLocation來跟蹤v_offset(稍后會(huì)在我們的著色器中看到)的uniform位置 ,然后我們聲明了一個(gè)方法來給我們的offsetWithPosition賦值。

下面我們打開MaskedSprite.m,并替換它的內(nèi)容如下:

#import "MaskedSprite.h"

@implementation MaskedSprite

- (id)initWithFile:(NSString *)file 
{
    self = [super initWithFile:file];
    if (self) {
        
        // 1
        _maskTexture = [[[CCTextureCache sharedTextureCache] addImage:@"yuan.png"] retain];
        [_maskTexture setAliasTexParameters];
        // 2               
        const GLchar * fragmentSource = (GLchar*) [[NSString stringWithContentsOfFile:[CCFileUtils fullPathFromRelativePath:@"Mask.fsh"] encoding:NSUTF8StringEncoding error:nil] UTF8String];
         self.shaderProgram = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionTextureA8Color_vert
                                                          fragmentShaderByteArray:fragmentSource];        
        
        // 3
        [shaderProgram_ addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position];
        [shaderProgram_ addAttribute:kCCAttributeNameColor index:kCCVertexAttrib_Color];
        [shaderProgram_ addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords];        
        
        // 4
        [shaderProgram_ link];        
       
        // 5
        [shaderProgram_ updateUniforms];        
        
        // 6
           _maskLocation = glGetUniformLocation( shaderProgram_->program_, "u_mask");        
           _offsetLocation = glGetUniformLocation(shaderProgram_->program_, "v_offset");
        //7
        offsetWithPosition = ccp(-1000,-1000);
    }
    
    return self;
}

@end

 

  我們重寫這個(gè)initWithFile:方法

      注釋1,我們加載我們的遮罩紋理(這里我們的遮罩紋理是一個(gè)480*320的圖片,但是它只有中間一個(gè)小圓是有顏色的,其它地方都是透明的),并且取消這個(gè)紋理的線性插值,我們要用它的真實(shí)顏色。

      注釋2,我們先加載我們自己的片源著色器(稍后會(huì)展示),然后用我們的片源著色器和一個(gè)cocos2d自帶的頂點(diǎn)著色器,合成一個(gè)著色程序賦給我們的shaderProgram屬性,這個(gè)屬性指示我們的著色程序

      注釋3,和前面講CCGrid類用的著色程序時(shí)的一樣,這里只啟用頂點(diǎn)著色器的attribute變量的,這里是坐標(biāo),紋理坐標(biāo),和顏色三個(gè)。

      注釋4,是對(duì)我們的這個(gè)自定義著色程序進(jìn)行鏈接。

      注釋5,這個(gè)是cocos2d幫我們?cè)O(shè)置我們的移動(dòng)、縮放、旋轉(zhuǎn)的矩陣的

      注釋6,我們得到我們自己寫的片源著色器里的兩個(gè)uniform變量的位置,一個(gè)是U_mask,一個(gè)是V_offset,這樣我們就可以在我們的代碼里通過這兩個(gè)位置為我們的這兩個(gè)變量賦值了。

      注釋7,為我們的offsetWithPosition賦一個(gè)初值。

下面我們?cè)搶?shí)現(xiàn)我們的postOffsetToShader:方法了,在initWithFile:方法下面添加如下實(shí)現(xiàn):

-(void) postOffsetToShader:(CGPoint)aOffset
{
    offsetWithPosition = aOffset;
}

 

  呵呵,就一句話,給我們的offsetWithPosition賦值為我們傳過來的參數(shù)aOffset,實(shí)在沒什么可說的這個(gè)方法,就是我們用來隨時(shí)改變offsetWithPositon用的。

下一步,我們要做的就很重要了,我們要重寫ccsprite的draw方法:

-(void) draw
{
    CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite, @"CCSprite - draw");
    
    NSAssert(!batchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called");
    
    CC_NODE_DRAW_SETUP();
    
    ccGLBlendFunc( blendFunc_.src, blendFunc_.dst );
    
    ccGLBindTexture2D( [texture_ name] );  //1
        glActiveTexture(GL_TEXTURE1);   //2
        glBindTexture( GL_TEXTURE_2D,  [_maskTexture name] );
        glUniform1i(_maskLocation, 1);
        glUniform2f(_offsetLocation, offsetWithPosition.x, offsetWithPosition.y); //3
    //
    // Attributes
    //
    
    ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );
    
#define kQuadSize sizeof(quad_.bl)
    long offset = (long)&quad_;
    
    // vertex
    NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
    glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
    
    // texCoods
    diff = offsetof( ccV3F_C4B_T2F, texCoords);
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
    
    // color
    diff = offsetof( ccV3F_C4B_T2F, colors);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
    
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    CHECK_GL_ERROR_DEBUG();
    
    
#if CC_SPRITE_DEBUG_DRAW == 1
    // draw bounding box
    CGPoint vertices[4]={
        ccp(quad_.tl.vertices.x,quad_.tl.vertices.y),
        ccp(quad_.bl.vertices.x,quad_.bl.vertices.y),
        ccp(quad_.br.vertices.x,quad_.br.vertices.y),
        ccp(quad_.tr.vertices.x,quad_.tr.vertices.y),
    };
    ccDrawPoly(vertices, 4, YES);
#elif CC_SPRITE_DEBUG_DRAW == 2
    // draw texture box
    CGSize s = self.textureRect.size;
    CGPoint offsetPix = self.offsetPosition;
    CGPoint vertices[4] = {
        ccp(offsetPix.x,offsetPix.y), ccp(offsetPix.x+s.width,offsetPix.y),
        ccp(offsetPix.x+s.width,offsetPix.y+s.height), ccp(offsetPix.x,offsetPix.y+s.height)
    };
    ccDrawPoly(vertices, 4, YES);
#endif // CC_SPRITE_DEBUG_DRAW
    
    CC_INCREMENT_GL_DRAWS(1);
    
    CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, @"CCSprite - draw");
    
    glActiveTexture(GL_TEXTURE0);   //4

}

 

  如果你自己寫這個(gè)darw方法的時(shí)候,如果不知道該寫些什么,我建議你直接去ccsprite.m里copy它的draw方法出來,然后再根據(jù)你自己的需要進(jìn)行改動(dòng),我就是這樣做的哈哈。

     draw方法里的代碼好多,有的我們已經(jīng)在前面講CCGrid的著色器時(shí)講到了,有的沒講,這些沒講到的我們也不用深究,對(duì)于我們自己定制這個(gè)draw方法來說,也就是這四個(gè)注釋值得說一下:

      注釋1,這是一個(gè)紋理綁定動(dòng)作,綁定我們的精靈紋理到紋理槽0,這里其實(shí)我們是偷了巧的,我們只寫了一句話,其實(shí)它和注釋2里的代碼是同樣的效果,在默認(rèn)情況下,是紋理槽0處于激活狀態(tài),我們直接進(jìn)行綁定操作,就是將我們的紋理綁定到當(dāng)前激活的紋理槽上,所以是綁到紋理槽0上了,在我們自己的片源著色器里有u_texture,它是指向紋理槽的,而默認(rèn)情況下它是0,這樣我們的u_texture就代表紋理槽0內(nèi)的紋理,也就是我們的精靈紋理,所以我們這樣一句代碼其實(shí)完成了三句代碼的功能。

     注釋2,這是注釋1的完整版代碼,不同時(shí)的是,它是把我們的遮罩紋理綁定到紋理槽1了,并通過glUniform1i(_maskLocation, 1)這個(gè)方法來把1傳遞給我們的著色器里的U_mask,使其指向紋理槽1。這樣u_mask就指向我們的遮罩紋理了。glUniform**()這個(gè)方法,是向第一個(gè)參數(shù)指向的位置(這個(gè)位置在前面我們已經(jīng)獲得了)代表的uniform變量賦值,方法時(shí)的數(shù)字?jǐn)?shù)代表一次要傳遞幾個(gè)數(shù)值,這時(shí)是1,代表我們要傳遞一個(gè)數(shù)值;數(shù)字后邊的i代表我們要傳的是整數(shù),如果是f的話,就代表我們要傳的是float數(shù)。

     注釋3,我們?yōu)槲覀兊闹骼锏膙_offset賦值,它是一個(gè)vec2類型的變量,所以我們是傳兩個(gè)float給我們的v_offset方法。

     注釋4,這個(gè)注釋在draw方法的最后一行,千萬不要漏掉。這是我們重新激活紋理槽0為當(dāng)前活動(dòng)紋理槽,因?yàn)槲覀冎霸谧⑨?的時(shí)候激活了紋理槽1,所以還原這個(gè)當(dāng)前活動(dòng)的紋理槽是很重要的。

事實(shí)上這時(shí)候我們的這個(gè)MaskedSprite類已經(jīng)完成了,只要實(shí)現(xiàn)我們自己的片源著色器就能用了。

現(xiàn)在,我們來寫一個(gè)我們自己的片源著色器吧

       回到Xcode,選擇File\New\New file,再選擇 iOS\Other\Empty,點(diǎn)擊Next。然后命名為Mask.fsh并點(diǎn)擊Save。然后我們的工程文件,MoveMask target,然后選擇build phase標(biāo)簽,然后把我們的Mask.fsh從compile source里拖到copy bundle source里,這樣它就會(huì)變成資源文件,而不是和我們的代碼一起編譯了。

 

 

然后把Mask.fsh的代碼替換成下面的樣子,然后我們會(huì)一步步講解我們的mask.fsh里的具體代碼:

 

#ifdef GL_ES
precision lowp float;
#endif

varying vec4 v_fragmentColor;  //1
varying vec2 v_texCoord;
uniform sampler2D u_texture;//2
uniform sampler2D u_mask;
uniform vec2 v_offset;//3

void main()
{
    vec2 onePixel = vec2(1.0 / 480.0, 1.0 / 320.0);//4
    vec2 texCoord = v_texCoord;//5
    vec2 finCoord = vec2(texCoord.x - onePixel.x*v_offset.x, texCoord.y + onePixel.y*v_offset.y);
    vec4 texColor = texture2D(u_texture, v_texCoord);//6
    vec4 maskColor = texture2D(u_mask, finCoord);
    
    vec4 finalColor = vec4(texColor.r, texColor.g, texColor.b, maskColor.a * texColor.a);//7    
    
    gl_FragColor = v_fragmentColor * finalColor;//8
}

 

 這段代碼的最前面是我們?cè)O(shè)置這個(gè)段著色器對(duì)于float數(shù)據(jù)計(jì)算的精度為低。

   注釋1,這是兩個(gè)從頂點(diǎn)著色器傳過來的兩個(gè)變量,一個(gè)是每個(gè)片源的顏色,一個(gè)是每個(gè)片源的紋理坐標(biāo)

   注釋2,這是我們定義了兩個(gè)紋理,它是被聲明為uniform的,所以我們需要在代碼里傳入這兩個(gè)紋理。一個(gè)是我們的精靈的正常的紋理,一個(gè)是我們的精靈的遮罩的紋理。

   注釋3,我們定義了一個(gè)vec2變量v_offset,用來存儲(chǔ)我們的精靈的紋理和我們的遮罩的紋理之間的位置偏差(我們就是通過這個(gè)偏差的變化來實(shí)現(xiàn)遮罩的移動(dòng)的)。同樣的,我們也把它聲明為uniform的,這樣,它也需要我們?cè)诖a里來給它賦值。

   注釋4,定義了一個(gè)vec2類,它用來存儲(chǔ)一個(gè)像素的寬和高,這樣我們?cè)谝苿?dòng)的時(shí)候就可以通過,移動(dòng)n倍的這個(gè)變量,來達(dá)到移動(dòng)多少個(gè)像素的目的。

   注釋5,我們先把由頂點(diǎn)著色器傳進(jìn)來的紋理坐標(biāo)賦給一個(gè)新的vec2變量,然后通過一系列的計(jì)算,得到在v_offset這個(gè)偏差存在的情況下,一個(gè)我們的精靈的紋理坐標(biāo)的位置,對(duì)應(yīng)的精靈的遮罩的紋理坐標(biāo)。

       我們來詳細(xì)地說一下這個(gè)計(jì)算。首先明確一個(gè)事情,我們的精靈的紋理和我們的精靈的遮罩的紋理是一樣大的,在本例中,它們都是480*320大小的,我們就拿兩張大小一樣的撲克牌來想像一下具體的情況,下面的牌相當(dāng)于精靈紋理,上面的相當(dāng)于遮罩紋理,這樣,在v_offset這個(gè)變量是(0,0)的時(shí)候,就相當(dāng)于是這兩張牌完全合在一起,上面的蓋著下面的,下面的完全看不到。當(dāng)你把這兩張牌的上面一張移動(dòng)了一定的距離使 這兩張牌疊在一起,但不完全重合的時(shí)候,我們可以看到,在這兩張牌疊在一起的部分,現(xiàn)在的下面的牌的紋理坐標(biāo)的位置,和疊在它上面的牌的紋理坐標(biāo)的位置不再是一樣的了,它們之間有了偏差。而這個(gè)偏差的值就是我們上面的牌移動(dòng)的距離,其實(shí)也就是我們的v_offset此刻的值,圖片應(yīng)該比文字更能說明問題:

 

 

      這個(gè)圖片代表兩個(gè)紋理完全重合的時(shí)候,可以看到它們重合的位置紋理坐標(biāo)是一致的,再看它們不重合的時(shí)候:

圖中我用藍(lán)色框代表精靈紋理,綠色框代表遮罩紋理,圖中的黃色區(qū)域就是在有了偏移后,兩個(gè)紋理的重合部分,在圖中我們可以清楚的看到,在重合的部分,現(xiàn)在的精靈的紋理坐標(biāo)(0,0)對(duì)應(yīng)的位置,不再是遮罩紋理的(0,0)坐標(biāo),而是圖中兩個(gè)紅線的交叉點(diǎn),遮罩紋理的大概(0.3,0.2)坐標(biāo)的樣子,這個(gè)變化的數(shù)就是我們的偏差值,也就是我們的v_offset的值。因此精靈紋理的紋理坐標(biāo),加上這個(gè)V_offset就可以得到在它的紋理坐標(biāo)位置上相應(yīng)的遮罩紋理的紋理坐標(biāo)。好的,知道了這個(gè)v_offset是干什么的,我們就可以很簡(jiǎn)單的看明白這個(gè)計(jì)算做了什么操作了。因?yàn)檫@個(gè)v_offset的值是計(jì)算的坐標(biāo)的差(下面的代碼中會(huì)有介紹),因些在我們的著色器里,要得到紋理坐標(biāo)的偏移量,我們需要用我們傳進(jìn)來的v_offset.x乘以一個(gè)像素的寬(即onePixel .x)來得到精靈紋理坐標(biāo)與遮罩紋理坐標(biāo)在x方向上的偏移量,同樣的v_offset.y*onePixel.y就可以得到在y方向上的偏移量了。

      那么有這個(gè)偏移量,我們應(yīng)該怎么得到這個(gè)偏移后的遮罩紋理的紋理坐標(biāo)呢?這個(gè)其實(shí)是要具體情況具體分析的,(你計(jì)算偏移的方式不同,這里計(jì)算的方式也會(huì)不同,你計(jì)算偏移的時(shí)候的減數(shù)和被減數(shù)的位置會(huì)決定你這里是應(yīng)該用加法還是減法,你后面會(huì)看到我們計(jì)算偏移的方式是用我們的觸摸點(diǎn)的位置減去我們的精靈的位置的),這里我們用減法,因?yàn)槿绻覀兊挠|摸點(diǎn)和在精靈的位置的左邊的話,情況就和上圖中的情況差不多,這時(shí)候,按我在后面計(jì)算偏移的方式的話,我得到的偏移量應(yīng)該是負(fù)的,這而這時(shí)候我們看圖片就能看出來,精靈紋理的紋理坐標(biāo)位置對(duì)應(yīng)的偏移后的遮罩紋理的紋理坐標(biāo)應(yīng)該是增大了,所以這里我們要用減法,所以(texCoord.x - onePixel.x*v_offset.x),這個(gè)式子得到偏移后的精靈紋理的紋理坐標(biāo)位置對(duì)應(yīng)的偏移后的遮罩紋理的紋理坐標(biāo)的x值,注意,這里有一個(gè)大陷井,不要覺得x是用減法了,那么求偏移后的y也應(yīng)該用減法,不要忘了一件事情,iPhone中用于Core Graphics的圖像坐標(biāo)系統(tǒng)和我們用的opengl_es的坐標(biāo)系統(tǒng)的y軸方向是相反的,opengl_es是從下向上增大的,而core graphics上從下向上減小的,所以我們得到的y的偏移量其實(shí)是相反的,所以這里得到偏移的y,我們需要用加法,(texCoord.y + onePixel.y*v_offset.y),這樣這個(gè)式子就得到了實(shí)際的偏移后相對(duì)應(yīng)的遮罩紋理的紋理坐標(biāo)。

      然后我們把這個(gè)求得的坐標(biāo)賦值給finCoord 。

      注釋6,我們根據(jù)我們的精靈紋理的紋理坐標(biāo),和偏移的的遮罩紋理的紋理坐標(biāo),得到屏幕上同一點(diǎn)對(duì)應(yīng)的,這兩個(gè)不同紋理的的具體的顏色,分別賦值給texcolor和maskcolor。

      注釋7,我們用我們的精靈紋理的rgb顏色做為一個(gè)新的顏色的rgb值,但是我們用我們剛剛得到的兩個(gè)texcolor和maskcolor的透明度值相乘做為這個(gè)新的顏色的透明度值,這樣主是在同一點(diǎn)上,如果遮罩紋理上的透明度為0的話,這個(gè)新的顏色的透明度就是0,那么在這個(gè)點(diǎn)上這個(gè)顏色就不可見了,如果遮罩上不是零的話,而那么相乘的透明度就不是零,那么這個(gè)顏色是可見的。

     注釋8,我們用注釋7得到的顏色乘以我們從頂點(diǎn)著色器得到的顏色,賦給我們的最終每個(gè)像素的顏色。

哦,好累呀,這一段東西實(shí)在是太繞口了,我自己都感覺說不清楚了,還是對(duì)著圖片多想想的話容易明白吧。吼吼……

希望就在前方

最后,我們應(yīng)該使用我們自己的這個(gè)MaskedSprite類了,打開HelloWorldLayer.h,在文件的頂部包含我們的類:

#import "MaskedSprite.h"

 接著在interface里加入我們的變量:

 MaskedSprite* hello;

 讓我們打開HelloWorldLayer.m文件,替換它的init方法如下:

-(id) init
{
    if( (self=[super init])) 
    {        
      hello = [MaskedSprite spriteWithFile:@"ground.jpg"];

     CGSize size = [[CCDirector sharedDirector] winSize];
    
      hello.position =  ccp( size.width /2 , size.height/2 );
        
      [self addChild: hello];
        
          self.isTouchEnabled = YES;
    }
    return self;
}

 很簡(jiǎn)單的,我們初始化我們自己的MaskedSprite類的一個(gè)對(duì)象,把它加入到屏幕中央的位置,然后加入到我們的層,最后我們啟用這個(gè)層的isTouchEnabled方法,這樣我們就可以讓這個(gè)層響應(yīng)我們的觸摸事件了。

     在init方法下面實(shí)現(xiàn)下面方法:

- (void)registerWithTouchDispatcher {
    [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self 
                                                     priority:0 swallowsTouches:YES];
}

  這個(gè)方法重新注冊(cè)我們對(duì)touch事件的響應(yīng)方式,默認(rèn)的話cclayer是響應(yīng)standard touch delegate的,現(xiàn)在我們讓它響應(yīng)targeted touch delegate。(兩種不同的響應(yīng)方式請(qǐng)參考CCTouchDelegateProtocol),priority是響應(yīng)的優(yōu)先級(jí),這是高為0,swallowsTouches設(shè)為Yes,這樣只我們的touch處理方法響應(yīng)并處理了這個(gè)touch事件,這個(gè)touch事件就不再繼續(xù)分發(fā)了。

下面主就是實(shí)現(xiàn)我們的touch響應(yīng)方法了:

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    
    CGPoint tempPoint = [self convertTouchToNodeSpace: touch];
    tempPoint = ccpSub(tempPoint, ccp(240,160));
    [hello postOffsetToShader: tempPoint];
    
    return TRUE;
}

-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
    CGPoint tempPoint = [self convertTouchToNodeSpace: touch];
    tempPoint = ccpSub(tempPoint, ccp(240,160));
    [hello postOffsetToShader: tempPoint];
}

-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    [hello postOffsetToShader:ccp(-1000,-1000)];
}

 

  在touchBegan方法中,我們得到我們的觸摸點(diǎn)在layer中的坐標(biāo),用這個(gè)坐標(biāo)減去我們的MaskedSprite的實(shí)際位置,這樣就得到了觸摸點(diǎn)和我們的精靈的位置這間的偏差,把這個(gè)偏差傳遞給我們的精靈的postOffsetToShader:方法,這樣這個(gè)偏差值就傳給了我們的精靈了,它把這個(gè)值賦給精靈的offsetWithPositon變量,而這個(gè)變量又會(huì)在我們的精靈的draw方法里通過注釋3的那句代碼傳遞給,我們自己寫的片源著色器里的被uniform聲明的v_offset變量。然后經(jīng)過我們的著色器的計(jì)算,就得到了我們的移動(dòng)的遮罩的效果。

    我們返回YES,表明,只要接收到touch事件,我們就處理。

     在touchMoved方法中,我們重復(fù)在began方法里的計(jì)算

     最后,在touchend方法里,我們傳遞給我們的精靈一個(gè)極大的負(fù)坐標(biāo),這個(gè)值和在我們的MaskedSprite類的init方法里,給offsetWithPosition賦的值是一樣的,這樣做的目的是,在沒有touch事件的時(shí)候,讓我們的遮罩紋理和精靈紋理完全不重疊,這樣精靈紋理所處的位置的遮罩紋理的透明度值肯定是0,這樣在沒有觸摸的情況下,我們的精靈就不可見了。可是只要你在屏幕點(diǎn)一觸摸你就會(huì)發(fā)現(xiàn)在,有一個(gè)經(jīng)過遮罩處理的精靈顯示在你的觸摸的位置,你移動(dòng)你的手指,這個(gè)遮罩的位置也隨著你移動(dòng)。

 

 

 利用這個(gè)功能我們能做什么

      這個(gè)功能可能會(huì)在塔防類的游戲里會(huì)有點(diǎn)用,就像本例中的一樣,如果在我們的HelloWorldLayer里先加一個(gè)背景,然后再加一個(gè)我們的MaskerSprite精靈的話,這個(gè)就可以實(shí)現(xiàn),動(dòng)態(tài)地只有在觸摸發(fā)生的情況下才在我們的游戲背景上顯示一個(gè)只在自己的遮罩范圍(遮罩應(yīng)該是一個(gè)塔的攻擊范圍的圖片)內(nèi)可見的一個(gè)位置網(wǎng)格是不是很不錯(cuò)?像這樣:

如果你有多個(gè)塔,每個(gè)塔的攻擊范圍是不一樣的,你只需要在我們的MaskedSprite類里多加幾個(gè)遮罩紋理就好了,在拖動(dòng)不同的塔的時(shí)候,根據(jù)塔的類型來綁定不同的遮罩紋理到紋理槽1,就可以了吧似乎,具體行不行,你試試吧呵呵,我也不確定能不能行呵呵。

完整源碼

  這個(gè)demo雖然笨拙,但是它還是能工作的,如果你有更好的實(shí)現(xiàn)的方法,留言告訴我,我會(huì)好好學(xué)習(xí)的,謝謝。能力有限,文中可能會(huì)用不對(duì)的地方,希望大家指教。謝謝!!

參考文章:
dingwenjie博客 http://www.cnblogs.com/dingwenjie/archive/2012/04/02/2429576.html
子龍山人博客http://www.cnblogs.com/andyque/archive/2011/09/16/2155068.html

posted on 2012-07-15 22:36 風(fēng)輕云淡 閱讀(2346) 評(píng)論(0)  編輯 收藏 引用

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美三级视频在线观看| 国产一区二区0| 国产日韩亚洲| 亚洲人成啪啪网站| 亚洲欧美日韩国产综合在线 | 先锋资源久久| 欧美成人日本| 亚洲视频一区| 久久亚洲国产精品一区二区| 欧美三级在线| 有码中文亚洲精品| 亚洲女ⅴideoshd黑人| 欧美freesex8一10精品| 这里只有精品视频在线| 麻豆av一区二区三区久久| 国产精品成人在线| 亚洲高清视频一区二区| 亚洲在线视频一区| 欧美激情亚洲自拍| 欧美一区二区三区精品电影| 欧美日本三级| 在线不卡欧美| 久久成人资源| 夜夜嗨av色综合久久久综合网| 久久亚洲影音av资源网| 国产精品久久影院| 亚洲精品一区二| 美脚丝袜一区二区三区在线观看 | 欧美影院成人| 欧美三级视频在线观看| 亚洲国产岛国毛片在线| 久久久久久91香蕉国产| 中文国产成人精品| 欧美暴力喷水在线| 在线播放亚洲| 久久精品首页| 亚洲欧美日韩综合一区| 欧美日韩一区二区精品| 日韩视频永久免费| 鲁大师成人一区二区三区| 午夜精品福利一区二区蜜股av| 欧美日韩性视频在线| 亚洲电影免费观看高清| 久久婷婷久久一区二区三区| 亚洲综合成人在线| 国产精品超碰97尤物18| 一区二区三区蜜桃网| 欧美激情精品久久久久久免费印度 | 亚洲电影第1页| 久久国产精品高清| 国产日韩欧美一二三区| 性欧美1819sex性高清| 一区二区三区久久精品| 欧美另类综合| 亚洲日本中文| 亚洲第一级黄色片| 麻豆精品视频在线| 在线精品福利| 免费久久99精品国产自| 久久精品国产69国产精品亚洲| 国产欧美短视频| 欧美一区二区三区视频在线| 亚洲天堂男人| 国产精品日韩欧美大师| 欧美一级欧美一级在线播放| 亚洲嫩草精品久久| 国产精品视频网址| 欧美亚洲视频一区二区| 性久久久久久久| 韩国一区二区三区在线观看| 久久久久久一区二区三区| 欧美在线观看天堂一区二区三区| 国产一区二区三区在线观看免费| 久久久久国产精品午夜一区| 久久精品动漫| 伊人久久婷婷色综合98网| 欧美成人精品h版在线观看| 麻豆精品精华液| 99国产精品| 一本到12不卡视频在线dvd| 国产精品s色| 久久精品国产77777蜜臀| 久久激情视频| 亚洲日本一区二区| 日韩视频一区二区三区在线播放免费观看 | 欧美一区二区在线免费播放| 欧美一区二区三区在线免费观看| 国内综合精品午夜久久资源| 免播放器亚洲| 欧美韩日高清| 亚洲在线成人| 欧美一区二区三区视频在线| 影音先锋久久| 亚洲国产一区二区三区青草影视| 欧美日韩精品免费观看视频完整| 午夜精品偷拍| 久久se精品一区二区| 亚洲国产精品一区二区尤物区| 亚洲日本黄色| 国产精品视频精品| 久久综合一区| 欧美人交a欧美精品| 午夜精品一区二区三区在线播放 | 精品av久久久久电影| 亚洲国产成人精品久久久国产成人一区| 欧美日本免费一区二区三区| 欧美一区二区免费观在线| 久久国产日韩欧美| 亚洲最新在线| 欧美一区二区三区啪啪| 最近中文字幕mv在线一区二区三区四区| 99视频在线精品国自产拍免费观看| 国产伦精品免费视频| 欧美成ee人免费视频| 欧美三级在线播放| 久久―日本道色综合久久| 欧美成人午夜激情| 欧美在线视频免费观看| 欧美成人亚洲| 久久av老司机精品网站导航| 欧美电影在线| 久久精品理论片| 欧美精品在线免费播放| 久久久久久久精| 欧美日韩情趣电影| 免费成人黄色| 国产精品中文在线| 最新国产精品拍自在线播放| 国产日韩欧美精品一区| 亚洲日本在线视频观看| 好看不卡的中文字幕| 一区二区三区国产盗摄| 在线成人h网| 午夜精品国产更新| 亚洲视频导航| 欧美91福利在线观看| 久久精品一区二区三区不卡| 欧美日韩综合在线免费观看| 欧美96在线丨欧| 国产日韩欧美成人| 一区二区三区日韩欧美| 亚洲欧洲一区二区天堂久久| 久久aⅴ国产欧美74aaa| 亚洲专区欧美专区| 欧美激情一区二区三级高清视频| 久久婷婷久久一区二区三区| 国产精品理论片| 亚洲精品日韩精品| 亚洲欧洲精品一区二区精品久久久 | 一本色道久久综合亚洲精品按摩| 亚洲国产精品久久精品怡红院| 小处雏高清一区二区三区| 亚洲私拍自拍| 欧美乱大交xxxxx| 亚洲第一色在线| 在线精品国精品国产尤物884a| 性久久久久久久| 新片速递亚洲合集欧美合集| 欧美日韩午夜剧场| 亚洲欧洲精品成人久久奇米网| 在线日韩日本国产亚洲| 久久激情视频| 久久久噜噜噜久久中文字幕色伊伊 | 亚洲精品国产精品国自产在线 | 欧美国产日韩一区二区三区| 免费久久久一本精品久久区| 狠狠做深爱婷婷久久综合一区 | 91久久久久久久久| 久久这里有精品15一区二区三区| 久久琪琪电影院| 国产日韩欧美在线播放不卡| 亚洲在线一区| 亚洲欧美一区二区在线观看| 欧美日韩综合网| 99精品99| 亚洲自拍偷拍福利| 国产精品第2页| 亚洲综合电影| 久久xxxx精品视频| 国产亚洲在线| 久久久久久国产精品mv| 女主播福利一区| 亚洲黄页一区| 欧美精品九九| 一区二区日韩| 欧美一级成年大片在线观看| 国产欧美一区视频| 欧美综合77777色婷婷| 媚黑女一区二区| 亚洲区免费影片| 欧美乱大交xxxxx| 亚洲视频在线播放| 久久国产精品亚洲77777| 国产一区香蕉久久| 久久最新视频| 亚洲毛片在线看| 亚洲欧美精品伊人久久| 国产日韩av一区二区| 久久疯狂做爰流白浆xx| 欧美成人一区二区在线|