0
\$\begingroup\$

I'm currently working on a mobile game that ressembles a snake game ; what I wished to do was use my limited glsl knowledge to write a pixel shader that could efficiently draw a curve with a certain width between a set of a few 2D points.

This is what I managed on desktop using basic libgdx to draw on the whole screen with the shader, and for input.

via GIPHY

To do this, first, I find which point is closest to gl_FragCoord, then, with quite a bit of math, I calculate the equation for a curve between the half-point before that point, and the half-point after it, using this point as control point for the curve... (by half-point I mean the average between it and the point next or before it in the table)

I then use more math to calculate all the local minimums of the distance between the pixel and this curve ; check which are valid, and calculate the minimum distance between the fragment and the snake. Then I can easily check if the pixel is in the width of the snake (named W).

I hope I explained it well, because the code is not annotated at all. I was hoping people had any ideas what could make the difference between the desktop version of libgdx that works perfectly (higher up), and the android version, that does less well...


Original Video

uniform vec2 stuff;
uniform vec2 points[10];

vec4 fragColor;

vec3 cubicsolve(float a,float b,float c,float d);

void main() {
    float tailLength = stuff.y;


    fragColor = vec4(0.,0.,0.,1.);
    vec4 primColor = vec4(92./255.,158./255.,237./255.,1.);//5C9EED
    vec4 degColor = vec4(236./255.,135./255.,192./255.,1.);//EC87C0


    int pointNum = int(stuff.x);
    vec2 F = gl_FragCoord.xy;
    float W = 10.; // width of the snake


    degColor = mix(primColor, degColor, 1.- (F.x/1920.+F.y/1080.)/2.);


    vec2 alux = points[0]-F;
    float first = alux.x*alux.x+alux.y*alux.y;
    int key = 0;

    for(int i = 1; i<pointNum-1; i++) {
        vec2 aux = points[i]-F;
        float dist2 = aux.x*aux.x+aux.y*aux.y;
        if(dist2<first) {
            first=dist2;
            key = i;
        }
    }

    float tlim = 1.00;
    if(key > pointNum-3) tlim = tailLength;

    vec2 t1,mid,t2;
    if(key == 0) {
        t1 = points[0];
        mid = points[1];
        t2 = (points[1]+points[2])/2.;
    } else {
        t1 = (points[key]+points[key-1])/2.;
        mid = points[key];
        t2 = (points[key]+points[key+1])/2.;
    }

    vec2 a = (t1-2.*mid+t2);
    vec2 b = 2.*(mid-t2);
    vec2 c = t2;
    vec2 P;


    vec2 A = 4.*a*a;
    vec2 B = 6.*a*b;
    vec2 C = 2.*b*b+4.*a*(c-F);
    vec2 D = 2.*b*(c-F);

    vec3 get = cubicsolve(A.x+A.y, B.x+B.y, C.x+C.y, D.x+D.y);
    float x[3];
    x[0] = get.x;x[1] = get.y;x[2] = get.z;

    float minDist = 1000.;

    for(int i = 0; i<3; i++) {
        float xi = x[i];
        if(xi>=1.-tlim && xi<=1.) {
            //fragColor += vec4(.01,.01,.01,.01);
            vec2 temp = a*xi*xi+b*xi+c;
            temp = temp * (temp-2.*F) + F*F;
            float tempDist = temp.x+temp.y;
            if(tempDist < minDist) minDist = tempDist;
        }
    }
    if(true) {
        vec2 temp = a*(1.-tlim)*(1.-tlim)+b*(1.-tlim)+c;
        temp = temp * (temp-2.*F)+F*F;
        float tempsol = temp.x+temp.y;
        if(tempsol < minDist) minDist = tempsol;
        temp = a*1.*1.+b*1.+c;
        temp = temp * (temp-2.*F)+F*F;
        tempsol = temp.x+temp.y;
        if(tempsol < minDist) minDist = tempsol;
    }

    if(minDist<W*W) {
        fragColor = vec4(1.,1.,1.,1.);
    }





    vec2 At = points[0];

    if((At.x-F.x)*(At.x-F.x)+(At.y-F.y)*(At.y-F.y)<7.*7.) {
        fragColor = vec4(0.,0.,0.,0.);
    } else if((At.x-F.x)*(At.x-F.x)+(At.y-F.y)*(At.y-F.y)<12.*12.) {
        fragColor = vec4(1.,1.,1.,1.);
    }

    fragColor+=degColor;

    for(int i = 0; i<pointNum ; i++) {
        vec2 T = points[i];
        if((T.x-F.x)*(T.x-F.x)+(T.y-F.y)*(T.y-F.y)<4.*4.) {
            fragColor = vec4(1.,0.,0.,1.);
        }
    }

    gl_FragColor = fragColor;
}

