1
votes

I am drawing circles/ellipses in WebGL using a single Quad and a fragment shader, in order to draw them in a resolution independent manner (Edge distance anti-aliasing)

Here is my fragment shader currently:

'#extension GL_OES_standard_derivatives : enable',
'precision mediump float;',
'varying vec2 coord;',
'vec4 circleColor = vec4(1.0, 0.5, 0.0, 1.0);',
'vec4 outlineColor = vec4(0.0, 0.0, 0.0, 1.0);',

'uniform float strokeWidth;',          
'float outerEdgeCenter = 0.5 - strokeWidth;',

'void main(void){',

      'float dx = 0.5 - coord.x;',
      'float dy = 0.5 - coord.y;',
      'float distance = sqrt(dx*dx + dy*dy);',

      'float delta = fwidth(distance);',
      'float alpha = 1.0 - smoothstep(0.45 - delta, 0.45, distance);',
      'float stroke = 1.0 - smoothstep(outerEdgeCenter - delta, outerEdgeCenter + delta, distance);',

      'gl_FragColor = vec4( mix(outlineColor.rgb, circleColor.rgb, stroke), alpha );',                  

'}'

This creates an orange circle with a black outline that is perfectly antialiased whatever the size.

enter image description here

However, as soon as I transform the quad (scale it) in order to turn the circle into an ellipse, the distance calculation transforms along with it, causing the outline to also scale. My understanding is that I would somehow need to account for the quad's transform by inverting it.

enter image description here

What I would like is for the distance to remain uniform even when the quad is transformed, in effect producing a constant width outline around the whole circle/ellipse.

Any Help would be greatly appreciated.

1

1 Answers

2
votes

The problem is, your fragment shader (FS) is kind of blackbox now (becausethe the code is lack of information).

Your FS is written to work within square space. So it always render circle in space where x and y are same size ( -1.0; 1.0 interval).

While quad is transformed outside (in VS or anywhere else) and there is no way how to reflect that transformation in FS yet.

To solve the problem, I suggest to push an additional information into FS about the scaling. Something like Shadertoy provides in shader inputs:

uniform vec3 iResolution; // viewport resolution (in pixels)

except this wont be the resolution of the screen size, but the information about quad transformation, so something like:

varying vec2 trans;
// where value (1.0, 1.0) mean no transformation

Then you can use this value to calculate different stroke. Instead of inverting the transformation for the current stroke, I would rather calculate unique dx and dy values for it.

There are more ways to achieve the working solution and it depends on how do you want to use it later (what kinds of transformations should be possible etc.). So I present only the basic but the easiest solution.