My task is to render quadratic Bezier curve (path) via Stage3d (Adobe Flash) technology, which have no any extensions for that drawing out-of-the box (while OpenGl have it, as I know). Yea, there is a Starling-Extension-Graphics, but it uses simple method to divide a curve segment to many straight lines, that generates very many triangles for my long curve path.
So.. There is a perfect way for rendering resolution independed shapes for Loop and Blinn. I've read GPUGems3 article (gpugems3_ch25.html) and ported that fragment shader to AGAL2:
Quadratic Curve Pixel Shader
float4 QuadraticPS(float2 p : TEXCOORD0,
float4 color : COLOR0) : COLOR
{
// Gradients
float2 px = ddx(p);
float2 py = ddy(p);
// Chain rule
float fx = (2*p.x)*px.x - px.y;
float fy = (2*p.x)*py.x - py.y;
// Signed distance
float sd = (p.x*p.x - p.y)/sqrt(fx*fx + fy*fy);
// Linear alpha
float alpha = thickness - abs(sd);
if (alpha > 1) // Inside
color.a = 1;
else if (alpha < 0) // Outside
clip(-1);
else
// Near boundary
color.a = alpha;
return color;
}
It works. But there are two fundamental problems:
I don't understand that algorithm :(. I read about signed distance field, derivates and other... I thought a lot of hours and read again - but no result! My question is: Does anyone help me and explain what happens in that shader (line by line (!), if it possible)?The second problem is that the curve is clipped at the corner of triangle and has a variable thickness. Please look at the picture: https://monosnap.com/file/84EBOuQ1czNM5lprQ5VlnRUKP2mKmW So if I draw a path it looks like this: https://monosnap.com/file/54Zs5Xui6s3BL6lNdZRCx9ibcV2bCF
I like that method by using one triangle per curve segment, because no any geometry needed. And I don't need to have very thick curves (1-2 px is excellent), but a variable thickness is a problem. Can anybody help me?
(Sorry for my English. It is not my native language.)
[edit1 by Spektre] just moved from comment and invalid answer
I plan to use one triangle per one curve segment, something like on the picture
- the path consists from many triangles
- one per one path segment (quadratic curve)
- How to handle issue with this approach if all control points are collinear (lie on the same straight) or nearly collinear?
For 3 control point Bezier curves I would:
enlarge control points to include area around curve to avoid artifacts
This way is fast and there is no problem to compute
A',B',C'
fromA,B,C
and vice versa. If the scale is constant (for examplescale=1.25
) then the max usable curvethickness<=2.0*min(|control_point-M|)*(scale-1.0)
.For safer enlargement you can compute exact scale needed (for example in geometry shader) and pass it to vertex and fragment ... All of above can be done by Geometry shader. You should use transparency to correctly join the curves together. The average middle point should stay the same
M=A+B+C=A'+B'+C'
if transparency is not an option
Then you need to change the approach so pass control points and position inside textures.
create one 2D
float
texture with control pointsfloat pnt[9][N]
pnt[0,1,2][]
is control pointA(x,y,z)
pnt[3,4,5][]
is control pointB(x,y,z)
pnt[6,7,8][]
is control pointC(x,y,z)
also create 1D color texture
rgba col[N]
x
axis resolution of both textures =N
is the number of Bezier curvesnow draw single Quad covering entire screen
And inside fragment shader check if pixel is inside any of the curve. If yes output its color ...
This can get very slow for high Bezier curve count
N
[edit1] almost collinear control points
for those I would use Quads
D,E
are mirrored pointsA,B
aroundC
D=C+C-A
E=C+C-B
C
is the middle pointM = (A+B+D+E)/4 = C = (A'+B'+C'+D')/4
A',B',C',D'
are enlargedA,B,D,E
control pointsA'=C+(A -C)*scale
B'=C+(B -C)*scale
A =C+(A'-C)/scale
B =C+(B'-C)/scale
This can be used for any Bezier not just almost collinear but it uses larger polygons so it will be slower on performance (more fragments then really needed)