vec3 cubicsolve(float a,float b,float c,float d) {
    vec3 x = vec3(-1.,-1.,-1.);

    if (a == 0.) {
        //fragColor = vec4(1.0,0.0,0.0,0.);
        if(b == 0.) {
            if(c == 0.) {

                return x;
            }
            x.x = -d/c;
            return x;
        }
        float delta = c*c+4.*b*d;
        if(delta>0.) {
            delta = sqrt(delta);
            x.x = (-c+delta)/(2.*b);
            x.y = (-c-delta)/(2.*b);
        } else if(delta == 0.) {
            x.x = -c/(2.*b);
        }
        return x;
    }

    if (d == 0.) {
        //fragColor = vec4(0.0,1.0,0.0,0.);
        x.x = 0.;
        float delta = b*b+4.*a*c;
        if(delta>0.) {
            delta = sqrt(delta);
            x.y = (-b+delta)/(2.*a);
            x.z = (-b-delta)/(2.*a);
        } else if(delta == 0.) {
            x.y = -b/(2.*a);
        }
        return x;
    }
    b /= a;
    c /= a;
    d /= a;
    float disc, q, r, dum1, s, t, term1, r13;
    q = (3.0*c - (b*b))/9.0;
    r = -(27.0*d) + b*(9.0*c - 2.0*(b*b));
    r /= 54.0;
    disc = q*q*q + r*r;
    x.x = 0.; //The first root is always real.
    term1 = (b/3.0);
    if (disc > 0.) { // one root real, two are complex
        s = r + sqrt(disc);
        s = ((s < 0.) ? -pow(-s, (1.0/3.0)) : pow(s, (1.0/3.0)));
        t = r - sqrt(disc);
        t = ((t < 0.) ? -pow(-t, (1.0/3.0)) : pow(t, (1.0/3.0)));
        x.x = -term1 + s + t;
        return x;
    } 
    // End if (disc > 0)
    // The remaining options are all real
    if (disc == 0.){ // All roots real, at least two are equal.
        r13 = ((r < 0.) ? -pow(-r,(1.0/3.0)) : pow(r,(1.0/3.0)));
        x.x = -term1 + 2.0*r13;
        x.y = -(r13 + term1);
        x.z = x.y;
        return x;
    } // End if (disc == 0)
    // Only option left is that all roots are real and unequal (to get here, q < 0)
    q = -q;
    dum1 = q*q*q;
    dum1 = acos(r/sqrt(dum1));
    r13 = 2.0*sqrt(q);
    x.x = -term1 + r13*cos(dum1/3.0);
    x.y = -term1 + r13*cos((dum1 + 2.0*3.14159265359)/3.0);
    x.z = -term1 + r13*cos((dum1 + 4.0*3.14159265359)/3.0);
    return x;
}
\$\endgroup\$
  • \$\begingroup\$ I'm pretty sure it's a bunch of floating point errors, but I have no idea what to do about it \$\endgroup\$ – Paul Boursin Jul 27 '16 at 21:12
  • \$\begingroup\$ Could be caused by precision issues. stackoverflow.com/questions/5366416/… \$\endgroup\$ – Adam Jul 27 '16 at 21:55
1
\$\begingroup\$

I found the solution, which is as obnoxious as opengl can get. While some time ago precision specifiers were still accepted but useless on desktop, for compatibility with opengl-es that still supports and needs these specifiers. So they decided to make it impossible to include a precision specifier on desktop glsl which means I spent two hours looking for a way to up the precision on mobile, but it wouldn't compile. I now need to make two different shaders : one for desktop, one for es.

The solution was to simply add the following at the very top of the code :

precision highp float;

while it still isn't desktop precision, it's enough for me.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.