const char *sphere_ao_render_fs =
  "//#define CONTOUR_LINES\n"
  "//#define TOON_SHADING\n"
  "#version 400\n"
  "precision highp float; \n"
  "// normalized corner\n"
  "in vec2 v_corner;\n"
  "// color\n"
  "in vec3 v_color;\n"
  "// position in eye-coordinate space\n"
  "in vec4 v_eyePos;\n"
  "// sphere radius\n"
  "in float v_radius;\n"
  "// AO tile offset\n"
  "in vec2 v_tileOffset;\n"
  "\n"
  "out vec4 outColor;\n"
  "// inverse model-view matrix\n"
  "uniform mat4 u_modelView;\n"
  "uniform mat3 u_invModelView;\n"
  "// the projection matrix\n"
  "uniform mat4 u_projection;\n"
  "// the texture sampler\n"
  "uniform sampler2D u_tex;\n"
  "uniform float u_texScale;\n"
  "\n"
  "#ifdef CONTOUR_LINES\n"
  "const float contourWidth = 0.3;\n"
  "#endif\n"
  "\n"
  "#ifdef TOON_SHADING\n"
  "const float levels = 4.0;\n"
  "#endif\n"
  "\n"
  "vec2 sphereSurfaceToTextureCoord(in vec3 coord)\n"
  "{\n"
  "  vec3 absCoord = abs(coord);\n"
  "  float d = absCoord.x + absCoord.y + absCoord.z;\n"
  "  return (coord.z <= 0.0) ? coord.xy / d : sign(coord.xy) * (1.0 - absCoord.yx / d);\n"
  "}\n"
  "\n"
  "float cosine(in vec3 a, in vec3 b)\n"
  "{\n"
  "  float cos_alpha = max(0.0, dot(a, b));\n"
  "#ifdef TOON_SHADING\n"
  "  cos_alpha = floor(cos_alpha * levels) / levels;\n"
  "#endif\n"
  "  return cos_alpha;\n"
  "}\n"
  "\n"
  "void main()\n"
  "{\n"
  "  // figure out if we are inside our sphere\n"
  "  float zz = 1.0 - v_corner.x * v_corner.x - v_corner.y * v_corner.y;\n"
  "#ifdef CONTOUR_LINES\n"
  "  if (zz <= -sqrt(contourWidth)) {\n"
  "    // fragment outside sphere + contour radius\n"
  "    discard;\n"
  "  }\n"
  "  if (zz <= 0.0) {\n"
  "    // fragment is part of the contour\n"
  "    float xi = abs(zz); // [0, contourWidth]\n"
  "    // eta determines how much the contours are pushed back\n"
  "    float eta = 0.0004;\n"
  "    outColor = vec4(0.0, 0.0, 0.0, 1.0); // black\n"
  "    gl_FragDepth = gl_FragCoord.z - 10e-10 + eta * xi;\n"
  "    return;\n"
  "  }\n"
  "#else\n"
  "  if (zz <= 0.0)\n"
  "    discard;\n"
  "#endif\n"
  "\n"
  "  // compute normal in eye coods\n"
  "  vec3 N = normalize(vec3(v_corner, sqrt(zz)));\n"
  "\n"
  "  // compute the pixel's depth\n"
  "  vec4 pos = v_eyePos;\n"
  "  pos.z += N.z * v_radius; // radius is 1.0\n"
  "  pos = u_projection * pos;\n"
  "  gl_FragDepth = (pos.z / pos.w + 1.0) / 2.0;\n"
  "\n"
  "  // transform to normal to model-space\n"
  "  vec3 modelN = N;\n"
  "  modelN = normalize(u_invModelView * modelN);\n"
  "  // determine (u, v) texture coordinates using gnomonic projection\n"
  "  vec2 uv = sphereSurfaceToTextureCoord(modelN); // [-1, 1]\n"
  "\n"
  "\n"
  "  uv = v_tileOffset + uv * u_texScale;\n"
  "\n"
  "  //gl_FragColor = vec4(v_color, 1.0) * texture2D(u_tex, uv);\n"
  "\n"
  "  // direction of light source\n"
  "  vec3 L = normalize(vec3(0, 1, 1));\n"
  "  // eye direction\n"
  "  vec3 E = vec3(0, 0, 1);\n"
  "\n"
  "  // angle between normal and light direction\n"
  "  float cos_alpha = cosine(N, L);\n"
  "\n"
  "  // compute ambient color\n"
  "  vec3 ambient = 0.4 * v_color;\n"
  "  // compute diffuse color\n"
  "  vec3 diffuse = 0.55 * v_color * cos_alpha;\n"
  "  // compute specular color (approximate Fresnel reflection)\n"
  "  vec3 H = normalize(L + E); // halfway vector between N and E\n"
  "  float cos_beta = cosine(N, H);\n"
  "  vec3 specular = 0.5 * (vec3(1, 1, 1) - v_color)* pow(cos_beta, 20.0);\n"
  "\n"
  "  // final color\n"
  "  vec3 color = ambient + diffuse + specular;\n"
  "  outColor = 1.2 * vec4(color, 1.0) * texture(u_tex, uv); // AO + Phong reflection [+ contours]\n"
  "  //gl_FragColor = vec4(color, 1.0); // Phong reflection [+ contours]\n"
  "  //gl_FragColor = 1.2 * texture2D(u_tex, uv); // AO [+ contours]\n"
  "  //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); // contours + white atoms\n"
  "}\n"
  "\n";
