7
\$\begingroup\$

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

I'm referring to something like this:

enter image description here

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.

\$\endgroup\$
3
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Feb 24, 2014 at 13:25
  • \$\begingroup\$ That would be great! I'll have a look at the forum you mentioned, let us know if you find out any reference, thanks! \$\endgroup\$ Commented Feb 25, 2014 at 8:30
  • \$\begingroup\$ @DAN why not move your edit to its own answer? \$\endgroup\$ Commented Mar 28, 2014 at 23:41

1 Answer 1

6
\$\begingroup\$

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:

enter image description here

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

\$\endgroup\$
4
  • 3
    \$\begingroup\$ "scale the object up slightly and render it completely black" won't work concave shape objects or objects with holes. \$\endgroup\$ Commented Feb 24, 2014 at 11:57
  • \$\begingroup\$ 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.. \$\endgroup\$ Commented Feb 24, 2014 at 12:09
  • 3
    \$\begingroup\$ 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. \$\endgroup\$ Commented Mar 26, 2014 at 19:37
  • \$\begingroup\$ @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. :) \$\endgroup\$ Commented Mar 28, 2014 at 23:51

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.