Play with shaders – black and white – part 1

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

While developing my game ‘Ice Heroes’, i discovered the shaders with cocos2d. I won’t explain in detail what is it, there is tons of resources about it, but basically, it is a small program used to display an object (http://www.opengl.org/wiki/Shader).

If you have a look at the cocos2d sources, you can see that every node rendered on screen is using a default shader: kCCShader_PositionTextureColor. This program key is set in CCShaderCache.m in loadDefaultShaders. It is composed of a vertex shader (executed on every vertex), and a fragment shader (executed every pixel on screen) – (ccShader_PositionTextureColor_vert.h and ccShader_PositionTextureColor_frag.h). We will use these two files as a template, and see how to modify and use them in order to change the colors of a texture.

Create a new project and modify the HelloWorld.m in order to display a single sprite. Your init should looks like this:

-(id) init
{
	if( (self=[super init]))
        {
            CGSize size = [[CCDirector sharedDirector] winSize];
            CCSprite *sprite = [[CCSprite alloc] initWithFile:@"ice.png"];
            sprite.position = ccp(size.width/2,size.height/2);
            [self addChild:sprite];
	}
	return self;
}

Use the image you want instead of ice.png and don’t forget to add it to your project resources.

shaders part 1.1

Now let’s create and use our shader. We need 2 files:

blackAndWhite.fsh

precision lowp float;
varying vec2 v_texCoord;
uniform sampler2D u_texture;

void main()
{   
    gl_FragColor = texture2D(u_texture, v_texCoord);
}

blackAndWhite.vsh

attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
uniform mat4 u_MVPMatrix;

void main()

{

    gl_Position = u_MVPMatrix * a_position;
    v_texCoord = a_texCoord;

}

Don’t forget to add the 2 files in your bundle resources:

shaders part 1.2

Let’s see how to use them (note that it will display the sprite as before, with normal colors)…

In the init we can add, just before the addchild:

        sprite.shaderProgram = [[CCGLProgram alloc] initWithVertexShaderFilename:@"blackAndWhite.vsh" fragmentShaderFilename:@"blackAndWhite.fsh"];
        [sprite.shaderProgram addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position];
        [sprite.shaderProgram addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords];
        [sprite.shaderProgram addAttribute:kCCAttributeNameColor index:kCCVertexAttrib_Color];
        [sprite.shaderProgram link];
        [sprite.shaderProgram updateUniforms];
        [sprite.shaderProgram use];

What we did here:
– loaded our fragment/vertex shader files to the sprite shaderprogram, added the 3 attributes position/texcoord/color (we find them in the .fsh and .vsh file, that’s what we will modify later).

– link the shaders and use it.

At this stage, you can run again the project and check if you still see your image.

Now we want to have it black and white. Remember, we talked about the fragment shader (executed on every visible pixel), that’s here where we will make a small operation to change the pixel colors.

The color is a vec4 (r, g, b, a) which we have thanks to: texture2D(u_texture, v_texCoord);

The only thing we need is to change the r g b values with a simple black and white formula, and give it back to the gl_FragColor instead of the normal texture color. Change your blackAndWhite.fsh like this:

precision lowp float;
varying vec2 v_texCoord;
uniform sampler2D u_texture;

void main()

{

    vec4 normalColor = texture2D(u_texture, v_texCoord);
    float gray = 0.299*normalColor.r + 0.587*normalColor.g + 0.114*normalColor.b;
    gl_FragColor = vec4(gray, gray, gray, normalColor.a);

}

Now run your project and you should see your sprite in black and white:

shaders part 1.3

 

We can imagine a lots of cool effects only with colors, but of course we can create more advanced effects… What you can do is also create a way to change the shaders used for a sprite (for exemple how to easily use the blackAndWhite or the normal colors with a method call or a flag).

The CCShaderCache can store your shader with a key, which you can get back when you need.

You can store:

[[CCShaderCache sharedShaderCache] addProgram:program forKey:@”yourShaderKey”];

And access and use it:

sprite.shaderprogram =  [[CCShaderCache sharedShaderCache] programForKey:@”yourShaderKey”];

[sprite.shaderprogram use];

Next time i will show how to use a mask to create a spot like effect. It will introduce the use of uniforms and the use of a second texture in the shader.

Some links:

http://www.khronos.org/opengles/2_X/

and the very useful quick reference card:

http://www.khronos.org/opengles/sdk/docs/reference_cards/OpenGL-ES-2_0-Reference-card.pdf

and for the impatient, my shader project where you can see in action several shaders (black and white, spot light with mask, dynamic spot light, refraction shader (water ripple), and a bump mapping with light : https://github.com/tapouillo/cocos2d-playWithShaders

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>