Fifty shaders of grey.

Probably the biggest leap from cocos2d-v1 to cocos2d-v2, is understanding shaders, and as is also is the most powerful aspect of graphics programming, I thought I would briefly walk you through the absolute minimum required, to get shaders working.

Everything displayed on the screen, must run through a shader. The shader consists of two steps. A vertex shader ( basically ) calculating geometry ( vertices ), and a fragment shader calculating actual pixels ( fragments ). Nothing can be displayed on the screen, without these two shaders present. As openGL is a state machine, it really only needs one set of shaders, but that would not be much fun, so cocos2d supports custom shaders for each and every node.

First, create a new cocos2d-v2 project, and add the following code last in HelloWorld init.

CCSprite* sprite = [ CCSprite spriteWithFile:@"iTunesArtwork" ];
sprite.position = ccp( 
  [ CCDirector sharedDirector ].winSize.width / 2, 
  [ CCDirector sharedDirector ].winSize.height / 2 );
[ self addChild:sprite ];

This will, surprisingly enough, display a large coconut at the centre of the screen.  Try it. After getting that to work, now, also add the following lines, to set up a custom shader for the sprite

sprite.shaderProgram = [ [ CCGLProgram alloc ]
  initWithVertexShaderByteArray:
    " \n\
    attribute vec4 a_position; \n\
    attribute vec2 a_texCoord; \n\
    \n\
    varying vec2 v_texCoord; \n\
    \n\
    void main( ) { \n\
      gl_Position = CC_MVPMatrix * a_position; \n\
      v_texCoord = a_texCoord; \n\
    } \n\
    "
  fragmentShaderByteArray:
    " \n\
    uniform sampler2D CC_Texture0; \n\
    \n\
    varying vec2 v_texCoord; \n\
    \n\
    void main( ) { \n\
      // old default shader \n\
      // gl_FragColor = texture2D( CC_Texture0, v_texCoord ); \n\
      // new greyscale shader \n\
      vec4 color = texture2D( CC_Texture0, v_texCoord ); \n\
      float grey = color.r * 0.212 + color.g * 0.715 + color.b * 0.072; \n\
      gl_FragColor = vec4( grey, grey, grey, color.a );  \n\
    } \n\
    "
  ];

[ sprite.shaderProgram 
  addAttribute:kCCAttributeNamePosition 
  index:kCCVertexAttrib_Position ];
[ sprite.shaderProgram 
  addAttribute:kCCAttributeNameTexCoord 
  index:kCCVertexAttrib_TexCoords ];
[ sprite.shaderProgram link ];
[ sprite.shaderProgram updateUniforms ];

Now, re-run the program, and watch the shader magic. The coconut is grey.

All the first \n\ code, is the shader code, added as a single string of chars. First the vertex shader, and then the fragment shader. This is the quick and dirty way to add shader code, but for large shaders it will quickly be a mess. In that case the shaders are added as separate files, but for clarity, I use the “simple” approach here.

The last 4 lines, is telling the shaders which data it will have to work with, and link it with openGL, so that it is usable. The data demonstrated here, is the absolute minimum required to draw a textured quad, namely vertex information, texture coordinates, and which texture to use.

I will not go into details with the shaders, there is plenty of information on the internet about this. A few things though, will help you understand the shader code.

Attributes are data supplied to a vertex shader, and which the vertex shader then interpolates. This could be vertex information.

Varying data are  data you want to share from your vertex shader, to your fragment shaders. If assigned from an attribute, they will be automatically interpolated.

Uniform data are constants ( data which does not change for each draw, like ex. a constant colour ), and can be used in both vertex- and fragment shaders.

Furthermore there are an array of pre defined data, like gl_Position, which acts like a varying, or gl_FragColor, which assigns the actual colour to the fragment.

If you look at the last 4 lines again. Notice that two of them sets up the attributes needed for the shader, and one sets up the uniforms. As long as you use standard attributes and uniforms ( normally colour, vertices, texture coordinates and texture ), this will be all you need.

If you want to progress from here, or ask questions please don’t do it here, but in the cocos2d forum http://www.cocos2d-iphone.org/forum/

Advertisements

3 responses to “Fifty shaders of grey.

  • Mark

    Thanks really clear!
    I suppose there is some problem in the shader though. The Link function returns “false”.

  • Mark

    I write here the error messages that I get :

    2013-04-16 10:22:59.112 Shaders[860:c07] cocos2d: ERROR: 0:8: Use of undeclared identifier 'CC_MVPMatrix'
    2013-04-16 10:22:59.112 Shaders[860:c07] cocos2d: ERROR: Failed to compile vertex shader
    2013-04-16 10:22:59.113 Shaders[860:c07] cocos2d: ERROR: 0:4: 'vec2' : declaration must include a precision qualifier for type
    ERROR: 0:10: Use of undeclared identifier 'v_texCoord'
    ERROR: 0:11: Use of undeclared identifier 'color'
    ERROR: 0:11: Use of undeclared identifier 'color'
    ERROR: 0:11: Use of undeclared identifier 'color'
    ERROR: 0:12: Use of undeclared identifier 'grey'
    ERROR: 0:12: Use of undeclared identifier 'grey'
    ERROR: 0:12: Use of undeclared identifier 'grey'
    ERROR: 0:12: Use of undeclared identifier 'color'
    2013-04-16 10:22:59.113 Shaders[860:c07] cocos2d: ERROR: Failed to compile fragment shader
    2013-04-16 10:22:59.114 Shaders[860:c07] cocos2d: ERROR: Failed to link program: 22

  • Mark

    It’s me again… I’m sorry it was a problem related with the Cocos2D version. CC_MVPMatrix has been added in 2.0.1.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: