引擎提供了CCGLProgram類來處理着色器相關操作,對當前繪圖程序進行了封裝,其中使用頻率最高的應該是獲取着色器程序的接口:const GLuint getProgram();
該接口返回了當前着色器程序的標識符。后面將會看到,在操作OpenGL的時候,我們常常需要針對不同的着色器程序作設置。注意,這里返回的是一個無符號整型的標識符,而不是一個指針或結構引用,這是OpenGL接口的一個風格。對象(紋理、着色器程序或其他非標准類型)都是使用整型標識符來表示的。
CCGLProgram提供了兩個函數導入着色器程序,支持直接從內存的字符串流載入或是從文件中讀取。這兩個函數的第一個參數均指定了頂點着色器,后一個參數則指定了像素着色器:
- /** Initializes the CCGLProgram with a vertex and fragment with bytes array */
- bool initWithVertexShaderByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray);
- /** Initializes the CCGLProgram with a vertex and fragment with contents of filenames */
- bool initWithVertexShaderFilename(const char* vShaderFilename, const char* fShaderFilename);
僅僅加載肯定是不夠的,我們還需要給着色器傳遞運行時必要的輸入數據。在着色器中存在兩種輸入數據,分別被標識為attribute和uniform。
attribute變量是應用程序直接傳遞給頂點着色器的變量,在段着色器中不能訪問。它描述的是每個頂點的屬性,如位置、法線等,被限制為向量或標量這樣的簡單結構。必須為每個頂點指定對應的值,這類似於C中的函數參數。
uniform變量是全局性的,可以同時在頂點着色器和段着色器中訪問。在整個渲染流水線中,每個uniform變量都是唯一的,不存在每個像素或頂點需要單獨定義的問題,這一點是和C的全局變量類似的。uniform變量的可定義類型會更豐富一些,還可以包括紋理矩陣和紋理,甚至可以通過uniform block自定義復雜的數據類型。
uniform變量是全局性的,可以同時在頂點着色器和段着色器中訪問。在整個渲染流水線中,每個uniform變量都是唯一的,不存在每個像素或頂點需要單獨定義的問題,這一點是和C的全局變量類似的。uniform變量的可定義類型會更豐富一些,還可以包括紋理矩陣和紋理,甚至可以通過uniform block自定義復雜的數據類型。
int glGetUniformLocation(GLuint program, const GLchar* name);
對uniform變量的設置存在着一系列以"glUniform"為前綴的函數,這些函數的第一個參數為需要設置的參數標識,后面跟若干參數值.
shader配置文件 .vsh 和 .fsh
initWithVertexShaderFilename(const char* vShaderFilename, const char* fShaderFilename) 這里我們可以用到shader配置文件 .vsh 和 .fsh
可以參考quick-lua3.3 quick\lib\quick-src\extra\filters\shader目錄下的部分例子,如
example_MultiTexture.vsh:
attribute vec4 a_position; attribute vec2 a_texCoord; attribute vec4 a_color; #ifdef GL_ES varying lowp vec4 v_fragmentColor; varying mediump vec2 v_texCoord; #else varying vec4 v_fragmentColor; varying vec2 v_texCoord; #endif void main() { gl_Position = CC_PMatrix * a_position; v_fragmentColor = a_color; v_texCoord = a_texCoord; }
example_MultiTexture.fsh:
#ifdef GL_ES precision mediump float; #endif varying vec4 v_fragmentColor; varying vec2 v_texCoord; uniform sampler2D u_texture1; uniform float u_interpolate; void main() { vec4 color1 = texture2D(CC_Texture0, v_texCoord); vec4 color2 = texture2D(u_texture1, v_texCoord); gl_FragColor = v_fragmentColor * mix( color1, color2, u_interpolate); }
OpenGL的渲染管線主要包括:
-
准備頂點數據(通過VBO、VAO和Vertex attribute來傳遞數據給OpenGL)
-
頂點處理(這里主要由Vertex Shader來完成,從上圖中可以看出,它還包括可選的Tessellation和Geometry shader階段)
-
頂點后處理(主要包括Clipping,頂點坐標歸一化和viewport變換)
-
Primitive組裝(比如3點組裝成一個3角形)
-
光柵化成一個個像素
-
使用Fragment shader來處理這些像素
-
采樣處理(主要包括Scissor Test, Depth Test, Blending, Stencil Test等)。
1 attribute vec4 position;
2 attribute vec4 inputTextureCoordinate;
3
4 varying vec2 textureCoordinate;
5
6 precision mediump float;
7 uniform float overTurn;
8
9 void main()
10 {
11 gl_Position = position;
12 if (overTurn>0.0) {
13 textureCoordinate = vec2(inputTextureCoordinate.x,overTurn-inputTextureCoordinate.y);
14 }
15 else
16 textureCoordinate = vec2(inputTextureCoordinate.x,inputTextureCoordinate.y);
17 }
首先,每一個Shader程序都有一個main函數,這一點和c語言是一樣的。
這里面有兩種類型的變量,一種是attribute,另一種是varying.
attribute是從外部傳進來的,每一個頂點都會有這兩個屬性,所以它也叫做vertex attribute(頂點屬性)。
而varying類型的變量是在vertex shader和fragment shader之間傳遞數據用的。
這里的變量命名規則保持跟c一樣就行了,注意gl_開頭的變量名是系統內置的變量,所以大家在定義自己的變量名時,請不要以gl_開頭。
而CC_MVPMatrix是一個mat4類型的變量,它是在cocos2d-x內部設置進來的。
即:
attribute:外部傳入vsh文件的變量 每幀的渲染的可變參數 變化率高 用於定義每個點。
varying:用於 vsh和fsh之間相互傳遞的參數。
precision mediump float 定義中等精度的浮點數。
uniform 外部傳入vsh文件的變量 變化率較低 對於可能在整個渲染過程沒有改變 只是個常量。
在main()當overTurn大於0的時候,函數里面做的事情就是將紋理y軸反轉。
vertex shader是作用於每一個頂點的,如果vertex有三個點,那么vertex shader會被執行三次。
.fsh 是片段shader。在這里面我可以對於每一個像素點進行重新計算。
1 varying highp vec2 textureCoordinate;
2 precision mediump float;
3 uniform sampler2D videoFrame;
4
5 vec4 memoryRender(vec4 color)
6 {
7 float gray;
8 gray = color.r*0.3+color.g*0.59+color.b*0.11;
9 color.r = gray;
10 color.g = gray;
11 color.b = gray;
12
13 color.r += color.r*1.5;
14 color.g = color.g*2.0;
15
16 if(color.r > 255.0)
17 color.r = 255.0;
18 if(color.g > 255.0)
19 color.g = 255.0;
20
21 return color;
22 }
23
24 void main()
25 {
26 vec4 pixelColor;
27
28 pixelColor = texture2D(videoFrame, textureCoordinate);
29
30 gl_FragColor = memoryRender(pixelColor);
31 }
varying highp vec2 textureCoordinate 就是從vsh中傳過來了紋理坐標。
uniform sampler2D videoFrame 是我們真正的紋理貼圖。
texture2D(videoFrame, textureCoordinate) 將紋理中的每個像素點顏色取出到pixelColor。
可以用memoryRender(pixelColor)將取到像素點重新加工。
fragment shader中也有一個main函數,同時我們看到這里也聲明了一個與vertex shader相同的變量textureCoordinate。前面我們講過,這個變量是用來在vertex shader和fragment shader之間傳遞數據用的。所以,它們的參數類型必須完全相同。如果一個是vec3,一個是vec4,shader編譯的時候是會報錯的。
而gl_FragColor我們知道它肯定是一個系統內置變量了,它的作用是定義最終畫在屏幕上面的像素點的顏色。
總結:
對於着色器的編程並不是很困難,完全是C的語法。但是debug比較麻煩,對於類型的約束也很嚴格,必須是相同類型的才能進行算術運算。我們理解了這兩個文件之后,就可以發揮我們的想象對紋理圖片做出各種的處理。
另有人總結:
vsh 負責搞定像素位置 ,填寫 gl_Posizion 變量,偶爾搞定一下點大小的問題,填寫 gl_PixelSize。
fsh 負責搞定像素外觀,填寫 gl_FragColor ,偶爾配套填寫另外一組變量。
他們都是一個像素運行一次的,也可能運行多次。為了“這一像素”而努力計算。
程序的版本是cocos2dx 3.0.vs 2012.
代碼如下:
1 #ifndef __GLTesting_H_
2 #define __GLTesting_H_
3
4 #include "cocos2d.h"
5
6 USING_NS_CC;
7
8 class GLTestingLayer : public cocos2d::Layer
9 {
10 public:
11 static cocos2d::Scene* createScene();
12 virtual bool init();
13 void menuCloseCallback(cocos2d::Ref* pSender);
14 CREATE_FUNC(GLTestingLayer);
15 void graySprite(Sprite * sprite);
16 virtual void visit( Renderer* renderer, const kmMat4 &parentTransform, bool parentTransformUpdated ) override;
17 void onDraw();
18 private:
19 CustomCommand _command;
20 };
21 #endif // ! __GLTesting_H_
1 #include "GLTestingLayer.h"
2
3 cocos2d::Scene* GLTestingLayer::createScene()
4 {
5 auto scene = Scene::create();
6 auto layer = GLTestingLayer::create();
7 scene->addChild(layer);
8 return scene;
9 }
10
11 bool GLTestingLayer::init()
12 {
13 if ( !Layer::init() )
14 {
15 return false;
16 }
17
18 this->setPosition(0,0);
19 Size visibleSize = Director::getInstance()->getVisibleSize();
20 auto sprite = Sprite::create("HelloWorld.png");
21 sprite->setAnchorPoint(Point(0.5, 0.5));
22 sprite->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2));
23 this->addChild(sprite);
24 graySprite(sprite);
25
26 this->setShaderProgram( ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_COLOR));
27
28 return true;
29 }
30
31 void GLTestingLayer::menuCloseCallback( cocos2d::Ref* pSender )
32 {
33
34 }
35
36 void GLTestingLayer::graySprite( Sprite * sprite )
37 {
38 if(sprite)
39 {
40 GLProgram * p = new GLProgram();
41 p->initWithFilenames("gray.vsh", "gray.fsh");
42 p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
43 p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);
44 p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
45 p->link();
46 p->updateUniforms();
47 sprite->setShaderProgram(p);
48 }
49
50 }
51
52 void GLTestingLayer::visit( Renderer* renderer, const kmMat4 &parentTransform, bool parentTransformUpdated )
53 {
54 Layer::visit( renderer, parentTransform, parentTransformUpdated );
55 _command.init(_globalZOrder);
56 _command.func = CC_CALLBACK_0(GLTestingLayer::onDraw,this);
57 Director::getInstance()->getRenderer()->addCommand(&_command);
58 }
59
60 void GLTestingLayer::onDraw()
61 {
62 auto glProgram = this->getShaderProgram();
63 glProgram->use();
64 glProgram->setUniformsForBuiltins();
65 glPointSize( 10.0f);
66 glColor4f( 0.0f,1.0f,0.0f,1.0f);
67 auto size = Director::getInstance()->getWinSize();
68
69 float vertercies[] = { 100,100,
70 200, 200,
71 300, 100 };
72 float color[] = { 1,0,0,1,
73 0,1,0,1,
74 0,0,1,1 };
75 GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR );
76 glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertercies );
77 glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT,GL_TRUE, 0, color);
78 // 繪制三角形
79 glDrawArrays(GL_TRIANGLES, 0 ,3 );
80 //通知cocos2d-x 的renderer,讓它在合適的時候調用這些OpenGL命令
81 CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,3);
82 CHECK_GL_ERROR_DEBUG();
83
84 }
內有利用shader使得sprite變灰的代碼。
下面是變灰所用到的vsh和fsh文件。
1 attribute vec4 a_position;
2 attribute vec2 a_texCoord;
3 attribute vec4 a_color;
4
5
6 varying vec4 v_fragmentColor;
7 varying vec2 v_texCoord;
8
9 void main()
10 {
11 gl_Position = CC_PMatrix * a_position;
12 v_fragmentColor = a_color;
13 v_texCoord = a_texCoord;
14 }
1 varying vec4 v_fragmentColor;
2 varying vec2 v_texCoord;
3 uniform sampler2D CC_Texture0;
4
5 void main()
6 {
7 vec4 v_orColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
8 float gray = dot(v_orColor.rgb, vec3(0.299, 0.587, 0.114));
9 gl_FragColor = vec4(gray, gray, gray, v_orColor.a);
10 }
http://www.58player.com/blog-2534-93050.html
使用例子:http://www.cnblogs.com/U-tansuo/p/quick2-25_shader.html

