Cocos2d-x 著色器
Cocos2d-x 著色器
宣告:本文使用的是cocos2d-x-3.17的程式碼
文章中的提到的測試程式碼下載地址https://gitee.com/Kyle12/Cocos2dRenderStudy
著色器GLProgram
Cocos2d-x 3中所有的渲染都使用了可程式設計管線,通過著色器進行渲染。Cocos2d-x底層使用的是OpenGL,對應的著色器語言為GLSL。Cocos2d-x中使用的GLSL語言版本為1.0,應該是為了跨平臺,適用更多的裝置,所以沒有使用高版本。Cocos2d-x只支援頂點著色器和片元著色器,不支援細分著色器、幾何著色器、計算著色器。
GLProgram
Cocos2d通過類GLProgram封裝了OpenGL中的著色器。
著色器編譯
點著色器和片元著色器都是通過函式GLProgram::compileShader編譯的,在編譯前會在著色器字串前面加上Cocos2d-x內部定義的Uniform值,內容如下:
static const char * COCOS2D_SHADER_UNIFORMS = "uniform mat4 CC_PMatrix;\n" "uniform mat4 CC_MultiViewPMatrix[4];\n" "uniform mat4 CC_MVMatrix;\n" "uniform mat4 CC_MVPMatrix;\n" "uniform mat4 CC_MultiViewMVPMatrix[4];\n" "uniform mat3 CC_NormalMatrix;\n" "uniform vec4 CC_Time;\n" "uniform vec4 CC_SinTime;\n" "uniform vec4 CC_CosTime;\n" "uniform vec4 CC_Random01;\n" "uniform sampler2D CC_Texture0;\n" "uniform sampler2D CC_Texture1;\n" "uniform sampler2D CC_Texture2;\n" "uniform sampler2D CC_Texture3;\n" "//CC INCLUDES END\n\n"; |
這些Uniform值會在所有的頂點著色器和片源著色器中定義,使用著色器繪製前通過函式GLProgram::setUniformsForBuiltins設定。
這些Uniform值中的矩陣(Matrix)命名,前面的M代表模型變換,V代表檢視變換,P代表投影變換。CC_Pmatrix按照正常邏輯應該說投影矩陣,但實際上CC_Pmatrix並不是,CC_Pmatrix實際上是檢視投影矩陣,也就是VPMatrix;CC_MVMatrix也不是模型檢視矩陣,而是模型矩陣MMatrix。這裡是Cocos2d本身命名錯誤的,自己編寫著色器程式時要注意。
著色器連結
著色器通過函式GLProgram::link進行連結,在連結前會通過函式GLProgram::bindPredefinedVertexAttribs設定頂點著色器中頂點屬性的location,著通過OpenGL函式glBindAttribLocation設定。設定的頂點屬性有:
const char* GLProgram::ATTRIBUTE_NAME_COLOR = "a_color"; const char* GLProgram::ATTRIBUTE_NAME_POSITION = "a_position"; const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD = "a_texCoord"; const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD1 = "a_texCoord1"; const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD2 = "a_texCoord2"; const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD3 = "a_texCoord3"; const char* GLProgram::ATTRIBUTE_NAME_NORMAL = "a_normal"; |
之所以要設定頂點屬性的location是因為系統中定義好的著色器其相同的頂點屬性location值將相同,方便快取設定輸入。例如頂點位置屬性a_position繫結為0,不管哪個系統著色器程式設定快取頂點位置輸入時都使用location=0,不用通過glGetAttribLocation獲取location。所以如果要編寫一個shader給Cocos2d中的物件使用,那麼頂點屬性的名字也需要用以上名字。
著色器連結成功後會通過GLProgram::parseVertexAttribs獲取程式中使用了的頂點屬性,儲存在_vertexAttribs中;通過函式GLProgram::parseUniforms獲取使用者自己定義的Uniform值,存在_userUniforms中。這裡不包括編譯階段系統新增的Uniform值,系新增的Uniform值完全由系統自己設定賦值,可以直接在Shader中使用。
GLProgram結構
GLProgramCache
GLProgramCache是一個用來快取GLProgram的類,防止重複建立,和Director一樣是單例模式。
函式GLProgramCache::loadDefaultGLPrograms載入系統中預設建立的著色器程式。
可以以通過函式GLProgramCache::getGLProgram獲取已快取的著色器程式。
GLProgramState
實際使用著色器時並不會直接使用GLProgram,而是使用GLProgramState。GLProgramState主要作用是設定頂點屬性和Uniform值,GLProgramState結構如下:
同樣為了防止GLProgramState重複建立,類GLProgramStateCache會快取已建立的GLProgramState。
繪製狀態RenderState
繪製的時候會需要設定繪製的狀態,如是否開啟深度比較,是否使用融合,使用的融合函式等等。這些控制主要通過類RenderState控制,RenderState結構如下:
繪製時如果要使用模板,需要通過類StencilStateManager控制,Cocos2d中的裁剪節點ClippingNode就使用了模板控制裁剪。
頂點快取和索引快取
頂點快取通過類VertexBuffer表示,索引快取由類IndexBuffer表示。對於頂點快取來說還會由頂點屬性來表示頂點快取中各個資料的作用。VertexBuffer中並不儲存頂點屬性,類VertexData就是用來儲存頂點快取與頂點屬對應的關係。VertexBuffer、IndexBuffer、VertexData三者的結構如下:
頂點屬性
對於著色器程式GLProgram程式會有頂點屬性,這個屬性由頂點著色器的輸入決定。對於頂點快取也會有頂點屬性,這個屬性由頂點快取的結構決定。當需要用著色器繪製某個快取時,著色器的頂點屬性必須要與頂點快取的頂點屬性對應之後才能繪製。
例子
Sprite著色器繪製
通過自己編寫的著色器程式來繪製Cocos2d中的Sprite,實現一些特殊的效果。自己編寫的著色器程式需要主義以下兩點:1、對應頂點著色器的輸入頂點的位置名字必須為“a_position”,紋理座標的位置名字必須為“a_texCoord”,因為編寫的程式是要繪製Cocos2d建立的快取,快取的頂點屬性是固定的,需要通過名字匹配;2、是Sprite繪製使用的是TrianglesCommand,為了合併Draw Call,會提前進行模型變換再繪製,所以只需要用CC_PMatrix(檢視投影矩陣)對點進行變換。
以下例子實現Sprite繪製成橢圓,並且給Sprite添加了青色作為底色。
完整程式碼:SpriteShaderScene.cpp/SpriteShaderScene.h,對於程式選單“Test GLProgram Shader”->“Sprite Custom Shader”。
完全自定義著色器
Cocos2d中可以重寫Node節點,並提供了CustomCommand繪製命令,利用這兩點可以很容易用自己的著色器和快取進行繪製。
以下例子通過著色器繪製了一個磚牆,這裡沒有用圖片紋理,而是完全由著色器完成繪製。
完整程式碼:CustomShaderScene.cpp/CustomShaderScene.h,對於程式選單“Test GLProgram Shader”->“Custom Shader”。
Shadertoy
Shadertoy是一個網站,裡面由很多分享的shader程式。Shadertoy上的著色器都是偏遠著色器,簡單修改一下就可以執行在Cocos2d中。
以下火焰的例子就是從shadertoy上的程式,地址為:https://www.shadertoy.com/view/XsXSWS
完整程式碼: ShaderToyScene.cpp/ShaderToyScene.h,對於程式選單“Test GLProgram Shader”->“ShaderToy Test”。