Making an arbitrary polygon look darker towards the edges

I am writing an application which displays 2d polygons. My application is in Java and uses jme3 / LWJGL at the higher levels but I don’t think that changes much, my question is at the GL level.

The 2d polygons have dozens of vertices and the shape of the polygon, including the number and position of vertices, changes a little with each rendered frame. The polygons cannot be assumed to be convex, but they are contiguous, don’t have holes, and the path defining each polygon does not ever cross itself.

Each frame, I’m breaking each polygon down to a set of triangles using a 3rd party java library. Then I’m sending these triangles to OpenGL to be rendered.

Currently I’m displaying each polygon with just one unshaded color.

What I want is to smoothly darken that color towards the edge of the polygon. That is, the closer a pixel is to the nearest point on the boundary of the polygon, the darker it should be, according to some formula relating the distance with the darkness.

I don’t know what this kind of effect is called so I’ve struggled to find any design ideas on the web. I’m looking for any pointers or links to achieving this effect, in general terms. Even “this is known as the XYZ problem”. Or any general ideas as to how I would achieve this.

Thanks

Have you found a solution to this problem yet?

No. Any suggestions you can think of would be much appreciated!

I believe what you are looking for is called pillow shading. You could do it by making an alpha-only copy of the polygon, blur it, and then render the original polygon again using a shader that reads the blurred texture intensity and multiplies it by the polygon color.

Hello,

For visual effect, you can:

  • [1] Use a Stencil Buffer (and stencil operations) and render your polygons and mark them (for example 1 for 'inside" polygon)
  • [2] Render in a target your edges
  • [3] Blur this RT [2]
  • [4] Inverse the RT [3]
  • [5] Use the StencilBuffer [1] to apply result of your RT [4] and mix the color result (darker towers the edges)

you can adjust :

  • [1] : the size width of your rendering edges
  • [3] : the filter size (of your blur filter)

This rendering can do the trick … i think ^^

Good luck,
YoOOYOOooOO

Thanks for those quick responses. That’s certainly given me some ideas.

[QUOTE=yoyonel;1252392]Hello,

For visual effect, you can:

  • [1] Use a Stencil Buffer (and stencil operations) and render your polygons and mark them (for example 1 for 'inside" polygon)
  • [2] Render in a target your edges
  • [3] Blur this RT [2]
  • [4] Inverse the RT [3]
  • [5] Use the StencilBuffer [1] to apply result of your RT [4] and mix the color result (darker towers the edges)

you can adjust :

  • [1] : the size width of your rendering edges
  • [3] : the filter size (of your blur filter)

This rendering can do the trick … i think ^^

Good luck,
YoOOYOOooOO[/QUOTE]

A simple way =>
Use a shader (pixel shader, fragment shader) when you’re rendering your triangle to compute the distance between the pixel/point and the “base”-edge of your triangle.
Point-Line distance is a simple computation, you only need to know the “good” edge for each triangle, and use this distance to smooth your render color.

YoYo

[QUOTE=yoyonel;1252395]A simple way =>
Use a shader (pixel shader, fragment shader) when you’re rendering your triangle to compute the distance between the pixel/point and the “base”-edge of your triangle.
Point-Line distance is a simple computation, you only need to know the “good” edge for each triangle, and use this distance to smooth your render color.

YoYo[/QUOTE]

Thanks yoyo!

This seems like the best method. For each triangle I can pass to the shader a subset of the polygon boundary edges. The shader can then compute the minimum length vector between the current pixel location and one of these polygon boundary edges. It can then use this vector to compute the color / alpha values or look them up in a texture.

As a potential optimization, in my application (i.e. outside the shader code) I can narrow down the boundary edges to a subset which includes only those edges which are the closest edge to some pixel in the triangle.

Sound like a good idea !
Please send your result :slight_smile:

The code below addresses your problem using Classic GL (fixed pipeline) and some math routines which are included. It carefully extrudes the poly edges inward making quads, then uses simple color interpolation to fade the quads from inner to outer edge. Source code and figure are included. There are some Gouraud shading artifacts. Plus I’ve only tested it for this fairly simple polygon. I leave it up to you to do more testing. Not sure how contorted your polys are. This algorithm assumes the vertices of a poly are listed in counterclockwise order. To get the effect I think you want, draw the polys twice: 1) use your technique to draw the poly filled with solid color, 2) draw again with my technique to fade the edges. Let me know if it works for you!

[ATTACH=CONFIG]1064[/ATTACH]



//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\

#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <gl.h>         // The GL Header File  
#include <glut.h>       // The GL Utility Toolkit (Glut) Header

float hh = 0.1;

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//----------------------------------------   vector_sum   ------------------------------------------
 
