title: Android OPEN GL ES 2 Fragment Shaders
body: <p>I have a problem with an Open GL ES 2 fragment shader and was wondering if anybody could help me.</p>
  
  <p>I'm using the shader to draw metaballs on the screen.  It's relatively simple and the code is as follows:</p>
  
  <pre><code>private static final String mFragmentShader = "precision mediump float;\n"
                                                + "uniform vec2 balls["
                                                + NUMBER_OF_BALLS
                                                + "];\n"
                                                + "float sqr(highp float x) { return x*x; }\n"
                                                + "void main() {\n"
                                                + "  vec4 pixelColor = vec4(0.0, 0.0, 0.0, 0.0);\n"
                                                + "  vec4 color = vec4(0.0, 0.5, 1.0, 1.0);\n"
                                                + "  for (int i=0; i&lt;"
                                                + NUMBER_OF_BALLS
                                                + "; ++i) {\n"
                                                + "    vec2 dist = balls[i] - gl_FragCoord.xy;\n"
                                                + "    float val = 100.0 / (sqr(dist.x) + sqr(dist.y));\n"
                                                + "    pixelColor += color * val;\n"
                                                + "  }\n"
                                                + "  highp float a = smoothstep(0.9, 1.0, pixelColor.a);\n"
                                                + "  gl_FragColor = vec4(pixelColor.rgb * a, 1.0);\n"
                                                + "}\n";
  </code></pre>
  
  <p>The shader compiles fine and works well when NUMBER_OF_BALLS is less than 15.  Unfortunately when number of balls is greater than 15, it renders as if the locations of all the balls are at position (0,0) (see <a href="http://www.suzannemartintutoring.co.uk/images/broken.png" rel="nofollow">here</a>).  I've checked the input to the shader and it is definitely right so there must be a problem with the shader itself.  Furthermore if I change the precision from mediump to highp then I can up the number of balls to 20 before the rendering becomes a problem.</p>
  
  <p>Can anyone tell me what I am doing wrong? </p>
  
  <p>Edit:  Here is the complete code just in case the problem is not wirth the fragment shader itself.</p>
  
  <pre><code>       private static final String mFragmentShader = "precision mediump float;\n"
                                                + "uniform vec2 balls["
                                                + NUMBER_OF_BALLS
                                                + "];\n"
                                                + "float sqr(highp float x) { return x*x; }\n"
                                                + "void main() {\n"
                                                + "  vec4 pixelColor = vec4(0.0, 0.0, 0.0, 0.0);\n"
                                                + "  vec4 color = vec4(0.0, 0.5, 1.0, 1.0);\n"
                                                + "  for (int i=0; i&lt;"
                                                + NUMBER_OF_BALLS
                                                + "; ++i) {\n"
                                                + "    vec2 dist = balls[i] - gl_FragCoord.xy;\n"
                                                + "    float val = 100.0 / (sqr(dist.x) + sqr(dist.y));\n"
                                                + "    pixelColor += color * val;\n"
                                                + "  }\n"
                                                + "  highp float a = smoothstep(0.9, 1.0, pixelColor.a);\n"
                                                + "  gl_FragColor = vec4(pixelColor.rgb * a, 1.0);\n"
                                                + "}\n";
  
  
  
  private static final String mVertexShader = "attribute vec4 vPosition;\n" + "void main() {\n"
                                              + "  gl_Position = vPosition;\n"
                                              + "}\n";
  
  int gProgram;
  int gvPositionHandle;
  
  private float[][] balls = new float[NUMBER_OF_BALLS][2];
  
  int[] lBalls = new int[NUMBER_OF_BALLS];
  
  private static final int FLOAT_SIZE_BYTES = 4;
  
  private final float[] mQuadVerticesData = { 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, };
  
  private FloatBuffer mQuadVertices;
  
  public MetaballRenderer() {
      mQuadVertices = ByteBuffer.allocateDirect(mQuadVerticesData.length * FLOAT_SIZE_BYTES)
                                .order(ByteOrder.nativeOrder())
                                .asFloatBuffer();
      mQuadVertices.put(mQuadVerticesData).position(0);
  }
  
  private int loadShader(int shaderType, String source) {
      int shader = GLES20.glCreateShader(shaderType);
      if (shader != 0) {
          GLES20.glShaderSource(shader, source);
          GLES20.glCompileShader(shader);
          int[] compiled = new int[1];
          GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
          if (compiled[0] == 0) {
              Log.e(TAG, "Could not compile shader " + shaderType + ":");
              Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
              GLES20.glDeleteShader(shader);
              shader = 0;
          }
      }
      return shader;
  }
  
  private int createProgram(String vertexSource, String fragmentSource) {
      int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
      if (vertexShader == 0) {
          return 0;
      }
  
      int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
      if (pixelShader == 0) {
          return 0;
      }
  
      int program = GLES20.glCreateProgram();
      if (program != 0) {
          GLES20.glAttachShader(program, vertexShader);
          checkGlError("glAttachShader");
          GLES20.glAttachShader(program, pixelShader);
          checkGlError("glAttachShader");
          GLES20.glLinkProgram(program);
          int[] linkStatus = new int[1];
          GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
          if (linkStatus[0] != GLES20.GL_TRUE) {
              Log.e(TAG, "Could not link program: ");
              Log.e(TAG, GLES20.glGetProgramInfoLog(program));
              GLES20.glDeleteProgram(program);
              program = 0;
          }
      }
      return program;
  }
  
  
  private void init_balls() {
      for (int i = 0; i &lt; NUMBER_OF_BALLS; ++i) {
          balls[i][0] = 200 + rand.nextInt(50);
          balls[i][1] = 200 + rand.nextInt(50);
      }
  }
  
  private void checkGlError(String op) {
      int error;
      while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
          Log.e(TAG, op + ": glError " + error);
          throw new RuntimeException(op + ": glError " + error);
      }
  }
  
  @Override
  public void onDrawFrame(GL10 arg0) {
  
      GLES20.glUseProgram(gProgram);
      checkGlError("glUseProgram");
  
      for (int i = 0; i &lt; NUMBER_OF_BALLS; ++i) {
          GLES20.glUniform2fv(i, 1, balls[i], 0);
          checkGlError("glUniform2fv");
      }
  
      GLES20.glVertexAttribPointer(gvPositionHandle, 2, GLES20.GL_FLOAT, false, 0, mQuadVertices);
      checkGlError("glVertexAttribPointer");
      GLES20.glEnableVertexAttribArray(gvPositionHandle);
      checkGlError("glEnableVertexAttribArray");
  
      GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
      checkGlError("glDrawArrays");
  
      GLES20.glUseProgram(0);
      checkGlError("glUseProgram");
  
  }
  
  @Override
  public void onSurfaceChanged(GL10 glUnused, int width, int height) {
  
      // Ignore the passed-in GL10 interface, and use the GLES20
      // class's static methods instead.
      gProgram = createProgram(mVertexShader, mFragmentShader);
      if (gProgram == 0) {
          return;
      }
  
      gvPositionHandle = GLES20.glGetAttribLocation(gProgram, "vPosition");
      checkGlError("glGetAttribLocation");
      init_balls();
      for (int i = 0; i &lt; NUMBER_OF_BALLS; ++i) {
          lBalls[i] = GLES20.glGetUniformLocation(gProgram, "balls[" + i + "]");
          checkGlError("glGetUniformLocation");
      }
  
      GLES20.glViewport(0, 0, width, height);
      checkGlError("glViewport");
  
  }
  
  @Override
  public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
  
  }
  </code></pre>
  
  <p>}</p>