Go Back

bezier.geom

// Geometry Shader to render a bezier curve as a triangular mesh, from a triangular mesh

#version 330 core

uniform int detail;
uniform float thickness;

uniform mat4 world_to_screen_transform;

//Input is a triangle in world space.
//Vertices 0 and 2 are end points of the line
//Vertex 1 is the control point
//All z are assumed to be the same
//All normals will point to z+ because this is for 2D things
layout (triangles) in;
layout (triangle_strip, max_vertices = 100) out;

//Description of the per-vertex output of the shader
//Each vertex has texture coordinates 
out vec2 TexCoord;
out vec3 WorldPos;

//Calculate a position in 3d space along a bezier curve with a single control point
//v[0] and v[2] are the end points of the curve
//v[1] is a control point that the line will curve towards
//t is the parameter from 0 to 1 to describe the position on the curve
vec2 BezierPosition( vec2 v[3], float t )
{
    vec2 p;
    float OneMinusT = 1.0 - t;
    float b0 = OneMinusT * OneMinusT;
    float b1 = 2.0 * OneMinusT * t;
    float b2 = t * t;
    return b0*v[0] + b1*v[1] + b2*v[2];
}

void main()
{
  vec2 positions[3];

  //Take the 2d xy components of the given posisions from input
  positions[0] = gl_in[0].gl_Position.xy;
  positions[1] = gl_in[1].gl_Position.xy;
  positions[2] = gl_in[2].gl_Position.xy;

  float OneOverDetail = 1.0 / float((detail) - 1.0);

  vec2 prevp = positions[0];
  vec2 p = BezierPosition(positions, 0);
  vec2 nextp = BezierPosition(positions, OneOverDetail);

  for(int i = 0; i < (detail) - 1; i++)
  {
    float t = i * OneOverDetail;
    float nextnextt = (i + 2) * OneOverDetail;

    if(i + 2 >= detail) nextnextt = (i + 1) * OneOverDetail;

    vec2 nextnextp = BezierPosition(positions, nextnextt);

    vec2 parallel = nextp - prevp;
    parallel = normalize(parallel);
    vec2 perp = vec2(-parallel.y, parallel.x);
    perp = (world_to_screen_transform * vec4((thickness / 2) * perp, 0, 0)).xy;

    gl_Position = vec4(p + (perp), gl_in[0].gl_Position.z, gl_in[0].gl_Position.w);
    TexCoord = vec2(0, 1);
    WorldPos = vec3(p + (perp), gl_in[0].gl_Position.z);
    EmitVertex();
    gl_Position = vec4(p - (perp), gl_in[0].gl_Position.z, gl_in[0].gl_Position.w);
    TexCoord = vec2(0, 0);
    WorldPos = vec3(p - (perp), gl_in[0].gl_Position.z);
    EmitVertex();

    parallel = nextnextp - p;
    parallel = normalize(parallel);
    perp = vec2(-parallel.y, parallel.x);
    perp = (world_to_screen_transform * vec4((thickness / 2) * perp, 0, 0)).xy;

    gl_Position = vec4(nextp + (perp), gl_in[0].gl_Position.z, gl_in[0].gl_Position.w);
    TexCoord = vec2(1, 1);
    WorldPos = vec3(nextp + (perp), gl_in[0].gl_Position.z);
    EmitVertex();
    gl_Position = vec4(nextp - (perp), gl_in[0].gl_Position.z, gl_in[0].gl_Position.w);
    TexCoord = vec2(1, 0);
    WorldPos = vec3(nextp - (perp), gl_in[0].gl_Position.z);
    EmitVertex();

    //Need to emit a quad every 4 vertices but only can do 2 at a time
    EndPrimitive();

    prevp = p;
    p = nextp;
    nextp = nextnextp;
  }
}