const char *sphere_ao_bake_vs =
  "//////////////////////////////////////////////////////////////////////\n"
  "//\n"
  "// Ambient occlusion shader for sphere impostors\n"
  "//\n"
  "// This fragment shader is used for baking the ambient occlusion\n"
  "// maps.\n"
  "//\n"
  "//////////////////////////////////////////////////////////////////////\n"
  "\n"
  "//\n"
  "// Input\n"
  "//\n"
  "\n"
  "#version 400\n"
  "precision highp float;\n"
  "// sphere center position: model coords\n"
  "in vec3 a_pos;\n"
  "// corner: [-radius, radius]\n"
  "in vec2 a_corner;\n"
  "// offset for the center of the sphere's AO map texture tile\n"
  "in vec2 a_tileOffset;\n"
  "\n"
  "//\n"
  "// Output\n"
  "//\n"
  "\n"
  "// the sphere center position: eye coords\n"
  "out vec3 v_pos;\n"
  "// the sphere radius\n"
  "out float v_radius;\n"
  "// stretched corner: [-1.x, 1.x] (see below)\n"
  "out vec2 v_corner;\n"
  "\n"
  "//\n"
  "// Uniforms\n"
  "//\n"
  "\n"
  "// the model-view matrix\n"
  "uniform mat4 u_modelView;\n"
  "// the size of the AO texture [pixels] (e.g. 1024)\n"
  "uniform float u_textureSize;\n"
  "// the size of a single tile in texture coords [0, 1]\n"
  "uniform float u_tileSize;\n"
  "\n"
  "void main()\n"
  "{\n"
  "  // pass through radius\n"
  "  v_radius = abs(a_corner.s);\n"
  "  // position: model coords -> eye coords\n"
  "  v_pos = vec3(u_modelView * vec4(a_pos, 1.0));\n"
  "\n"
  "  // normalize corner: [-radius, radius] -> [-1, 1]\n"
  "  vec2 corner = a_corner / v_radius;\n"
  "  // enlarge texture space to trim half a texel from the tile\n"
  "  // note: v_corner is in range [-1, 1] so we add 2 / (tile size in pixels)\n"
  "  v_corner = corner * (1.0 + 2.0 / (u_textureSize * u_tileSize));\n"
  "\n"
  "  // NDC are in range [-1, 1], the  * 2 - 1  translates and scales the position to [0, 1]\n"
  "  gl_Position = vec4(a_tileOffset * 2.0 - vec2(1.0) + corner * u_tileSize, 0.0, 1.0);\n"
  "}\n"
  "\n";
