const char *sphere_ao_bake_fs =
  "//////////////////////////////////////////////////////////////////////\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"
  "#version 400\n"
  "precision highp float;\n"
  "// the sphere center position: eye coords\n"
  "in vec3 v_pos;\n"
  "// the sphere radius\n"
  "in float v_radius;\n"
  "// stretched corner: [-1.x, 1.x] (see below)\n"
  "in vec2 v_corner;\n"
  "\n"
  "//\n"
  "// Uniforms\n"
  "//\n"
  "\n"
  "// the model-view matrix\n"
  "uniform mat4 u_modelView;\n"
  "// the orthographic projection matrix\n"
  "uniform mat4 u_projection;\n"
  "// depth texture sampler\n"
  "uniform sampler2D u_depthTex;\n"
  "// intensity = 1 / (number of light directions)\n"
  "uniform float u_intensity;\n"
  "\n"
  "out vec4 outColor;\n"
  "\n"
  "/**\n"
  " * Inverse gnomonic projection over octahedron unfloded into a square. This\n"
  " * inverse  projection goes from texture coordinates to the surface of the unit\n"
  " * sphere. Both the texture and unit sphere coordinates are in the range\n"
  " * [-1, 1].\n"
  " *\n"
  " * In practice, this function returns the normal vector in model coordinate\n"
  " * space. The z is inverted since going from clip coords to NDC inverts the\n"
  " * z axis.\n"
  " *\n"
  " * reference: Tarini et al. page 3, eq. (5)\n"
  " */\n"
  "vec3 textureToSphereSurfaceCoord(in vec2 coord)\n"
  "{\n"
  "  vec2 absCoord = abs(coord);\n"
  "  float h = 1.0 - absCoord.s - absCoord.t;\n"
  "  return (h >= 0.0) ? vec3(coord.st, -h) : vec3(sign(coord.st) * (1.0 - absCoord.ts), -h);\n"
  "}\n"
  "\n"
  "void main()\n"
  "{\n"
  "  // map texture coords to normal in model coords\n"
  "  vec3 N = textureToSphereSurfaceCoord(clamp(v_corner, -1.0, 1.0));\n"
  "\n"
  "  // model coords -> eye coords\n"
  "  N = normalize(vec3(u_modelView * vec4(N, 0.0)));\n"
  "\n"
  "  // add the normal xy components to the sphere eye coords\n"
  "  vec4 pos = vec4(v_pos, 1.0);\n"
  "  pos.xy += N.xy * v_radius;\n"
  "  // eye coord -> clip coords [-1, 1]\n"
  "  pos = u_projection * pos;\n"
  "  // clip coords -> [0, 1] for xy and [near, far] for z\n"
  "  pos.xy = (pos.xy + vec2(1.0, 1.0)) / 2.0;\n"
  "  pos.z = ((gl_DepthRange.diff * pos.z) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;\n"
  "\n"
  "  // compute angle between sphere surface and light direction\n"
  "  float cos_alpha = dot(N, vec3(0, 0, 1));\n"
  "\n"
  "  // since we are using flat impostors in the depth texture, cos_alpha needs to be positive\n"
  "  if (cos_alpha > 0.0 && texture(u_depthTex, pos.xy).r > pos.z) {\n"
  "    // the texel is visible from the light source\n"
  "    outColor = vec4(vec3(1.0, 1.0, 1.0) * cos_alpha * u_intensity, 1.0);\n"
  "  } else {\n"
  "    // texel not visible\n"
  "    outColor = vec4(0.0, 0.0, 0.0, 0.0);\n"
  "  }\n"
  "\n"
  "}\n"
  "\n";
