cocos2dx下的OpenGL學習總結.doc_第1頁
cocos2dx下的OpenGL學習總結.doc_第2頁
cocos2dx下的OpenGL學習總結.doc_第3頁
cocos2dx下的OpenGL學習總結.doc_第4頁
cocos2dx下的OpenGL學習總結.doc_第5頁
已閱讀5頁,還剩25頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

Objective:1、了解cocos2dx渲染流程,學習如何在cocos2dx引擎下編寫OpenGL代碼2、了解OpenGL的渲染管線,學習Vertex shader和Fragment shader的編寫,學習shader與c+程序如何傳做數(shù)據(jù)傳遞作者:織法在手游如雨后春筍般上線的今天,手游的同質化極其嚴重。不同游戲間差別可能只是一層皮,在這種情況下,一款游戲若想獲得成功,就必須有所突破,圖形就是一個很好的突破口。在中國,Cocos2dx是當下最流行的2D手游開發(fā)引擎,其最大的優(yōu)點之一就是開源。正因為這一特點,我們可以輕松的添加一些自己想要的效果,尤其在圖形方面。下面,我將講解如何在cocos2dx下使用OpenGL。目錄一Cocos2dx的渲染流程31.1Node的Visite函數(shù)31.2 Sprite的draw函數(shù)61.3 render對象淺析61.4 AutoBatch原理講解101.5 自定義RenderCommand13二OpenGL簡介142.1 OpenGL 渲染管線142.1.1 Vertex Shading152.1.2 Primitive Assembly152.1.3 Clipping162.1.4 Rasterization162.1.5 fragment shading172.1.6 Pre-Fragment Operations172.2 一個簡單的著色器程序182.3 VAO與VBO212.4 彩色的三角形232.5 shader語法簡介252.5.1 變量的聲明252.5.2 變量的轉換262.5.3 Vector Component Accessors272.5.4 shader的結構272.5.5shader中的內置變量與內置函數(shù)282.5.6 cocos2dx提供的內置變量28三 3D圖元的繪制303.1 MVP 3D 矩陣變幻303.1.1 投影矩陣31一 Cocos2dx的渲染流程要想了解如何在cocos2dx引擎下編寫OpenGL代碼,首先要知道這些代碼應當寫在哪里。而cocos2dx這個引擎的渲染模塊,正是基于OpenGL的API來編寫,我們可以研究一下他的渲染流程。1.1 Node的Visite函數(shù)打開cocos2dx引擎中的CCNode.cpp文件,翻到Visit處,可看到以下代碼,如圖1.1所示。圖1.1 VisitNode的Visite先從導演類中取得渲染器Render對象,在從矩陣棧中獲取當前的modelView矩陣。再把他們傳入帶3個參數(shù)的Visit中。下面我們看看這個函數(shù)都做了些什么,如圖1.2,1.3所示圖1.2 visit1圖1.3 visit2我們可以看到,當visible為false時,函數(shù)直接被return掉。之后先向棧中推入一個矩陣,在把當前的modelView矩陣放入棧中。而后就是我們熟悉的遍歷過程。先遍歷Z-order小于0的,繪制,而后是大于0的。這也就揭示了zorder是如何控制渲染層級的,也揭示節(jié)點樹的繪制順序。1.2 Sprite的draw函數(shù)下面,我們看一下精靈類draw到底做了些什么。打開引擎的CCSprite.cpp文件,翻到draw處,其代碼如圖1.4所示。圖1.4 Sprite:draw我們可以看到,傳說中的渲染剔除,就是在這里做的。而且就是一個AABB的判斷。這函數(shù)的主要功能,就是把相關信息放入一個command中,傳到了render對象中。1.3 render對象淺析下面,我們將詳細分析下這個render對象。首先,我們找6張可愛的水果圖片,在helloworld中重復添加這些圖片,運行效果如圖1.5所示。圖1.5 小水果在Renderer的render函數(shù)中打上斷點,如圖1.6所示。圖1.6 render函數(shù)在Renderer的render函數(shù)中打上斷點,如圖1.6所示。我們查看調用堆棧的上一級,可以看到我們的大導演在每幀的循環(huán)中做了這些事情。如圖1.7,1.8,1.9所示。首先更新scheduler,而后讓當前的場景visit(所有節(jié)點draw函數(shù)就在這里調用的),最后調用render,推出當前矩陣,交換背緩沖。圖1.7 大導演的drawScene (1)圖1.8 大導演的drawScene (2) 圖1.6 大導演的drawScene (3)而后我們再回到render的函數(shù)中,看一下他的visitRenderQueue函數(shù),如圖1.7,1.8所示。我們首先看最常用的Quard的渲染。如果當前緩存的Quard頂點數(shù)量還沒達到VBO_SIZE(10922),就把這些頂點數(shù)據(jù)拷貝到quard數(shù)組中,再用command中保存modelView矩陣對這些頂點進行變換。而對于其他的命令,就是調用他的execute方法。對于自定義的execute,其代碼如圖1.9所示,就是調用一下他的綁定函數(shù)func,這通常在自定義節(jié)點的draw函數(shù)中綁定,這也是我繪制3D場景所用的方法。圖1.7 visiteRendereQueue的QUAD_COMMAND圖1.8 visiteRendereQueue的其他命令類型圖1.9 自定義命令的1.4 AutoBatch原理講解而后,我們再看看他的drawBatchQuard是怎么做的。如圖1.10,1.11所示,這個函數(shù)首先判斷是否有頂點,若沒有則return掉。而后向顯存?zhèn)魅霐?shù)據(jù),綁定VAO。之后的那一段(圖1.11)就包含傳說中的的autoBatch,drawCall合并()。遍歷數(shù)組,當當前命令的材質與上一個相同,則batch,否則繪制之前BUFFER,把這個quard給batch進去。圖1.10 drawBatchedQuads(1)圖1.11 drawBatchedQuads(2)也就是說,如果我們相鄰添加的Sprite的紋理不同,引擎是不會做batch的,正如圖1.5所示,雖然我們只用了6張紋理,但drawCall仍然是10001。下面我們來做個試驗,來說明這個問題。首先,把之前的代碼修改為如圖1.12所示的樣子,只讓程序繪制一種水果。我們可以看到,引擎經行了Autobatch,繪制所有的水果的drawCall只有1次(另一次是glClear()。圖1.12 代碼1圖1.13 autobatch效果再次修改代碼,如圖1.14所示,交替添加兩種水果的sprite。drawCall又變回10001。再次修改代碼,如圖1.16所示。圖1.14 代碼2圖1.15 autobatch效果2圖1.16代碼3圖1.17 autobatch效果3修改代碼后,芒果遮住了所有的葡萄(葡萄還發(fā)生了繪制),此時drawCall變成了3。這也正應正了前面的猜想。那么,我沒修改Z-order是否會影響drawCall呢?答案是肯定的。修改代碼如圖1.18所示。我們可以看到,修改了Zorder,并沒有使drawCall暴增,因為在繪制前,引擎會根據(jù)zOrder進行排序,所以此時drawCall為5。再對比一下幀數(shù),我們可以發(fā)現(xiàn),經過drawCall合并的渲染,效率提升了至少20個百分點。圖1.18代碼4圖1.19 autobatch效果41.5 自定義RenderCommand下面,我們開始進入正題,探究一下如何在2dx下寫openGL代碼。首先,我們依照慣例寫個類,CustomRenderCommandTest:class CustomRenderCommandTest : public cocos2d:Nodepublic:CustomRenderCommandTest();CustomRenderCommandTest();static CustomRenderCommandTest* create();void draw(cocos2d:Renderer *renderer, const cocos2d:Mat4& transform, uint32_t flags)override;void onDraw(const cocos2d:Mat4& transform, uint32_t flags);protected:virtual bool init()override;cocos2d:CustomCommand m_renderCommand;void CustomRenderCommandTest:draw(cocos2d:Renderer *renderer, const cocos2d:Mat4& transform, uint32_t flags)m_renderCommand.init(_globalZOrder); m_renderCommand.func = CC_CALLBACK_0(CustomRenderCommandTest:onDraw, this, transform, _globalZOrder);renderer-addCommand(&m_renderCommand);void CustomRenderCommandTest:onDraw(const cocos2d:Mat4& transform, uint32_t flags)glClearColor(0.1f, 0.1f, 0.5f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);其實邏輯很簡單,定義一個customRenderCommand,在draw初始化他,使用CC_CALLBACK_0綁定一下onDraw函數(shù)(CC_CALLBACK_1,2,3,4指的是固定值參數(shù),其宏定義的內容就是std:bind)。在onDraw里,我們簡單的調用glClear函數(shù),使屏幕變?yōu)樗{色。其效果如圖1.20所示。二 OpenGL簡介在上一章,我們已經了解了cocos2dx引擎的渲染流程,了解了如何在2dx下編寫OpenGL代碼。但問題又來了,到底該如何編寫OpenGL代碼么?且聽在下分解。2.1 OpenGL 渲染管線OpenGL implements whats commonly called a rendering pipeline, which is a sequence of processing stages for converting the data your application provides to OpenGL into a final rendered image.咳咳,每天裝個B,生活好甜蜜。上面那一段是OpenGL紅寶書第八版上對OpenGL管線的描述。說管線就是把從程序輸入的數(shù)據(jù)變?yōu)樽罱K圖像的一系列不同的程序階段,也就是我們常說的渲染流水線。整個管線的流程,如圖2.1所示。圖2.1 openGL 渲染管線2.1.1 Vertex Shading當我們向顯卡發(fā)送渲染命令時,顯卡會把顯存中的頂點傳入這個階段。針對每一個頂點,程序員可以編寫代碼控制他的各項屬性,如位置,顏色,紋理坐標,以及其他自定義屬性。在可編程管線時代,把3D空間的頂點坐標轉換為2D的平面的頂點坐標,也是在這一階段實現(xiàn)的。在這一階段之后,GPU會把各個頂點傳入Tessellation Shading 和 Geometry Shading。這兩個階段我們現(xiàn)在不用關心,在以后的深入學習中,再去探討。2.1.2 Primitive Assembly經過前面的一系列操作,顯卡就會將前面的一系列頂點組裝成圖元,在ES系統(tǒng)下,通常是組裝成三角形。傳入下面一個階段。2.1.3 Clipping組裝好的圖元將會被送進這個階段。在這個階段中,完全超出視圖區(qū)域的將被完全丟棄,一部分超出剪裁區(qū)域的將被裁切成新的圖元。2.1.4 Rasterization經過剪裁的圖元將被送入這個階段,進行光柵化操作。也就是把三角形進行填充,把頂點信息進行插值,這也是為什么給頂點設置不同的顏色,可以畫出彩色方塊的原因。如圖2.2所示。圖2.2 彩色立方體2.1.5 fragment shading這一階段,就是我們所熟知的片源著色器階段。在這一階段,我們使用自定義的程序,對每一個片源進行處理。在可編程管線時代,紋理的采樣,光照的處理,都是在這一階段執(zhí)行。2.1.6 Pre-Fragment Operations在執(zhí)行完上一階段后,管線還會做一些額外的操作,如圖2.3所示。圖2.3 Pre-Fragment Operations在這里,我們常用用到的是這幾個,剪裁測試,Alpha測試,深度測試,蒙版測試,顏色混合。剪裁測試,是通過調用glScissorXXX相關函數(shù),設定一個矩形區(qū)域,當片源不在矩形區(qū)域中,管線將自動阻止他進入下一階段。Alpha測試,我們可以通過調用glAlphaFunc,設定只有alpha滿足一定條件后,才能通過這個測試。深度測試,當將要繪制的片源Z值大于原先幀緩沖片源的Z值時,才會通過這一階段。蒙版測試,我們可以調用glStencil 相關函數(shù),設置蒙版區(qū)域,只有蒙版區(qū)域被標記為某些值得時候,才能通過測試。通常,深度緩沖和蒙版緩沖是放在一起的,深度緩沖占24位,蒙版緩沖占8位。另外糾正一點,我們平常愛說的,給某一個層添加一個蒙版,添加了一個半透明的層,蒙到場景上面。準確的說,這種方法叫Blending,而不是蒙版。是管線根據(jù)alpha值,與之前的幀緩沖經行疊加。2.2 一個簡單的著色器程序了解了OpenGL管線,小伙伴們是不是已經按捺不住,想要做點什么呢。下面,我們將寫第一個應用shader程序,如圖2.4,2.5,2.6,2.7所示。圖2.4 第一個三角形代碼-1圖2.5 第一個三角形代碼-2圖2.6 第一個三角形代碼-3圖2.7 第一個三角形Vertex Shader:attribute vec2 position;void main()gl_Position = vec4(position,0.0,1.0);Fragment Shader:void main()gl_FragColor = vec4(0.1,0.1,0.5,1.0);下面,我將來逐一講解這些程序。首先是初始化shader,這些都是固定的代碼,以后可以封裝到基類中,以供統(tǒng)一調用。在這里,我不得不吐槽一下新版本的GLProgramState。這個類是對于GLProgram的一個封裝,提供了對uniform和attribute的統(tǒng)一管理。但當我查看他的源碼的時候發(fā)現(xiàn),他的實現(xiàn)方法用了共用體,用了std:function的callback,令人很難駕馭,而且在apply函數(shù)中,沒有檢測是否需要更新??傊?,還沒有小編以前寫的UniformManager好用。所以建議大家不要用他。2.3 VAO與VBO下面就是創(chuàng)建頂點,并把頂點傳入顯存中。有些同學看到這里就要問了,為什么要把數(shù)據(jù)傳入顯存中,內存中一份數(shù)據(jù),顯存中一份數(shù)據(jù),這不是浪費存儲么?但問題在于,CPU和顯卡的GPU并不是放一起的,在硬件中是兩個獨立的原件。CPU更善于隨機訪問的邏輯運算,GPU善于做批量處理的數(shù)學運算。我們的手機、電腦之所以要裝顯卡,是為了把CPU從繁重的圖形學運算中解脫出來,專職處理邏輯運算。而讓更善于做并行批量數(shù)學運算的顯卡去做圖形學計算。這樣一來,CPU和GPU就成了一個CS架構。在硬件高度發(fā)展的今天,圖形學程序的效率損失,往往會發(fā)生在CPU和GPU之間的數(shù)據(jù)傳輸以及同步上,如圖2.8所示。圖2.8 GPU和CPU的數(shù)據(jù)傳遞正因如此,OpenGL提供了一個叫Vertex Buffer Object的解決方案,也就是我們常說的VBO。我們使用glGenBuffer申請BUFFER的標識(有GL管理),使用glBindBuffer指定當前操作哪一個VBO,使用glBufferData向顯存?zhèn)鬏敂?shù)據(jù)。關于這些函數(shù)的用法,我們可以在/opengles/sdk/docs/man/ 查到。顯卡有了數(shù)據(jù),但還不知到該如何解釋這些數(shù)據(jù),所以我們要告訴他。這樣Vertex Array Object就派上了用場。我們使用GLprogram對象的getAttribLocation方法,獲取頂點數(shù)組在shader中的位置,使用glEnableVertexAttribute來激活這個位置,使用glAttributePointer來制定對數(shù)據(jù)的解析。glAttributePointer的幾個參數(shù)比較有講究,我單獨拿出來講講。第一個參數(shù)index是指用getAttribLocation獲取的那個位置,第二個參數(shù)size,是一個頂點由幾維,只能寫1,2,3,4,如我們程序傳的都是二維頂點,寫2就好了。第三個參數(shù)type是數(shù)據(jù)類型,我們通常用GLFloat。第四個參數(shù)normalize指是否歸一化,我們通常寫false。第5個參數(shù)stride,指的是一個頂點數(shù)據(jù)的字節(jié)數(shù),如果我們定點數(shù)據(jù)只有一種,如位置,這里寫0就可以。第6個參數(shù),offest,指的是第一個該數(shù)據(jù)在傳入VBO中的偏移量。我們的這個程序里只傳入了頂點位置,寫0就好了。這些工作做完后,我們不要忘了把VBO和VAO綁定回0,以免影響其他圖元的繪制。最后,在ondraw函數(shù)中,綁定前面的VAO,調用drawArray命令,完成三角形的繪制。在這個程序里,我們的shader寫的很簡單,就是把傳過來的頂點負值,把顏色設為藍色。2.4 彩色的三角形有的小伙伴看到這里,不禁要吐槽了。”擦,老子褲子都脫了,你就給我看這個”。別急,下面我們讓三角形帶上點顏色。我們對之前的代碼做如下修改,如圖2.9,2.10,2.11所示,運行效果如2.12所示。圖2.9 彩色三角形代碼修改-1圖2.10 彩色三角形代碼修改-2圖2.11 彩色三角形代碼修改-3圖2.12 彩色三角形運行效果Vertex Shader:attribute vec2 position;attribute vec4 color;varying vec4 v_color;uniform mat4 vmatrix;void main()gl_Position = vmatrix * vec4(position,0.0,1.0);v_color = color;Fragment Shader:varying vec4 v_color;uniform mat4 cmatrix;void main()gl_FragColor = cmatrix * v_color;在這個程序中,我們對每個頂點添加了顏色,為頂點的變幻和顏色的變幻添加了一個控制矩陣。在shader中,我們讓矩陣乘以這些值。2.5 shader語法簡介Shader的整體語法與C+類似,但也有些許不同,現(xiàn)在我們來逐一分析。2.5.1 變量的聲明Shader中的變量與C語言類似,有float,double, int, uint, bool。這些類型的詳細解釋如圖2.13所示。但要注意的是,不能用unsigned做修飾,bool型變量目前不能用作unifrom,因為引擎沒有提供傳值得基礎。圖2.13 shader中的基本類型除了上面的基礎類型,在shader中還有一些和向量矩陣有關的拓展類型,如圖2.14所示。圖2.14 shader中的拓展類型腦筋靈活的同學,又該發(fā)出疑問,在shader中數(shù)組和指針該如何定義呢。額,告訴大家一個不幸的好消息,shader是腳本語言,指針這種殺傷腦細胞的東西是木有滴,對于數(shù)組的聲明,我們可以像既可以c語言那樣在變量后面加中括號(float a3;),也可以像C#那樣在類型名后加中括號(float a;)2.5.2 變量的轉換在shader的語法中,對類型的隱式轉換比C更加嚴格,比如float a= false這樣的代碼是會報錯的。只有一下類型的隱式轉換是被允許的,如圖2.15所示。圖2.15 shader中的隱式類型轉換大多數(shù)情況下,我們使用顯示類型轉換,類型名()的形式,如float(a);vec4(a_vec2.xy,0.0,1.0);2.5.3 Vector Component Accessorsvec4(a_vec2.xy,0.0,1.0)?xy是什么?這是為了方便書寫,GLSL對vec中的變量提供了訪問別名,如下圖所示。圖2.16 vector的別名2.5.4 shader的結構我們知道,我們常用的shader有兩個,一個是頂點著色器,一個是片元著色器。在頂點著色器中,傳入的變量用attribute修飾,通常放在程序頂部。需要在頂點著色器與片元著色器間傳遞的變量,用varying修飾,通常這種變量在頂點著色器中賦值,在片元著色器中讀?。ㄗx取到的內容是

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論