How to outline a 3D object with a cartoon style stroke? [duplicate]

How could I draw (stroke) the outline of a 3D object in a cartoon style with GLKit?

I'm referring to something like this:

However we can start with a simple line around the object and then try to improve the stroke with a shader.

I'm drawing the object with this code:

glBindVertexArrayOES(_vertexArray);
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0)


As I'm not very familial with GLKit: all the solutions I've found make use of OpenGL APIs not supported by OpenGL ES.

• There's direct shaders for this. I'm at work and cannot link you directly, but try searching around. If it helps, the last implementations I saw revolved around the Ogre3D forums.
– user39686
Feb 24 '14 at 13:25
• That would be great! I'll have a look at the forum you mentioned, let us know if you find out any reference, thanks!
– DAN
Feb 25 '14 at 8:30
• @DAN why not move your edit to its own answer? Mar 28 '14 at 23:41

I'm not too familiar with what features aren't supported by OpenGL ES but the way I see it you have a couple options.

A quick and easy way to get a black outline effect around an object is to scale the object up slightly and render it completely black. You can then render the regular version of the model again.

Another way would be to use an edge detection post processing effect.

Also you could use N dot V to determine how much of the surface is visible. The below link has an example on that.

http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter09.html

EDIT BY DAN:

This is a solution developed thanks to the answer and comments below. As suggested I've slightly scaled the object up, rendered it completely black (only back faces) and finally rendered the regular version of the model again.

Here's the result:

This is the drawing method:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClear(GL_COLOR_BUFFER_BIT);

// Set matrices
[self setupMatrices];

// Positions
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, cubePositions);

// Texels
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 0, cubeTexels);

// Normals
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 0, cubeNormals);

// Save the current modelview matrix in a temporary variable
GLKMatrix4 mat = self.effect.transform.modelviewMatrix;
self.effect.transform.modelviewMatrix = GLKMatrix4Scale(self.effect.transform.modelviewMatrix, 1.02, 1.02, 1.02);

glDisable(GL_CULL_FACE);
glCullFace(GL_BACK);

// Set the material to black
self.effect.material.ambientColor = GLKVector4Make(0.0, 0.0, 0.0, 1.0);
self.effect.material.diffuseColor = GLKVector4Make(0.0, 0.0, 0.0, 1.0);

// Prepare effect
[self.effect prepareToDraw];

// Draw the outline
glDrawArrays(GL_TRIANGLES, 0, cubeVertices);

// Restore the correct material color
self.effect.material.ambientColor = GLKVector4Make(251.0/255.0, 95.0/255.0, 95.0/255.0, 1.0);
self.effect.material.diffuseColor = GLKVector4Make(251.0/255.0, 95.0/255.0, 95.0/255.0, 1.0);

// Prepare effect
[self.effect prepareToDraw];
glEnable(GL_CULL_FACE);

// Restore the modeview matrix
self.effect.transform.modelviewMatrix = mat;

// Prepare effect
[self.effect prepareToDraw];

// Draw the object
glDrawArrays(GL_TRIANGLES, 0, cubeVertices);

}


Before scaling the object I save the modelview matrix in a temp variable, which I restore to draw the object in the last step. Any hint to improve the code is really appreciated!

Thanks a lot guys for your help.

Cheers, DAN

• "scale the object up slightly and render it completely black" won't work concave shape objects or objects with holes. Feb 24 '14 at 11:57
• My only object is a 3D Cube, it could work; I'm trying to write some code to do it in order to understand whether I can obtain a cartoon style with this solution..
– DAN
Feb 24 '14 at 12:09
• Rather than strictly scaling the object up (with the issues Krom Stern points out), you can push all vertices out a short distance along their normals, and render only back faces. This can generate internal lines at the edges of concavities. Special attention needs to be paid at smoothing splits though, or you'll get a gap in your lines. The example image above appears to use this technique (see the telltale sharp points at the armpits) - it's quick and dirty and good for thick lines, but not as controllable as using screenspace effects. Mar 26 '14 at 19:37
• @DMGregory Actually I think it is more controllable than post-process outlines because it (the outline mesh) could be done in a 3d modelling program, tweaked by the artist (for example they could remove the artifacts at smoothing splits) then exported with the model with reverse windig of geometry. :) Mar 28 '14 at 23:51