const char *solid_first_fs =
  "//////////////////////////////////////////////////////////////////////\n"
  "//\n"
  "// First-stage screen-space fragment shader for the solid pipeline\n"
  "//\n"
  "// It offers ambient occlusion and edge detection capabilities.\n"
  "//\n"
  "//////////////////////////////////////////////////////////////////////\n"
  "\n"
  "#version 400\n"
  "precision highp float;\n"
  "\n"
  "//\n"
  "// Input\n"
  "//\n"
  "\n"
  "// texture coordinates\n"
  "in vec2 UV;\n"
  "\n"
  "out vec4 outColor;\n"
  "\n"
  "//\n"
  "// Uniforms\n"
  "//\n"
  "\n"
  "// RGB rendered texture\n"
  "uniform sampler2D inRGBTex;\n"
  "\n"
  "// RGB color for applying fog\n"
  "uniform float fogR;\n"
  "uniform float fogG;\n"
  "uniform float fogB;\n"
  "uniform float offset;\n"
  "\n"
  "// Depth rendered texture\n"
  "uniform sampler2D inDepthTex;\n"
  "// 1.0 if enabled, 0.0 if disabled\n"
  "uniform float inAoEnabled;\n"
  "// 0.0 if disabled\n"
  "uniform float inFogStrength;\n"
  "// Shadow strength for SSAO\n"
  "uniform float inAoStrength;\n"
  "// 1.0 if enabled, 0.0 if disabled\n"
  "uniform float inEdStrength;\n"
  "// amount of offset when zoom-in or zoom-out.\n"
  "uniform float uoffset;\n"
  "// Dof strength\n"
  "uniform float inDofStrength;\n"
  "// Dof position\n"
  "uniform float inDofPosition;\n"
  "// position for other molecules.\n"
  "uniform float inFogPosition;\n"
  "// Rendering surface dimensions, in pixels\n"
  "uniform float width, height;\n"
  "\n"
  "vec3 getNormalAt(vec2 normalUV)\n"
  "{\n"
  "  float xpos = texture(inDepthTex, normalUV + vec2(1.0 / width, 0.0)).x;\n"
  "  float xneg = texture(inDepthTex, normalUV - vec2(1.0 / width, 0.0)).x;\n"
  "  float ypos = texture(inDepthTex, normalUV + vec2(0.0, 1.0 / height)).x;\n"
  "  float yneg = texture(inDepthTex, normalUV - vec2(0.0, 1.0 / height)).x;\n"
  "  float xdelta = xpos - xneg;\n"
  "  float ydelta = ypos - yneg;\n"
  "  vec3 r = vec3(xdelta, ydelta, 1.0 / width + 1.0 / height);\n"
  "  return normalize(r);\n"
  "}\n"
  "\n"
  "vec3 getNormalNear(vec2 normalUV)\n"
  "{\n"
  "  float cent = texture(inDepthTex, normalUV).x;\n"
  "  float xpos = texture(inDepthTex, normalUV + vec2(1.0 / width, 0.0)).x;\n"
  "  float xneg = texture(inDepthTex, normalUV - vec2(1.0 / width, 0.0)).x;\n"
  "  float ypos = texture(inDepthTex, normalUV + vec2(0.0, 1.0 / height)).x;\n"
  "  float yneg = texture(inDepthTex, normalUV - vec2(0.0, 1.0 / height)).x;\n"
  "  float xposdelta = xpos - cent;\n"
  "  float xnegdelta = cent - xneg;\n"
  "  float yposdelta = ypos - cent;\n"
  "  float ynegdelta = cent - yneg;\n"
  "  float xdelta = abs(xposdelta) > abs(xnegdelta) ? xnegdelta : xposdelta;\n"
  "  float ydelta = abs(yposdelta) > abs(ynegdelta) ? ynegdelta : yposdelta;\n"
  "  vec3 r = vec3(xdelta, ydelta, 0.5 / width + 0.5 / height);\n"
  "  return normalize(r);\n"
  "}\n"
  "\n"
  "float lerp(float a, float b, float f)\n"
  "{\n"
  "    return a + f * (b - a);\n"
  "}\n"
  "\n"
  "float rand(vec2 co) {\n"
  "    return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);\n"
  "}\n"
  "\n"
  "float depthToZ(float depth) {\n"
  "    float eyeZ = ((height * 0.57735) / 2.0);\n"
  "    float near = 2.0;\n"
  "    float far = 8000.0;\n"
  "    float depthNormalized = 2.0 * depth - 1.0;\n"
  "    return 2.0 * near * far / (far + near - depthNormalized * (far - near));\n"
  "}\n"
  "\n"
  "float calcBlur(float z, float pixelScale) {\n"
  "    return clamp(abs(z - 39.0), 0.0, 0.5 * pixelScale);\n"
  "}\n"
  "\n"
  "vec4 applyBlur(vec2 texCoord) {\n"
  "    float pixelScale = max(width, height);\n"
  "    float origZ = depthToZ(texture(inDepthTex, texCoord).x);\n"
  "    float blurAmt = calcBlur(origZ, pixelScale);\n"
  "    // Skip blurring if the original depth is less than the threshold\n"
  "    if (origZ < uoffset * inDofPosition) {\n"
  "        return texture(inRGBTex, texCoord);\n"
  "    }\n"
  "    float total = 1.0;\n"
  "    vec4 color = texture(inRGBTex, texCoord);\n"
  "    for (int i = 0; i < 32; i++) {\n"
  "        float t = (float(i) / float(64));\n"
  "        float angle = (t * 4.0) * 6.28319;\n"
  "        float radius = (t * 2. - 1.);\n"
  "        angle += 1.0 * rand(gl_FragCoord.xy);\n"
  "        vec2 offset = (vec2(cos(angle), sin(angle)) * radius * 0.05 * inDofStrength) / pixelScale;\n"
  "        float z = depthToZ(texture(inDepthTex, texCoord + offset).x);\n"
  "        float sampleBlur = calcBlur(z, pixelScale);\n"
  "        float weight = 1.0 - smoothstep(0.0, 1.0, abs(z - origZ) / blurAmt);\n"
  "        vec4 texSample = texture(inRGBTex, texCoord+offset);\n"
  "        color += weight * texSample;\n"
  "        total += weight;\n"
  "}\n"
  "return color / total;\n"
  "}\n"
  "\n"
  "vec4 applyFog(vec2 texCoord) {\n"
  "    vec4 finalColor = mix(\n"
  "            texture(inRGBTex, texCoord),\n"
  "            vec4(vec3(fogR, fogG, fogB), 1.),\n"
  "            pow(texture(inDepthTex, texCoord.xy).r, uoffset * inFogPosition / 10.0)\n"
  "        ) + inFogStrength / 100.0;\n"
  "    return finalColor;\n"
  "}\n"
  "\n"
  "const vec2 SSAOkernel[16] = vec2[16](\n"
  "        vec2(0.072170, 0.081556),\n"
  "        vec2(-0.035126, 0.056701),\n"
  "        vec2(-0.034186, -0.083598),\n"
  "        vec2(-0.056102, -0.009235),\n"
  "        vec2(0.017487, -0.099822),\n"
  "        vec2(0.071065, 0.015921),\n"
  "        vec2(0.040950, 0.079834),\n"
  "        vec2(-0.087751, 0.065326),\n"
  "        vec2(0.061108, -0.025829),\n"
  "        vec2(0.081262, -0.025854),\n"
  "        vec2(-0.063816, 0.083857),\n"
  "        vec2(0.043747, -0.068586),\n"
  "        vec2(-0.089848, 0.049046),\n"
  "        vec2(-0.065370, 0.058761),\n"
  "        vec2(0.099581, -0.089322),\n"
  "        vec2(-0.032077, -0.042826)\n"
  "    );\n"
  "\n"
  "float computeSSAOLuminosity(vec3 normal)\n"
  "{\n"
  "  float totalOcclusion = 0.0;\n"
  "  float depth = texture(inDepthTex, UV).x;\n"
  "  float A = (width * UV.x + 10 * height * UV.y) * 2.0 * 3.14159265358979 * 5.0 / 16.0;\n"
  "  float S = sin(A);\n"
  "  float C = cos(A);\n"
  "  mat2 rotation = mat2(\n"
  "    C, -S,\n"
  "    S, C\n"
  "  );\n"
  "  for (int i = 0; i < 16; i++) {\n"
  "    vec2 samplePoint = rotation * SSAOkernel[i];\n"
  "    float occluderDepth = texture(inDepthTex, UV + samplePoint).x;\n"
  "    vec3 occluder = vec3(samplePoint.xy, depth - occluderDepth);\n"
  "    float d = length(occluder);\n"
  "    float occlusion = max(0.0, dot(normal, occluder)) * (1.0 / (1.0 + d));\n"
  "    totalOcclusion += occlusion;\n"
  "  }\n"
  "\n"
  "  return max(0.0, 1.2 - inAoStrength * totalOcclusion);\n"
  "}\n"
  "\n"
  "float computeEdgeLuminosity(vec3 normal)\n"
  "{\n"
  "    return max(0.0, pow(normal.z - 0.1, 1.0 / 3.0));\n"
  "}\n"
  "\n"
  "void main() {\n"
  "    float luminosity = 1.0;\n"
  "    vec4 color = texture(inRGBTex, UV);\n"
  "    vec4 finalColor = color; // Initialize finalColor with base color\n"
  "\n"
  "    // Compute luminosity based on Ambient Occlusion (AO) and Edge Detection\n"
  "    if (inAoEnabled != 0.0) {\n"
  "        luminosity *= max(1.2 * (1.0 - inAoEnabled), computeSSAOLuminosity(getNormalNear(UV)));\n"
  "    }\n"
  "    if (inEdStrength != 0.0) {\n"
  "        luminosity *= max(1.0 - inEdStrength, computeEdgeLuminosity(getNormalAt(UV)));\n"
  "    }\n"
  "\n"
  "    // Compute foggedColor if Fog is enabled\n"
  "    vec4 foggedColor = color;\n"
  "    if (inFogStrength != 0.0) {\n"
  "        foggedColor = applyFog(UV);\n"
  "    }\n"
  "\n"
  "    // Compute blurredColor if DOF is enabled\n"
  "    vec4 blurredColor = color;\n"
  "    if (inDofStrength != 0.0) {\n"
  "        blurredColor = applyBlur(UV);\n"
  "    }\n"
  "\n"
  "    // Determine finalColor based on enabled effects\n"
  "    if (inAoEnabled != 0.0 || inEdStrength != 0.0 || inDofStrength != 0.0) {\n"
  "        if (inFogStrength != 0.0 && inDofStrength != 0.0) {\n"
  "            // Both Fog and DOF are enabled\n"
  "            vec4 mixedColor = mix(foggedColor, blurredColor, 0.5);\n"
  "            finalColor = vec4(mixedColor.rgb * luminosity, mixedColor.a);\n"
  "        } else if (inFogStrength != 0.0) {\n"
  "            // Only Fog is enabled with ao/edge-detection\n"
  "            finalColor = vec4(foggedColor.rgb * luminosity, foggedColor.a);\n"
  "        } else if (inDofStrength != 0.0) {\n"
  "            // Only DOF is enabled with/without ao/edge\n"
  "            finalColor = vec4(blurredColor.rgb * luminosity, blurredColor.a);\n"
  "        } else {\n"
  "            // Only AO and/or Edge Detection are enabled\n"
  "            finalColor = vec4(color.rgb * luminosity, color.a);\n"
  "        }\n"
  "    } else {\n"
  "        // Neither AO, DOF, nor Edge Detection is enabled\n"
  "        if (inFogStrength != 0.0) {\n"
  "            // Only Fog is enabled\n"
  "            finalColor = foggedColor;\n"
  "        } else {\n"
  "            // No effects are enabled\n"
  "            finalColor = color;\n"
  "        }\n"
  "    }\n"
  "\n"
  "    // Set the final fragment color\n"
  "    outColor = finalColor;\n"
  "    gl_FragDepth = texture(inDepthTex, UV).x;\n"
  "}\n"
  "\n";
