private static string MovC_(
     char letter,
     string target,
     uint value)
 {
     return($"{target} = {DlShaderGenerator.CToValue_(letter, value)};" +
            Environment.NewLine);
 }
        private int CreateFragmentShader_(
            int cycles,
            UnpackedCombiner combArg)
        {
            var newLine = '\n';

            // TODO: Shade might need to be an "in" instead?
            var fragmentHeaderLines =
                @"#version 400

            uniform float time;
            uniform vec3 cameraPosition;

            uniform float u_lightingEnabled;
            uniform float u_sphericalUvEnabled;
            uniform float u_linearUvEnabled;

            struct TextureParams {
              vec2 clamped;
              vec2 mirrored;

              vec2 maxUv;
            };

            uniform TextureParams textureParams0;
            uniform TextureParams textureParams1;

            uniform vec4 EnvColor;
            uniform vec4 PrimColor;
            uniform vec4 Blend;
            uniform float PrimColorL;

            uniform sampler2D texture0;
            uniform sampler2D texture1;

            in vec3 v_position;
            in vec2 v_uv0;
            in vec2 v_uv1;
            in vec3 v_normal;
            in vec3 v_projectedNormal;
            in vec4 v_shade;

            out vec4 outColor;" +
                newLine;

            fragmentHeaderLines += this.CreateUvMethods();

            // TODO: Allow changing light position.
            var fragmentMainLines =
                @"vec4 applyLighting(vec4 p_color) {
              vec3 ambientLightColor = vec3(1, 1, 1);
              vec3 diffuseLightColor = vec3(1, 1, 1);
              vec3 specularLightColor = vec3(1, 1, 1);

              // Ambient color
              float ambientLightStrength = .2;
              vec3 ambientColor = ambientLightStrength * ambientLightColor;

              // Diffuse color
              vec3 normal = v_normal;

              //vec3 lightPos = 10000 * vec3(1, 1, 1);
              //vec3 lightDir = normalize(lightPos - v_position);  
              vec3 lightDir = normalize(vec3(-1, 0, 0));

              float diff = max(dot(normal, lightDir), 0);
              //float diff = .5 + .5 * dot(normal, lightDir);
              vec3 diffuseColor = diff * diffuseLightColor;

              // Specular color
              float specularStrength = 0.5;
              
              vec3 mergedColor = ambientColor + diffuseColor;
              vec3 finalColor = mergedColor * p_color.rgb;

              return vec4(finalColor, p_color.a);
            }

            vec4 calculateCcmuxColor(void) {
              vec3 CCRegister_0;
              vec3 CCRegister_1;
              vec3 CCReg;
              float ACRegister_0;
              float ACRegister_1;
              float ACReg;

              vec3 projectedNormal = normalize(v_projectedNormal);
              vec2 uv0 = calculateUv(v_uv0, textureParams0, projectedNormal);
              vec2 uv1 = calculateUv(v_uv1, textureParams1, projectedNormal);

              vec4 Texel0 = texture(texture0, uv0);
              vec4 Texel1 = texture(texture1, uv1);" +
                newLine;

            for (var i = 0; i < cycles; ++i)
            {
                // Final color = (ColorA [base] - ColorB) * ColorC + ColorD
                fragmentMainLines +=
                    DlShaderGenerator.MovC_('a',
                                            "CCRegister_0",
                                            combArg.cA[i]);
                fragmentMainLines +=
                    DlShaderGenerator.MovC_('b',
                                            "CCRegister_1",
                                            combArg.cB[i]);
                fragmentMainLines += "CCRegister_0 = CCRegister_0 - CCRegister_1;" +
                                     newLine +
                                     newLine;

                fragmentMainLines +=
                    DlShaderGenerator.MovC_('c',
                                            "CCRegister_1",
                                            combArg.cC[i]);
                fragmentMainLines += "CCRegister_0 = CCRegister_0 * CCRegister_1;" +
                                     newLine;

                fragmentMainLines +=
                    DlShaderGenerator.MovC_('d',
                                            "CCRegister_1",
                                            combArg.cD[i]);
                fragmentMainLines += "CCRegister_0 = CCRegister_0 + CCRegister_1;" +
                                     newLine +
                                     newLine;


                fragmentMainLines +=
                    DlShaderGenerator.MovA_('a', "ACRegister_0", combArg.aA[i]) +
                    newLine;
                fragmentMainLines +=
                    DlShaderGenerator.MovA_('b', "ACRegister_1", combArg.aB[i]) +
                    newLine;
                fragmentMainLines +=
                    "ACRegister_0 = ACRegister_0 - ACRegister_1;" +
                    newLine;

                fragmentMainLines +=
                    DlShaderGenerator.MovA_('c', "ACRegister_1", combArg.aC[i]) +
                    newLine;
                fragmentMainLines +=
                    "ACRegister_0 = ACRegister_0 * ACRegister_1;" +
                    newLine;

                fragmentMainLines +=
                    DlShaderGenerator.MovA_('d', "ACRegister_1", combArg.aD[i]) +
                    newLine;
                fragmentMainLines += "ACRegister_0 = ACRegister_0 + ACRegister_1;" +
                                     newLine +
                                     newLine;

                fragmentMainLines += @"CCReg = CCRegister_0;
                               ACReg = ACRegister_0;" +
                                     newLine;
            }

            // TODO: Is this the right shadow calculation?
            fragmentMainLines +=
                @"return vec4(CCReg, ACReg);
          }

          void main(void) {
            vec4 ccmuxColor = calculateCcmuxColor();

            outColor = mix(ccmuxColor, applyLighting(ccmuxColor), u_lightingEnabled);
          }";

            var fragmentShaderLines = fragmentHeaderLines + fragmentMainLines;

            return(ShaderCompiler.Compile(Gl.GL_FRAGMENT_SHADER, fragmentShaderLines));
        }