Adding outline to sprite

I’ve found in tests an outline shader, but I’ve got no idea how to use it. This class in tests is so confusing and this is my first attempt to use shaders.

Somewhere I found this:

const GLchar *fragmentSource = FileUtils::getInstance()->getStringFromFile("outline_shader.fsh").c_str();
GLProgram* p = GLProgram::createWithByteArrays(ccPositionTextureA8Color_vert, fragmentSource);

p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);
p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
p->link();
p->updateUniforms();
sprite->setGLProgram(p);

But first of all I’m getting bunch of errors:

Error linking shader program: 'ERROR: No definition of main in fragment shader

And also:

cocos2d: warning: Uniform not found: u_outlineColor
cocos2d: warning: Uniform not found: u_radius
cocos2d: warning: Uniform not found: u_threshold

In tests I also found this:

Vec3 color(1.0f, 0.2f, 0.3f);
GLfloat radius = 0.01f;
GLfloat threshold = 1.75;

GLProgramState* _glprogramstate = GLProgramState::getOrCreateWithGLProgram(p);

_glprogramstate->setUniformVec3("u_outlineColor", color);
_glprogramstate->setUniformFloat("u_radius", radius);
_glprogramstate->setUniformFloat("u_threshold", threshold);

But I don’t know how to combine these two.

I tried this:

    sprite->setGLProgramState(_glprogramstate);

But then I’m getting bad access error.

I just want to dynamically add/remove outline and it’s parameters.

And here’s shader code (untouched by me):

/*
  Created by guanghui on 4/8/14.
http://www.idevgames.com/forums/thread-3010.html
*/

varying vec2 v_texCoord;
varying vec4 v_fragmentColor;

uniform vec3 u_outlineColor;
uniform float u_threshold;
uniform float u_radius;

void main()
{
    float radius = u_radius;
    vec4 accum = vec4(0.0);
    vec4 normal = vec4(0.0);
    
    normal = texture2D(CC_Texture0, vec2(v_texCoord.x, v_texCoord.y));
    
    accum += texture2D(CC_Texture0, vec2(v_texCoord.x - radius, v_texCoord.y - radius));
    accum += texture2D(CC_Texture0, vec2(v_texCoord.x + radius, v_texCoord.y - radius));
    accum += texture2D(CC_Texture0, vec2(v_texCoord.x + radius, v_texCoord.y + radius));
    accum += texture2D(CC_Texture0, vec2(v_texCoord.x - radius, v_texCoord.y + radius));
    
    accum *= u_threshold;
    accum.rgb =  u_outlineColor * accum.a;
    accum.a = 1.0;
    
    normal = ( accum * (1.0 - normal.a)) + (normal * normal.a);
    
    gl_FragColor = v_fragmentColor * normal;
}

You’re really close, use GLProgramState instead of GLProgram

const GLchar *fragmentSource = FileUtils::getInstance()->getStringFromFile("outline_shader.fsh").c_str();
GLProgram* p = GLProgram::createWithByteArrays(ccPositionTextureA8Color_vert, fragmentSource);

GLProgramState* _glprogramstate = GLProgramState::getOrCreateWithGLProgram(p);

_glprogramstate->setUniformVec3("u_outlineColor", color);
_glprogramstate->setUniformFloat("u_radius", radius);
_glprogramstate->setUniformFloat("u_threshold", threshold);

 sprite->setGLProgramState(_glprogramstate);

If you have a bad access error, can you post a screenshot?

Okay now I can see something working, but in bad way. However I’m not getting any errors.

Before:

After:

As you can see it got a lot smaller and also it’s misplaced by quite a distance.

Hmm, You would need to tweak the shader to make it work as you expected, It’s probably a good idea to change the shader base on the default sprite shader.

Hey, check this pr out
This should fix the rectangular problem
https://github.com/cocos2d/cocos2d-x/pull/10425

Also you said the sprite become smaller, it’s because you need to use a different vertex shader.

Screw this. I’m confused a bit: it was working in tests now it’s not and generating error: OpenGL error 0x0502 :slight_smile: Gotta use few sprites to make outlines, because it’ll be better for performance anyway.