1

I'm trying to draw a triangle using an OpenGL fragment shader.

I succeeded in drawing a circle but I have a problem with handling the equation/logic or the code to draw a triangle.

draw_triangle(vec2 v1 , vec2 v2, vec2 v3)

Here is the fragment shader:

#version 330 core

out vec4 frag_color;

void draw_circle(vec2 shift_val, int radius,int color)
{
    vec2 res = vec2(1280,720);
    vec2 norm_cord = gl_FragCoord.xy;
    float dist = length(norm_cord - (res*shift_val));
    if( dist < radius )
    {
        if( color ==1 )
            frag_color = vec4(1.0, 1.0, 1.0, 1.0);
            else
            frag_color = vec4(0.0, 0.0, 0.0, 1.0);
    }
}

void draw_triangle(vec2 v1 , vec2 v2, vec2 v3)
{
    vec2 res = vec2(1280,720)*vec2(0.58,0.4);
    vec2 v = vec2(gl_FragCoord.x,gl_FragCoord.y);

    float slope1 = abs((v1.y-v2.y)/(v1.x-v2.x)); //y2-y1/x2-x1
    float slope2 = abs((v2.y-v3.y)/(v2.x-v3.x)); //y2-y1/x2-x1
    float slope3 = abs((v1.y-v3.y)/(v1.x-v3.x)); //y2-y1/x2-x1

    float slope_ref1 = abs((v.y-v1.y)/(v.x-v1.x)); //y2-y1/x2-x1
    float slope_ref2 = abs((v.y-v2.y)/(v.x-v2.x)); //y2-y1/x2-x1
    float slope_ref3 = abs((v.y-v3.y)/(v.x-v3.x)); //y2-y1/x2-x1

    float slope_RES1 = abs((res.y-v1.y)/(res.x-v1.x)); //y2-y1/x2-x1
    float slope_RES2 = abs((res.y-v2.y)/(res.x-v2.x)); //y2-y1/x2-x1
    float slope_RES3 = abs((res.y-v3.y)/(res.x-v3.x)); //y2-y1/x2-x1

    if (slope_RES1 < slope1 )
    {
        if(slope_ref1 < slope1)// && slope_ref3 < slope2 )//slope_ref1 < slope1 &&
            frag_color = vec4(1.0, 0.0, 1.0, 1.0);
    }

    if (slope_RES2 > slope2)
        {
            if(slope_ref2 > slope2)
                frag_color = vec4(1.0, 0.5, 1.0, 1.0);
        }

    /*if (slope_RES3 < slope3)
        {
            if(slope_ref3 > slope3)
                frag_color = vec4(1.0, 0.0, 1.0, 1.0);
        }*/
}

// This is entry point of the fragment shader and it will be called for every fragment covered by the rasterized geometry
void main() {
    // Here we just output a constant color which is red (R=1, G=0, B=0, A=1)
    //frag_color = vec4(0.0, 0.0, 0.0, 1.0);
    draw_circle(vec2(0.5,0.5),100,1); //draws face of circle
    draw_circle(vec2(0.5,0.58),16,0); //draws eye (1 for white and anynumber for black)
    draw_triangle(vec2(0.5f,0.5f),vec2(-0.5,0.0f),vec2(0.5f,-0.5f));
}
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 3
    Please expand in detail about what you mean when you say you "have a problem". Give expected results and actual results. – Wyck Nov 05 '20 at 21:39
  • Related [Intersect a ray with a triangle in GLSL C++](https://stackoverflow.com/questions/59257678/intersect-a-ray-with-a-triangle-in-glsl-c/59261241#59261241) – Rabbid76 Nov 05 '20 at 21:56
  • 1
    See also [How to determine if a point is in a 2D triangle?](https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle) – Wyck Nov 05 '20 at 21:58

2 Answers2

2

To compute if a point is in a triangle using the same side technique, you need to test the candidate point against three lines to see which side of each line it is on. If it meets the sidedness test for all three lines, then it is inside the triangle.

The condition test will be C(0) && C(1) && C(2).

Where C(n) means: "Is the point on the correct side of edge n"

The condition "which side of the line AB is the point X" is typically checked by checking the sign of the cross product of AB × AX. You could, by convention, assign a winding order to your triangle, and always check that the sign of this cross product is positive. This, of course, depends on the winding order of the vertices of your triangle. (For example, clockwise vertices require a negative cross product, and counterclockwise vertices require a positive cross product. Choose whichever convention you like or is most convenient given the definition of your polygon.)

You can, alternatively, test using the barycentric technique.

See: this site for more details.

Wyck
  • 10,311
  • 6
  • 39
  • 60
  • This is a good answer. I will add that the cross product here is equivalent to the exterior product / wedge product `AB ∧ AX`, which has the advantage that it’s just 2D and doesn't require using 3D vectors. – Dietrich Epp Nov 05 '20 at 22:01
  • I followed your advice but I can't reach the expected output ``` vec2 v = vec2(gl_FragCoord.x,gl_FragCoord.y); vec2 AB = vec2(b.x-a.x,b.y-a.y); vec2 BC = vec2(c.x-b.x,c.y-b.y); vec2 CA = vec2(a.x-c.x,a.y-c.y); vec2 Av = vec2(v.x-a.x,v.y-a.y); vec2 Bv = vec2(v.x-b.x,v.y-b.y); vec2 Cv = vec2(v.x-c.x,v.y-c.y); float ax= AB.x * Av.y - AB.y * Av.x; float bx= BC.x * Bv.y - BC.y * Bv.x; float cx= CA.x * Cv.y - CA.y * Cv.x; if (ax<0 && bx<0 && cx<0) frag_color = vec4(0.0, 0.0, 0.0, 1.0); } ``` – George Maher Nov 06 '20 at 06:22
  • 1
    @GeorgeMaher in comments use single ` instead of ``` to encapsulate `code` so you have more chars left. Also beware there must be no spaces between code and ` or ``` otherwise it will not work which is the case of your last comment – Spektre Nov 06 '20 at 09:32
  • With shaders you have to work methodically with small changes and with lots of testing. Can you make it render just plain white? If so, then slowly make incremental changes to your shader until you find the piece you have done incorrectly. Are you getting compile errors? Do you have coordinate errors? What's not working for you? – Wyck Nov 06 '20 at 20:25
2

Hope you are rendering QUAD covering the view/screen...

The fragment shader friendly way of rendering triangle is to:

  1. compute barycentric s,t coordinates of fragment

    go for the matrix approach as you got mat3,vec3 in GLSL ...

  2. decide if it is inside or outside

    simply by testing s+t<=1.0

  3. then set output color or discard;

    however discard is not an option for you as you got more shapes...

So compute:

--------------------------------------------------------
| s |           | (p1.a - p0.a) , (p2.a - p0.a) , p0.a |   | p.a |
| t | = inverse | (p1.b - p0.b) , (p2.b - p0.b) , p0.b | * | p.b |
| 1 |           |       0       ,       0       ,   1  |   |  1  |
------------------------------------------------------------------
if (s+t<=1.0) set output color

You can also use the s,t for texturing (even procedural one).

Spektre
  • 49,595
  • 11
  • 110
  • 380