void vector_sum (float a[3], float b[3], float c[3])
{
    c[0] = a[0] + b[0];
    c[1] = a[1] + b[1];
    c[2] = a[2] + b[2];
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//----------------------------------------   vector_dif   ------------------------------------------
  
void vector_dif (float a[3], float b[3], float c[3])
{
    c[0] = a[0] - b[0];
    c[1] = a[1] - b[1];
    c[2] = a[2] - b[2];
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//-----------------------------------------   vec_mag   --------------------------------------------

  // Return the magnitude of a 3D vector.

float vec_mag (float v[3])
{
    return ((float)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]));
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//---------------------------------------   vector_angle   -----------------------------------------

         //  Return the angle between two vectors in radians.

float vector_angle (float a[3], float b[3])
{
    float  num, den;

    num = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
    den = vec_mag(a) * vec_mag(b);

    return ((float)acos(num/den));
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//---------------------------------------   unit_vector   ------------------------------------------

void unit_vector (float v[3])
{
    float mag;

    mag = (float)sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);

    v[0] = v[0] / mag;
    v[1] = v[1] / mag;
    v[2] = v[2] / mag;
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//----------------------------------------   vec_scale   -------------------------------------------

     //  Vector scale function.

void vec_scale (float vec[3], float s)
{
    vec[0] *= s;
    vec[1] *= s;
    vec[2] *= s;
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//----------------------------------------   vec_cross   -------------------------------------------

    //   Cross product of two 3D vectors

void vec_cross (float a[3], float b[3], float c[3])
{
    c[0] = a[1] * b[2]  -  a[2] * b[1];
    c[1] = a[2] * b[0]  -  a[0] * b[2];
    c[2] = a[0] * b[1]  -  a[1] * b[0];
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//-----------------------------------------   Keyboard   -------------------------------------------

void Keyboard (unsigned char key, int x, int y)
{
    switch (key)  {

       case 'x':   hh += 0.05;   break;
       case 'z':   hh -= 0.05;   break;

       case  27:     exit (0);   break;     // Quit program with 'esc' key.

       default :  printf ("   Keyboard -> key = %d, key = %c
", key, key);   break;
    }

    glutPostRedisplay ();
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//------------------------------------------   Display   -------------------------------------------

void Display (void)
{
	int v, i, j;
	float a[3], b[3], c[3], goly[5][3], x, alfa;

	static short first = 1, convex[5];
	static float poly[5][3] = {{-1.0,-2.0},{1.0,-2.0},{0.0,0.5},{0.0,3.0},{-3.0,0.4}};


	// Convex = 1 -> convex vertex, convex = 0 -> concave vertex.

	if (first)  {
	   first = 0;
	   for (v = 0; v < 6; v++)  {
	       i = (v - 1) % 5;
	       j = (v + 1) % 5;
               vector_dif (poly[v], poly[i], a);
               vector_dif (poly[j], poly[v], b);
               vec_cross  (a, b, c);
               if (c[2] > 0.0)  convex[v] = 1;
               else             convex[v] = 0;
	   }
	}

	glLoadIdentity ();
	glClear (GL_COLOR_BUFFER_BIT);


	// Extrude lines to get quads of thickness 'hh'.
	// 'hh' can be interactively changed via the 'x' and 'z' keys.

	for (v = 0; v < 5; v++)  {

	   i =  (v + 4) % 5;
	   j =  (v + 1) % 5;

	   vector_dif (poly[i], poly[v], a);
	   vector_dif (poly[j], poly[v], b);

	   unit_vector (a);
	   unit_vector (b);

	   vector_sum (a, b, c);
           unit_vector (c);
	   alfa = 0.5 * vector_angle (a,b);
	   x = hh / sin (alfa);
	   vec_scale (c, x);
	   if (convex[v]) vector_sum (poly[v], c, goly[v]);
	   else           vector_dif (poly[v], c, goly[v]);
	}

	glBegin (GL_QUADS);
           for (v = 0; v < 5; v++)  {
	      j =  (v + 1) % 5;
	      glColor3f (0.2, 0.1, 0.2);
              glVertex3fv (poly[v]); glVertex3fv (poly[j]);
	      glColor3f (0.4, 0.9, 0.4);
	      glVertex3fv (goly[j]); glVertex3fv (goly[v]);
	   }
	glEnd ();

    glutSwapBuffers ();
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//------------------------------------------   Init_GL   -------------------------------------------

void Init_GL (void)
{
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho (-5.0,5.0, -5.0,5.0, -5.0,5.0);
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

    glClearColor (0.2, 0.1, 0.2, 1.0);
}

//---+----4----+----3----+----2----+----1----+---/\---+----1----+----2----+----3----+----4----+---\\
//-------------------------------------------   main   ---------------------------------------------

void main (int argc, char** argv) 
{
    glutInit (&argc, argv);

    glutInitDisplayMode    (GLUT_RGB | GLUT_DOUBLE); 
    glutInitWindowSize     (800, 800); 
    glutInitWindowPosition (350, 100);
    glutCreateWindow       ("Pillow Fill - Carmine");

    glutKeyboardFunc (Keyboard);
    glutDisplayFunc  ( Display);

    Init_GL();

    glutMainLoop ();
}