『壹』 cocoscreator 2.4.x版本 drawcall優化 第一期(掌握控制drawcall數量的必要知識)
1,測試環境
2,為何drawcall多會影響性能
3, 哪些組件支持渲染
4,影響drawcall的因素
5,一句話介紹如何減少drawcall
6,哪些渲染組件不會被渲染
7,減少drawcall的理論(放在第二期)
8,理論指導實踐,實踐印證理論,demo實操(放在第三期)
9,總結(放在第三期)
「測試環境」 :
1.Mac 系統
2.cocoscreator 2.4.x版本
「為何drawcall多會影響性能?」
Drawcall: 繪制調用,指cpu調用圖形繪制介面命令gpu進行圖形繪制
「每一次繪制前,CPU要准備繪制參數(狀態)比如色彩通道(color filter),繪圖方式(shader)等復雜的數據處理,然後Drawcall,如果有大量drawcall,cpu會很「忙」,而gpu的處理能力很強,這時他可能閑置,不能充分發揮應有的能力,導致性能下降。」
「哪些組件支持渲染:」 因為一個drawcall是一次cpu調用圖形繪制介面命令 gpu進行圖形繪制渲染的過程,所以需要了解cocoscreator中哪些組件支持渲染,才能更好的控制drawcall
**
「影響drawcall的因素:」
1,層級(zindex)
2,材質(Material)(shander,貼圖(紋理),混合模式(blend))。只有擁有相同材質的渲染節點 才可能進行批處理,貼圖,shader 決定了材質,而層級則決定了相同的材質 是否能 進行合並處攜橋賀理 即合並網格(mesh) 合並drawcall.,
「一句話介紹如何減少drawcall:」 繪制狀態的變化 是導致drawcall增多的 主要原因。cocoscreator認為要以深度(zindex)優先的方式對渲染組件進行渲染,並且cocoscreator認為相同的材質可以被批量渲染。所以具有相同材質的並且連續的渲染節點 可以合並渲染 減少drawcall.
「連續:」
1,層級相同添加順序相鄰,
2,層級不同 中間層級沒有其他材質的渲染組件。比如 a的層級是1 b的層級是3 在 1-3層級之間沒有其他材質的 渲染組件.
「影響drawcall的因素:」
「1,渲染節點(zindex)層級」
zIndex是節點的層級是用來對節點進行排序的關鍵屬性,它決定一個節點 在兄弟節點之間的層級,和誰被優先渲染。
1) zIndex 的取值介於 cc.macro.M IN_ZINDEX 和 cc.macro.MAX_ZINDEX 之間
即 - math.pow(2,15). 和 math.pow(2,15)-1之間。
實際操作中一般是 -1 到 n n一般不會超過1000
2)父節點主要根據節點的 zIndex 和添加次序來排序,擁有更高 zIndex 的節點將被排在後面(後被渲染先被渲染的圖在後被渲染的圖下面),如果兩個節點的 zIndex 一致,先添加的節點會穩定排在另一個節點之前。排在前面的節點先被渲染,也就是說兩張圖層級相同 先添加的會先被渲染 顯示出來的結果是 在後被渲染的圖的下面。
3)節點在 children 中的順序決定了其渲染順序。父節點永遠在所有子節點之前被渲染
4)node節點放在Canvas或者父節點的zindex默認值是0
5)決定節點層級的另一個因素是siblingIndex 他的權重低於 zIndex 當我們在編輯器上編輯借點的時候 兄弟節點之間的zIndex相同,消數為什麼會出現一個先被渲染一個後被渲染呢 ,就是因為 siblingIndex 不同,排在前面的siblingIndex要小一些後面的要大一些 最終後面的後選擇然 層級就在 前面的上邊。 也就是說 zindex 其決定性作用,zIndex相同 就比較siblingIndex來判定最終層級。
「2,材質」
1)紋理(貼圖)
2)shander:渲染器,能夠讀懂的點和顏色的對應關系的程序,簡單來說就是繪圖的方式)
只有擁有相同材質的物體才可以進行批處理。因此,辯派如果你想要得到良好的批處理效果,你需要在程序中盡可能地復用材質和物體。
如果你的兩個材質僅僅是紋理不同,那麼你可以通過 紋理拼合 操作來將這兩張紋理拼合成一張大的紋理。一旦紋理拼合在一起,你就可以使用這個單一材質來替代之前的兩個材質了。
「哪些渲染組件不會被渲染」
cocoscreator 認為 透明度 === 0. 或者 active = false 的渲染組件 不會被渲染。
『貳』 WebGL Shader教程
關於WebGL Shader的視頻教程比較少,你開發游戲用的是那種游戲引擎,Layabox,cocos creator,還是白鷺。每種游戲引擎在shader的開發上都有些差別,主要是引擎向GPU提交數據上會有不同的寫法,存數據的位置也不太一樣。具體到shader內功能的開發,都是差不多的,因為都是用的WebGL那一套。
https://e.csdn.net/course/detail/32454
這個教程主要是用Layabox來開發webGL Shader的教程。講的很詳細,對新手入門很友好,源代碼都可以下載,比較方便新手學習。如果你是使用cocos creator 或 白鷺的話,可以參照引擎的官方文檔,熟悉引擎怎麼提交數據的,shader內部的計算都是一樣的,也很有學習的價值。
『叄』 求cocos2d-x教程
cocos教程網路網盤免費資源在線學習
鏈接: https://pan..com/s/1lYZHKPPVuvBR4rddE1jasA
cocos教程 極客學院Cocos2d-x源碼 05_第5階段 項目實戰 04_第4階段 功能擴展 03_第3階段 常用功能 02_第2階段 基礎知識 01_第1階段 環境搭建 5 使用Eclipse在Ubuntu下搭建Cocos2d-x 3集成開發環境 4 Cocos Code IDE使用 3 Windows環境下Visual Studio 2013中搭建Cocos2d-x 3.1集成開發環境 2 Cocos2d-x3.1rc0項目創建及新功能介紹 1 WinMac環境Cocos2d-x開發環境搭建 06. HelloWorld示例詳解.webm 05. 在Mac平台編譯成Android程序.webm 04. Mac平台開發環境搭建.webm
『肆』 CocosCreator教程(入門篇)
自動釋放資源: 切換場景後,上一個場景中的資源,從內存中釋放。
延遲載入資源: 意味著不用等待所有資源載入完畢,才顯示場景。(快速切換場景,資源陸續在畫面顯示)
普通圖,子層為一張spriteFrame。
創建方式:拖拽場景節點,到資源管理器。
精靈圖,子層為多張spriteFrame。(精靈圖合成軟體:TexturePacker、Zwoptex)
打包時,將所在目錄中的所有碎圖,合成為圖集。
數字為內容的圖集。
動態字體:.ttf
點陣圖字體:.fnt + .png(存在於同一目錄)
小型動畫
模式: web audio、dom audio
操作流程:
(1)導出:文件 => 資源導出,選擇 .fire場景文件,輸出assets目錄的 .zip壓縮包。
(2)導入:文件 => 資源導入,選擇壓縮包源路徑、解壓路徑,輸出assets目錄內容。
基於size mode,盡量去除spriteFrame無像素的部分,減小圖片尺寸。
作用: 用於變換、子節點定位基準。
對攝像機、渲染組件的了解。
對widget、layout等UI組件的了解。
(1)創建動畫的基本流程
(2)時間曲線(雙擊動畫線,進入編輯窗口)
(3)事件管理(雙擊游標、加減按鈕控制參數個數)
(4)腳本控制
碰撞組件(普通碰撞)
(1)editing——是否為編輯模式
(2)regenerate points——計算圖形邊界,自定生成控制點,數值為控制點的生成密度 / 准確度
(3)ctrl + 點擊——刪除控制點
(4)組件類型:矩形、圓形、多邊形
(5)設置碰撞組(項目 => 項目設置 => 分組設置):
制定分組 => 匹配分組 => 碰撞組件所在節點上,設置所屬分組
(6)腳本控制
Box2D物理引擎(高級碰撞)
(1)audioSource組件
(2)腳本控制
(1)定義 CCClass
(2)實例化
(3)判斷類型
(4)構造函數(ctor)
(5)實例方法
(6)繼承(extends)
(7)父構造函數
(8)完整聲明屬性
properties常用參數
(1)獲得組件所在的節點
(2)獲得其它組件
(3)獲得其它節點及其組件
(4)訪問已有變數里的值(通過模塊訪問)
(1)節點狀態和層級操作
(2)更改節點的變換(位置、旋轉、縮放、尺寸)
(3)顏色和不透明度
(4)常用組件介面
cc.Component 是所有組件的基類,任何組件都包括如下的常見介面:
(1)創建新節點
(2)克隆已有節點
(3)創建預制節點
(4)銷毀節點
(1)載入和切換
(2)通過常駐節點,進行場景資源管理和參數傳遞
(3)場景載入回調
(4)預載入場景
(1)資源屬性的聲明
(2)靜態載入(在屬性檢查器里設置資源)
(3)動態載入
(4)載入遠程資源和設備資源
(5)資源的依賴和釋放
(1)監聽事件
(2)關閉監聽
(3)發射事件
(4)派送事件
(5)事件對象(回調參數的event對象)
(1)滑鼠事件類型和事件對象
(2)觸摸事件類型和事件對象
(3)其它事件
(1)動作控制
(2)容器動作
(3)即時動作
(4)時間間隔動作
(5)動作回調
(6)緩動動作
(1)XMLHttpRequest——短連接
(2)WebSocket——長連接
對象池的概念
在同一場景中,需要多次進行節點的生成、消失時,假如直接進行創建、銷毀的操作,就會很浪費性能。因此,使用對象池,存儲需要消失的節點,釋放需要生成的節點,達到節點回收利用的目的。
工作流程
(1)初始化對象池
(2)從對象池請求對象
(3)將對象返回對象池
清除對象池
『伍』 cocos2dx creator canvas 怎麼使用shader
滿懷期待用了一段時間cocos creator, 比較令人失望,文檔不夠全, 介面太亂,經常遇到和原cocos2d介面不匹配問題, 導致學習成本增加。作為一個初學者我不知道後面還會有什麼坑。所以決定暫時先退出cocos creator 使用。從stio到creator觸控總是令人比較失望。
『陸』 cocos creator 2.4.0 渲染流程詳解(七:ForwardRender)
全文共5000+字,分為8個章節,由本人歷時一周整理而來。由於篇幅問題,將本文分為8個章節分開發布。全文 ( 不 ) 詳細描述了cocoscreator 引擎的2.40版本中,web平台的js部分引擎的渲染流程。請將文章配合源碼一起食用!
由於我尚在學習引擎源碼中,文章可能有不正確的部分,所以我會不斷更新內容攜讓。如有錯誤或補充,請留言交流!
全部章節鏈接:
一: 渲染流程中用到的核心類
二 : 渲染流程詳解
三: RenderFlow 的運行邏輯
四: Assembler 的作用
五: ModelBatcher 數據合知隱弊批
六: 材質系統
七: ForwardRender
ForwardRender 繼承於 Base, 是與底層渲染最靠近的類型,當上面的流程處理完畢後,會在ForwardRender 的 render() 中處理當前場景的渲染狀態,材質,光照,通道,著色器,更新著色器的統一變數。並在 _draw() 中調用 device.draw()方法,進行繪制。
部分重要的繼承於 Base 的成員變數:
_device:根據運行平台對應的繪制圖形對象 gfx.Device 的實例,用於繪制圖形到屏幕,類型定義於 cocos2d
enderergfxindex.js。
_programLib : 管理 shader 定義,獲取,檢查等相關的變數。類型定義於 cocos2d
enderercoreprogram-lib.js。
_stage2fn:保存有不同渲染通道的名稱與其對應的不同渲染方法。ForwardRender 中設置有 shadowcast, opaque, transparent 三種渲染通道。
_viewPools:單個相機的描述數據類(View) 的對象池。一個View對應一個相機。
_drawItemsPools:渲染數據類的對象池,保存有每個渲染批次需要的model,effect 等數據。
_stageItemsPools:單個渲染通道需要渲染的數據的對象池,搭族本質是對 _drawItemsPools 中的數據按照不同通道進行了分類。
ForwardRender 中定義的成員變數:
_lights:保存所有燈光數據。
_shadowLights:保存所有陰影燈光數據。
類名 ForwardRender 翻譯為前向渲染,泛指傳統上只有 Opaque 和 Transparent 兩個通道的渲染技術。cocos有三個渲染通道,渲染通道方法定義在 _stage2fn 中。
渲染管線具體詳解請參考unity官方文檔(對的,真要學cocos還得看unity的文檔):
內置渲染管線中的渲染路徑
相關鏈接
『柒』 如何用Cocos引擎打造次世代3D畫質『游戲大觀
從Cocos 2d-x 3.0起我們已經可以在游戲中使用3D元素。Cocos引擎推出3D功能的時間不算太遲,我們已經可以看到越來越多的手機上能流暢地渲染3D游戲,而且這些機型正在成為主流。在最近兩年我們可以看到,高端手機游戲從2D轉到3D的傾向很明顯。許多游戲開發商試圖在競爭激烈的紅海里佔有一席之地,那麼選擇開發3D游戲或許會是一個強有力的競爭手段。
上面的視頻是我的下一款游戲作品《Food of the Gods》。這游戲使用了Cocos 2d-x 3.3,視頻是從我iPhone上錄制的實際運行效果。在這篇文章里我將要介紹我是如何製作它、如何把它跑在cocos引擎上的。對於熟悉cocos官方提供的3D示例游戲 《Fantasy Warrior》的開發者,將會看到以下一些主要不同點:
1. 光照貼圖(Light Mapping):你將看到每件物體都有被照亮並且投射陰影。光影效果的質量是由你的3D工具軟體決定的,用3D軟體能烘焙出復雜的光效,包括直接光照,反射光照,以及陰影。
2. 頂點合並(Vertex Blending):請注意看路、草地和懸崖交接的地方,看不到任何可見的接縫。
3. 透明遮罩(Alpha Masks):灌木如果沒有透明遮罩就跟紙片一樣。
4. 濾色疊加的公告板(Billboards):增加一些光束和其他環境的效果。
所有的模型都是用一個叫Modo的3D 軟體建模製作的,貼圖則是使用Photoshop。關於3D模型的製作和貼圖的繪制在此就不再贅述,網上已經有很多教程,在此主要介紹下跟Cocos 2d-x有關的部分。
模型網格和貼圖(Meshes and Textures)
如下圖所示,每個模型的貼圖都是由幾個256 x 256或者更小的貼圖組成的。同時你也會注意到我把所有的小圖片都合在了一張貼圖上,這是減少GPU繪制次數(draw call)最簡單的方法之一。貼圖是從http://www.cgtextures.com 或者網上找的。
為了把這些圖片拼接起來,我使用的是Photoshop的補償濾鏡(offset filter)然後在接縫的地方用修復畫筆來做一些自然的過渡。為了獲得一種油畫的視覺效果我會先使用cutout濾鏡(注意:cutout濾鏡也會使得png格式圖片的壓縮效果更好),然後在需要的地方繪制一些高光和陰影的效果。我發現如果直接拿照片當貼圖的話,當你把它尺寸縮小的時候會出現圖像噪點。
另一種方案是為每一個模型網格製作一整張獨立的貼圖。當網格比較小或者攝像機不是很靠近網格的時候這種方法是可行拆橡的。如果你的photoshop技術過硬的話,出來的效果會更好。附帶的好處是,因為只使用一張貼圖因此只有一次GPU繪制調用。但我不建議採用這種方法來製作第一人稱射擊游戲(FPS)中的建築,因為當你走得很靠近建築物的時候,貼圖解析度過低的問題就會顯露出來。我不喜歡用這種整張貼圖方法,因為這實在太費時耗力了。這個場景的製作花了我足足四天時間。
光照貼圖(Light Maps)
當你做好模型和貼圖之後,現在就可以來烘畝御殲焙光照貼圖了。Cocos 2d-x目前還不像Unreal或Unity一樣在官方編輯器里提供烘焙光照貼圖的功能,但是別失望,大部分的製作3D模型的軟體都可以烘焙光照貼圖,並且效果比市面上任何游戲引擎的效果還好。首先,在你的3D工具軟體里,先給場景打好燈光,照亮場景,然後為每份網格製作第二張UV map。每份網格的表面都必須被映射在0到1范圍內的UV 平面上。這聽起來好像很復雜且耗時,但在Modo里這是非常簡單的。我先後使用 「Atlas map」的UV 工具和「Pack UV」工具,這兩個工具會自動將網格展開成一個相當不錯的排布圖。
這些都完成迅沖之後,設置3D工具軟體的渲染器為「只渲染烘焙的光照」,然後開始渲染。當然了,如果你想做一些環境光遮罩的效果也是可以的。
你也可以使用一些解析度較低的光照貼圖。有時候這樣的效果反而會看起來更好,因為相互混疊的模糊像素會讓陰影看起來更柔和。上面的這些建築都映射到一張512 x 512的光照貼圖上。整個場景總共使用了4 張512 x 512的光照貼圖。請確保每個小圖塊之間有一定的空隙,且讓你的渲染范圍比這些圖塊的邊界多出幾個像素。這樣可以防止當較低的mip-maps(一種紋理采樣)起作用時黑邊出現在網格周圍的角落裡。
最後一點聽起來像是3D技術的行話。如果是對Texture Packer熟悉的話,那麼其中的「Extrude」值起到的作用就是剛剛我所描述的。對貼圖的邊緣接縫做一些塗抹處理,這樣在精靈之間就不會有那些煩人的縫隙了,那些縫隙在這里會變成多邊形邊緣的黑邊。
如果你想犧牲內存和包大小來提高性能的話,你可以把顏色和光照信息都烘焙到一張貼圖上並避免共同使用一張光照貼圖。但是這樣做的話,同樣的像素密度,貼圖的大小至少得翻一倍。這完全取決於你個人、以及你游戲的要求。
接下來,添加頂點顏色。我在地形上提供了頂點顏色,這可以讓著色器在合成懸崖頂上的草地貼圖時,不會有任何可見的接縫。下圖中塗成白色的頂點部分可以合成你指定的貼圖。在這個例子里實際上我只使用紅色通道,當然了根據實際需要你可以使用4個通道(RGBA)去合成不同的貼圖。
最後,我把整個場景分成了很多獨立的網格(mesh):每個建築都有自己獨立的網格,地形獨立一個網格,水也是獨立一個。帶透明遮罩的貼圖也會有一個網格——比如視頻中看到的植物葉子和小旗子。我這樣做有兩個原因,首先,讓地形、建築、水和帶透明遮罩的貼圖各自使用不同的著色器。其次,我們打算通過不渲染攝像機范圍外的對象來減少性能開支。很重要的一點是攝像機會根據網格的包圍盒來決定對象是否可見,因此盡量把網格弄成小塊,這樣包圍盒會比較小。
導出
完成了模型和貼圖之後,我們需要把每個mesh導出為一個.fbx文件。幸運的是,大多數的3D建模軟體都支持這個功能。Autodesk為此格式提供了一個免費SDK。但不幸的是,Modo 701在導出fbx格式時會出現相當多的錯誤。因此我必須自己寫一些腳本來保證第二組貼圖坐標和頂點顏色的正確導出。你可以從我個人網站上的「Modo Scripts」部分下載這個導出腳本。搞定fbx之後,你將需要用到Cocos 2d-x自帶的fbx-conv.exe命令行工具,它位於Cocos 2d-x根目錄的/tools下。
fbx-conv.exe -a your_mesh_name_here.fbx
使用「-a」參數後,工具會同時導出mesh的二進制文件(.c3b)和文本格式文件(.c3t)。文本格式的文件非常的有用,你可以利用它來查看所有的東西是否被正確導出,但千萬不要把它放到resource目錄下。如果所有的都被正確地導出的話,你將在c3t文件的開頭看到以下的內容:
「attributes」: [{
「size」: 3,
「type」: 「GL_FLOAT」,
「attribute」: 「VERTEX_ATTRIB_POSITION」
}, {
「size」: 3,
「type」: 「GL_FLOAT」,
「attribute」: 「VERTEX_ATTRIB_NORMAL」
}, {
「size」: 2,
「type」: 「GL_FLOAT」,
「attribute」: 「VERTEX_ATTRIB_TEX_COORD」
}, {
「size」: 2,
「type」: 「GL_FLOAT」,
「attribute」: 「VERTEX_ATTRIB_TEX_COORD1″
}]
注意VERTEX_ATTRIB_TEX_COORD1這個屬性。如果沒有它光照貼圖將無法顯示。如果你導出了一張帶頂點顏色的mesh,你也應該要看到一個類似的屬性才行。還有一點很重要,貼圖的坐標也必須按正確的順序才行。我通常採用的是第一個tex_coord是瓦片貼圖,最後一個tex_coord是光照貼圖。使用Modo的話,uv maps會按照字母順序排列。
著色器(Shaders)
我花了很長的一段時間來搞懂GLSL和著色器,但正如編程中經常遇到的,有時候一個點通了,其他的就都好理解了。一旦理解了其中的原理,你便會發現著色器真的很簡單。如果你不只是想用Cocos 2d-x來把貼圖套到模型網格上的話,你需要學會如何寫著色器。目前Cocos 2d-x沒有Unreal那樣好用的著色器可視化編輯器(visual shader editor),所以我們只能自己動手焊代碼。
本節我將講解我為視頻中的游戲場景所寫的著色器,並說明我做了什麼、為什麼這樣做。如果你對著色器已經非常熟悉了,那麼可以快速跳過本節。
首先,先來看一下如何將著色器應用到模型網格上。
這段代碼摘自Cocos 2d-x的測試集cpp-tests工程。如果你用不同的著色器來載入大量的meshes,那麼最好根據功能來進行,這樣可以避免冗餘。那麼現在我們只關心如下的代碼段,來看下這個著色器。
GLProgram* shader =GLProgram::createWithFilenames(「shaders/lightmap1.vert」,」shaders/lightmap2.frag」);
GLProgramState* state = GLProgramState::create(shader);
mesh->setGLProgramState(state);
Texture2D* lightmap =Director::getInstance()->getTextureCache()->addImage(「lightmap.png」);
state->setUniformTexture(「lightmap」,lightmap);
「lightmap1.vert」是頂點著色器(vertex shader)。如果將其應用到網格上,那麼每個頂點的每一幀都將執行這個操作。而「lightmap2.frag」是片段著色器(fragment shader),網格上貼圖的每個像素的每一幀都將執行這個操作。我不太確定為什麼將其命名為「片段著色器」,我一直認為應叫做「像素」著色器(pixel shader)。從這段描述,我們可以很容易理解為什麼大量著色器指令會降低幀率,尤其是你用片段著色器的話。
接下來我們詳細地分解頂點著色器:
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec2 a_texCoord1;
這些屬性是由渲染器提供的。「a_position」是頂點的位置。「a_texCoord」 和 「a_texCoord1」對應你那兩個UV坐標。還記得在.cbt文本格式文件中開頭部分的「VERTEX_ATTRIB_TEX_COORD」么?這些值與屬性對應起來了。你可以在渲染器中獲取更多其他的屬性,包括頂點法線(vertexnormal)和頂點顏色(vertex color)。請在cocos引擎的CCGLProgram.cpp中查看完整屬性列表。
varying vec2 v_texture_coord;
varying vec2 v_texture_coord1;
「varying」值將被傳到片段著色器中(fragment shader)。片段著色器所需要的任何變數前都需要添加「varying」限定符。這個例子中,我們僅需要知道這兩個貼圖的坐標。
void main(void)
{
gl_Position = CC_MVPMatrix * a_position;
v_texture_coord.x = a_texCoord.x;
v_texture_coord.y = (1.0 – a_texCoord.y);
v_texture_coord1.x = a_texCoord1.x;
v_texture_coord1.y = (1.0 – a_texCoord1.y);
}
設置頂點位置,拷貝貼圖的坐標給varying values,這樣片段著色器就可以使用這些值。現在我們一起來分解片段著色器。
#ifdef GL_ES
varying mediump vec2 v_texture_coord;
varying mediump vec2 v_texture_coord1;
#else
varying vec2 v_texture_coord;
varying vec2 v_texture_coord1;
#endif
聲明從頂點著色器傳遞過來的「varying」 值
uniform sampler2D lightmap;
還記得在將著色器應用到網格時所使用的 state->setUniformTexture(「lightmap「,light map); 語句么?這個值就是對應語句中的那個貼圖。
void main(void)
{
gl_FragColor = texture2D(CC_Texture0, v_texture_coord) *(texture2D(lightmap, v_texture_coord1) * 2.0);
}
這個語句設置像素顏色。首先你會注意到從未聲明過的 CC_Texture0變數。Cocos 2d-x中有大量可在著色器中使用的默認統一變數。再次強調,可在CCGLProgram.cpp中查看完整屬性列表。這個例子中,CC_Texture0對應在3D模型中所應用到網格中的貼圖。texture2D命令會在給定的貼圖坐標中去查找貼圖的像素顏色和透明度。它會返回一個包含了那個像素的RGBA值的vec4值 。所以這里我會在UV1中查找到瓦片貼圖的顏色值,然後在UV2中查到光照貼圖的顏色值,最後把兩個值相乘。
你應該注意到了我先是把光照貼圖的顏色值兩兩相乘了。因為貼圖顏色值范圍為0.0-1.0,所以很顯然,如果用白色值vec4(1.0, 1.0, 1.0, 1.0)去乘中間灰值vec4( 0.5, 0.5, 0.5,1.0 ),那麼你仍是得到一個中間灰值vec4( 0.5, 0.5, 0.5,1.0 )。將兩個值相乘可以使貼圖更亮,同時也可以使貼圖更暗,這將使你獲得一個很好的可變的亮度范圍。
『捌』 cocos creator 2.1版本Material材質系統解析
管理與緩存 shader 源碼與編譯後的 shader 引用
_templates 關聯 shader_name 與 shader 源碼 (vert, frag, defines) 。顯而易見, shader_name 要求全局唯一。
_cache 緩存編譯後的 shader 引用。因為支持宏, 不同的宏配置,就對應了單獨的 shader 源碼,
不同的宏配置編譯出不一樣的 program ,所以 _cache 的 key 為元組 (shader_name, defines) 計算得到。
Pass 通過 programName 綁定 shader ,並記錄一些 webgl 的狀態:
詳見 enginecocos2dcore enderer ender-engine.js:10322
是不是類比於 Unity 的 Rendering Mode ?
如果是的話, 2d 游戲, stage 基本上都是設為 transparent 就可以了
Technique 管理1到多個 pass . 多個 pass 的意義在於多通道渲染一組模型。 描邊或許算是一種應用場景?
Technique 也提供了 pass 中用到的 uniform 變數的名稱、卜禪類型、大小和值的管理。
為 Technique 設定 Stages ,可為渲染順序提供參考,通常設為 transparent 。 stages 為數組類型, passes 也是數組類型,是否存在一一對應的關系?
_layer 不知道作用
關聯多個 Technique
配置 uniform 屬性值
配置 shader 宏
注意 defines 要求在構造函數中給出,後續 define 的值可以變,但屬性沒辦法直接調用 define 函數動態增減
關聯 一個 Effect
管理 _texIds :
維護一個更新哈希值 _hash 。材質數據有變化時,需要調用 updateHash 更新哈希。
上述可知, Material , Effect , Technique , Pass 都只是數據容器而已,具體如何使用,就是渲染函數的責任了。
網上的資料講, OGRE 的材質系統分成三層抽象: Material , Technique , Pass . Unity 的材質系統也是三層: Shader , SubShader , Pass 。多 Pass 實現同一模型,調用多次渲染。多 Technique 方便作低中高質量切換, Material 存放配置數據。
cocos creator 的材質系統多出個 Effect ,現在還是比較不理解。
渲染相關類:
render-engine.js 中定義了唯一一個 stage : transparent .並在 ForwardRenderer 中注冊了 transparent stage的渲染函數 _transparentStage 。
渲染入口函數為 ForwardRenderer.prototype.render ,遍歷所有相機,為每個相機調用一型褲塵次 ForwardRenderer.prototype.renderCamera(camera, scene) 。然後跳入 Base._render ,清除設備,從 scene._models 中 extractDrawItem ,遍歷每個 drawItem ,從 effect.getTechnique(stage) 中得到 tech 。最純畢後調用 _transparentStage 。
_transparentStage 設定下矩陣,又回到 Base._draw 函數中,執行真正的渲染。
Base._draw 根據 Effect , Technique , Pass 的數據,得到 shader ,並為 shader 設置好 webgl 狀態和各個 Uniform 變數,最後調用 device.draw 完成一個渲染流程。
根據渲染流程,可推測, cocos creator 的材質系統也是三層: Effect , Technique , Pass 。 Material 繼承 Asset ,對 Effect 作進一步封閉, 是為了方便編輯器?
詳見 enginecocos2dcore enderer ender-engine.js:13303 和 enginecocos2dcore enderer ender-engine.js:13677
『玖』 CocosCreator游戲性能優化(3):GPU優化之降低計算解析度
本文從降低計算或設計解析度來分如何提升性能, 僅提供一些可參考的思路。
本文鏈接 CocosCreator游戲性能優化(3):GPU優化之降低計算解析度
相關鏈接 CocosCreator游戲性能優化(1):性能分析工具
CocosCreator游戲性能優化(2):合批渲染之RenderToTarget
GPU的Fragment Shader即片元著色器程序在執行的時候,會並行每個像素(嚴格定義是光柵化後的每個片元,也即resolution)都執行一次。
此時主要的性能小號取決於兩方面:
1、片元著色器程序內部邏輯的復雜度和計算量。
2、需要執行的片元數量。
現在我們從削減片元數量的角度來提升性能
假設我們的圖片TextureA。應用了ShaderA。
TextureA的寬高分別是1920和1080。
此時我們的片元著色器程序執行次數calcCount = 1920*1080 = 2,073,600次。即200萬次。
這在很多普通機器上是可能存在性能問題的,即便shader邏輯不復雜。
因此我們TextureA的寬高減半, 傳入片元著色器程序。計算後的calcCount = 960*540= 518,400次。即50萬次。
執行完shader後通過線性拉伸來使用該處理過後地textureA,如下。
我們發現計算量降低為原來的1/4,而圖的精度值僅降低了1/2.
因此,我們我們在對圖的渲染精度要求不高或不需要的情況下,應盡可能地降低其計算解析度,來提升基基純性能。
通過上述分析啟發,我們做游戲時,最初在決定設計解析度的時候,應該預先考慮到這個問題。
鋒擾 在使用GPU渲染到紋理時,可以盡量減小目標紋理解析度。使片元數量降低,從而降低計算量。
離屏渲染,和目標FrameBuffer解析度有關。如果目標紋理解析度搏咐能夠降低,那麼游戲中大部分對象的解析度都會比較低,因而會帶來非常可觀的性能效率提升。
最後將RenderTexture傳入節點進行scale拉伸,得到我們需要的實際顯示效果。
本文鏈接 CocosCreator游戲性能優化(3):GPU優化之降低計算解析度
相關鏈接 CocosCreator游戲性能優化(1):性能分析工具
CocosCreator游戲性能優化(2):合批渲染之RenderToTarget
『拾』 Cocos 3.x 由淺到淺入門批量渲染
參考
由淺到淺入門批量渲染(一)
由淺到淺入門批量渲染(二)
由淺到淺入門批量渲染(三)
由淺到淺入門批量渲染(四)
由淺到淺入門批量渲染(完)
【Unity游戲開發】靜態、動態合批與GPU Instancing
本文只摘抄部分原文,達到概念性了解。
批量渲染其實是個老生常談的話題,它的另一個名字叫做「合批」。在日常開發中,通常說到優化、提高幀率時,總是會提到它。
可以簡單的理解為: 批量渲染是通過減少CPU向GPU發送渲染命令(DrawCall)的次數,以及減少GPU切換渲染狀態的次數,盡量讓GPU一次多做一些事情,來提升邏輯線和渲染線的整體效率。 但這是建立在GPU相對空閑,而CPU把更多的時間都耗費在渲染命令的提交上時,才有意義。
如果瓶頸在GPU,比如GPU性能偏差,或片段著色器過於復雜等,那麼沒准適當減少批處理,反而能達到優化的效果。所以要做性能優化,還是應該先定位瓶頸到底在哪兒,然後再考慮優化方案,而不是一股腦的就啪啪啪合批。當然,通常情況下,確實是以CPU出現瓶頸更為常見,所以適當的兆握了解些批量渲染的技法,是有那麼一丟丟必要的。
靜態合批是一種聽起來很常用,但在大多數手游項目里又沒那麼常用的合批技術。這里,我簡單的將靜態合批分為預處理階段的合並,和運行階段的批處理。
合並時,引擎將符合合批條件的渲染器身上的網格取出,對網格上的頂點進行空間變換,變換到合並根節點的坐標系下後,再合並成一個新的網格;這里需要注意的是,新網格是以若干個子網格的形式組合而成的,因為需要記錄每一個合並前網格的索引數量和起始索引(相對於合並後的新網格)。
空間變換的目的,是為了「固化」頂點緩沖區和索引緩沖區內的數據,使其頂點位置等信息都在相同的坐標系下。這樣運行時如果需要對合並後的對象進行空間變換(手動靜態合批對象的根節點可被空間變換),則無需修改緩沖區內的頂點屬性,只提供根節點的變換矩陣即可。
在Unity中,可以通過勾選靜態批處理標記,讓引擎在打包時自動合並;當然,也可以在運行時鬧鄭調用合並函數,手動合並。打包時的自動合並會膨脹場景文件,會在一定程度上影響場景的載入時間。此外,不同平台對於合並是有頂點和索引數量限制的,超過此限制則會合並成多個新網格。
運行時是否可以合批(Batch)成功,還取決於渲染器材質的設置。
當然,如果手動替換過場景中所有Material,也會打斷批次。
靜態合批與直接使用大網格(是指直接製作而成,非靜態合並產生的網格)的不同,主要體現液猜頌在兩方面。
其一,靜態合批可以主動隱藏部分對象。靜態合批在運行時,由於每個參與合並的對象可以通過起始索引等彼此區分,因此可以通過上述多次DrawCall的策略,實現隱藏指定的對象;而直接使用大網格,則無法做到這一點。
其二,靜態合批可以有效參與CPU的視錐剔除。當有剔除發生時,被送進渲染管線的頂點數量就會減少(通過參數控制),也就意味著被頂點著色器處理的頂點會減少,提升了GPU的效率;而使用大網格渲染時,由於整個網格都會被送進渲染管線,因此每一個頂點都需要被頂點著色器處理,如果攝像機只能照到一點點,那麼絕大多數參與計算的頂點最後都會被裁減掉,有一些浪費。
當然,這並不意味著靜態合批一定就比使用大網格要更好。如果子網格數量非常多,視錐剔除時CPU的壓力也會增加,所以具體情況具體分析吧~
靜態合批採用了以空間換時間的策略來提升渲染效率。
其優勢在於:網格通常在預處理階段(打包)時合並,運行時頂點、索引信息也不會發生變化,所以無需CPU消耗算力維護;若採用相同的材質,則以一次渲染命令,便可以同時渲染出多個本來相對獨立的物體,減少了DrawCall的次數。
在渲染前,可以先進行視錐體剔除,減少了頂點著色器對不可見頂點的處理次數,提高了GPU的效率。
其弊端在於:合批後的網格會常駐內存,在有些場景下可能並不適用。比如森林中的每一棵樹的網格都相同,如果對它採用靜態合批策略,合批後的網格基本等同於:單顆樹網格 x 樹的數量,這對內存的消耗可能就十分巨大了。
總而言之,靜態合批在解決場景中材質基本相同、網格不同、且自始至終都保持靜止的物體上時,很適用。
試想一個場景:一場激烈的戰斗中,雙方射出的箭矢飛行在空中,數量很多,材質也相同;但因為都在運動狀態,所以無法進行靜態合批;倘若一個一個的繪制這些箭矢,則會產生非常多次繪制命令的調用。
對於這些模型簡單、材質相同、但處在運動狀態下的物體,有沒有適合的批處理策略呢?有吧,動態合批就是為了解決這樣的問題。
動態合批沒有像靜態合批打包時的預處理階段,它只會在程序運行時發生。 動態合批會在每次繪制前,先將可以合批的對象整理在一起(Unity中由引擎自動完成),然後將這些單位的網格信息進行「合並」,接著僅向GPU發送一次繪制命令,就可以完成它們整體的繪制。
動態合批比較簡單,但有兩點仍然需要注意:
動態合批不會在繪制前創建新的網格,它只是將可以參與合批單位的頂點屬性,連續填充到一塊頂點和索引緩沖區中,讓GPU認為它們是一個整體。
在Unity中,引擎已自動為每種可以動態合批的渲染器分配了其類型公用的頂點和索引緩沖區,所以動態合批不會頻繁的創建頂點和索引緩沖區。
在向頂點和索引緩沖區內填充數據前,引擎會處理被合批網格的每個頂點信息,將其空間變換到世界坐標系下。
這是因為這些對象可能都不屬於相同的父節點,因此無法對其進行統一的空間轉換(本地到世界),需要在送進渲染管線前將每個頂點的坐標轉換為世界坐標系下的坐標(所以Unity中,合並後對象的頂點著色器內被傳入的M矩陣,都是單位矩陣)。
相對於上述看起來有點厲害但是本質上無用的知識而言,了解動態合批規則其實更為重要。比如:
當我們想要呈現這樣的場景:一片茂密的森林、廣闊的草原或崎嶇的山路時,會發現在這些場景中存在大量重復性元素:樹木、草和岩石。它們都使用了相同的模型,或者模型的種類很少,比如:樹可能只有幾種;但為了做出差異化,它們的顏色略有不同,高低參差不齊,當然位置也各不相同。
使用靜態合批來處理它們(假設它們都沒有動畫),是不合適的。因為數量太多(林子大了,多少樹都有), 所以合並後的網格體積可能非常大,這會引起內存的增加 ;而且,這個合並後的網格還是由大量重復網格組成的,不劃算。
使用動態合批來處理他們,雖然不會「合並」網格,但是仍然需要在渲染前遍歷所有頂點,進行空間變換的操作;雖然單顆樹、石頭的頂點數量可能不多,但由於 數量很多,所以也會在一定程度上增加CPU性能的開銷,沒必要 。
那麼,對於場景中這些模型重復、數量多的渲染需求,有沒有適合的批處理策略呢?有吧,實例化渲染就是為了解決這樣的問題。
實例化渲染,是通過調用「特殊」的渲染介面,由GPU完成的「批處理」。
它與傳統的渲染方式相比,最大的差別在於:調用渲染命令時需要告知GPU這次渲染的次數(繪制N個)。當GPU接到這個命令時,就會連續繪制N個物體到我們的屏幕上,其效率遠高於連續調用N次傳統渲染命令的和(一次繪制一個)。
舉個例子,假設希望在屏幕上繪制出兩個顏色、位置均不同的箱子。如果使用傳統的渲染,則需要調用兩次渲染命令(DrawCall = 2),分別為:畫一個紅箱子 和 畫一個綠箱子。
如果使用實例化渲染,則只需要調用一次渲染命令(DrawCall = 1),並且附帶一個參數2(表示繪制兩個)即可。
當然,如果只是這樣,那GPU就會把兩個箱子畫在相同的位置上。所以我們還需要告訴GPU兩個箱子各自的位置(其實是轉換矩陣)以及顏色。
這個位置和顏色我們會按照數組的方式傳遞給GPU,大概這個樣子吧:
那接下來GPU在進行渲染時,就會在渲染每一個箱子的時候,根據當前箱子的索引(第幾個),拿到正確的屬性(位置、顏色)來進行繪制了。
靜、動態合批實質上是將可以合批的對象真正的合並成一個大物體後,再通知GPU進行渲染,也就是其頂點索引緩沖區中必須包含全部參與合批對象的頂點信息; 因此,可以認為是CPU完成的批處理。
實例化渲染是對網格信息的重復利用,無論最終要渲染出幾個單位,其頂點和索引緩沖區內都只有一份數據, 可以認為是GPU完成的批處理 。
其實這么總結也有點問題,本質上講: 動、靜態合批解決的是合批問題,也就是先有大量存在的單位,再通過一些手段合並成為批次;而實例化渲染其實是個復制的事兒,是從少量復制為大量,只是利用了它「可以通過傳入屬性實現差異化」的特點,在某些條件下達到了與合批相同的效果。
無論是靜態合批、動態合批或實例化渲染,本質上並無孰優孰劣,它們都只是提高渲染效率的解決方案,也都有自己適合的場景或擅長解決的問題。個人以為:
Unity下可以通過以下兩種方式快速優化骨骼蒙皮動畫:
在相同的測試環境下,再次進行測試後可以發現,這種方法確實可以產生一定效果。其原因我認為主要有以下兩點:
開啟GPU蒙皮,Unity會通過ComputeShader的方式,使用GPU進行蒙皮。GPU上有大量的ALU(算數邏輯單元),可以並行大量的數值計算,效率較高,應該很適合這種針對頂點屬性的數值計算。
但是實際情況是:在移動設備上使用GPU蒙皮反而會使主線程的耗時增加。通過Profiler可以發現,CPU把更多的時間放在了執行ComputeShader上,由於骨骼動畫的實例很多(500個),所以這個調用時間本身成為了性能熱點。所以,以目前的情況來看,這種在移動設備上使用GPU蒙皮的方式,似乎不適合處理大量骨骼蒙皮動畫實例(也許是我使用的方式存在問題)。
簡單來說,它們基本的思路,都是將骨骼蒙皮動畫的「結果」預先保存在一張紋理中;然後在運行時通過GPU從這張紋理中采樣,並使用采樣結果來更新頂點屬性; 再結合實例化技術(GPU instancing) ,達到高效、大批量渲染的目的。
可以簡單的將它的工作流程分為兩個階段:
這個階段其實是為後面的播放階段准備動畫資源,你也可以把這個資源簡單的理解為一種特殊類型的動畫文件。
首先,在編輯狀態下,讓攜帶骨骼蒙皮動畫的角色,按照一定幀率播放動畫。我們知道:動畫播放時,角色網格會被蒙皮網格渲染器(SkinnedMeshRenderer)更新而產生變形(頂點變化);如果在動畫播放的同時,記錄下每一個頂點在這一時刻相對於角色坐標系(通常是角色腳下)的位置。那麼,當動畫播放完畢時,我們會得到一張「每一個頂點在每一個關鍵幀時的位置表」,我們就叫它「幀頂點位置表」吧。可是「幀頂點位置表」名字太長,你可能不太好記,我打字也比較麻煩,所以我們後面就叫它「表表」吧。
除了表表,我們還能得到與它對應的動畫信息,比如這個動畫的名稱、時長、幀率、總幀數、是否需要循環播放等信息。
到這里,第一個階段我們需要的內容就准備就緒了,可以進入下一個階段:播放動畫階段。
在動畫播放時,通過一個變數(播放時長)來更新播放進度;結合上一階段我們記錄下來的動畫總長度、動畫總幀數信息,就可以計算出當前動畫播放到了第幾幀(當前幀數 = 已播放時長 / 動畫總時長 x 動畫總幀數)。一旦得到當前動畫幀,就表示可以通過表表鎖定一行頂點數據。
接下來,只要遍歷這行頂點數據;然後根據索引找到網格中對應的頂點並更新它的位置,就等於完成了蒙皮工作。
既然通過一個播放進度、表表和一些簡單的動畫信息,就能直接完成蒙皮(對頂點屬性的更新),那麼我們就不再需要以下內容了:
使用CPU來讀取表表,並遍歷更新所有頂點,顯然不如GPU來的高效;所以接下來,我們將這一步放到渲染管線中的頂點變換階段,借著GPU處理頂點的契機,完成使用表表中的數據對頂點屬性進行更新。
實例化渲染的特點是使用相同網格,相同材質,通過不同的實例屬性完成大批量的帶有一定差異性的渲染;而烘焙頂點恰好符合了實例化渲染的使用需求。
所以,我們只需將控制動畫播放的關鍵屬性:比如過渡動畫播放的V坐標、當前和下一個動畫的插值比例等,放入實例化數據數組中進行傳遞;再在頂點著色器中,對關鍵屬性獲取並使用即可。
與蒙皮網格渲染器的比較
烘焙頂點的主要問題
除了烘焙頂點,另一種常用的優化方案是烘焙骨骼矩陣動畫。
聽名字就知道,烘焙骨骼矩陣與烘焙頂點位置,原理十分相似;最大的差異在於它們在烘焙時所記錄的內容不一樣:烘焙頂點記錄下來的是每個頂點的位置,而烘焙骨骼矩陣記錄下來的是每一根骨骼的矩陣,僅此而已。
烘焙骨骼矩陣最大的意義在於它補上了烘焙頂點的短板:受頂點數量限制、烘焙的動畫紋理過大 及 紋理數量較多。
烘焙頂點對於紋理(面積)的使用是受頂點數量決定的,可以簡單理解為:
所以,當模型的頂點數量過多時(數以千計),這種烘焙方式或者無法烘焙下整個模型(頂點數量>2048),或者需要一張或多張(法線、切線)大尺寸紋理(<2048 && > 1024)。
但是,烘焙骨骼矩陣主要取決於骨骼的數量,可以簡單理解為:
在移動平台上,通常20根左右的骨骼就可以取得不錯的表現效果,所以相對於烘焙頂點,烘焙骨骼可以記錄下更長的動畫,同時它也不再受頂點數量的限制,也無需對法線或切線進行特殊處理(因為可以在采樣後通過矩陣計算得出)。
針對這兩種方案,我個人認為並沒有絕對的孰優孰劣;正如你所看到的,烘焙骨骼在處理復雜模型或多動作時更具優勢;但如果渲染數量較多、模型頂點數較少、表現需求較弱(無需法向量參與計算)時,烘焙頂點也是值得嘗試的,因為它在性能上會更好一些。
目前靜態合批方案為運行時靜態合批,通過調用 BatchingUtility.batchStaticModel 可進行靜態合批。
該函數接收一個節點,然後將該節點下的所有 MeshRenderer 里的 Mesh 合並成一個,並將其掛到另一個節點下。
在合批後,將無法改變原有的 MeshRenderer 的 transform,但可以改變合批後的根節點的 transform。只有滿足以下條件的節點才能進行靜態合批:
引擎目前提供兩套動態合批系統,instancing 合批和合並 VB 方式的合批,兩種方式不能共存,instancing 優先順序大於合並 VB。
要開啟合批,只需在模型所使用的材質中對應勾選 USE_INSTANCING 或 USE_BATCHING 開關即可。
通過 Instancing 的合批適用於繪制大量頂點數據完全相同的動態模型,啟用後繪制時會根據材質和頂點數據分組,每組內組織 instanced attributes 信息,然後一次性完成繪制。
合並 VB 合批適用於繪制大量低面數且頂點數據各不相同的非蒙皮動態模型,啟用後繪制時會根據材質分組,然後每組內每幀合並頂點和世界變換信息,然後分批完成繪制
通常來說合批系統的使用優先順序為:靜態合批 > instancing 合批 > 合並 VB 合批。
骨骼動畫是一種常見但類型特殊的動畫,我們提供了 預烘焙骨骼動畫 和 實時計算骨骼動畫 兩套系統,針對不同方向的需求,分別優化。
這兩套系統的唯一開關就是 SkeletalAnimation 上的 useBakedAnimation 開關,啟用時會使用預烘焙骨骼動畫系統,禁用後會使用實時計算骨骼動畫系統,運行時也可以無縫切換。
目前所有模型資源在導入後,prefab 中全部默認使用預烘焙系統,以達到最佳性能。我們建議只在明顯感到預烘焙系統的表現力無法達標的情況下,再使用實時計算系統。雖然兩套系統可以在運行時無縫切換,但盡量不要高頻執行,因為每次切換都涉及底層渲染數據的重建。
基於預烘焙系統的框架設計,蒙皮模型的 instancing 也成為了觸手可及的功能,但要保證正確性還需要收集一些比較底層的信息。
這里的根本問題是,同一個 drawcall 內的各個模型使用的骨骼貼圖必須是同一張,如果不是同一張,顯示效果會完全錯亂。所以如何將動畫數據分配到每張骨骼貼圖上,就成為了一個需要用戶自定義的信息,對應在編輯器項目設置的 骨骼貼圖布局面板 進行配置。
注意 :
目前底層上傳 GPU 的骨骼紋理已做到全局自動合批復用,上層數據目前可以通過使用 批量蒙皮模型組件(BatchedSkinnedMeshRenderer)將同一個骨骼動畫組件控制的所有子蒙皮模型合並: