/**
   * Helper function to compile and link a program.
   *
   * @param vertexShaderHandle An OpenGL handle to an already-compiled vertex shader.
   * @param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader.
   * @param attributes Attributes that need to be bound to the program.
   * @return An OpenGL handle to the program.
   */
  private int createAndLinkProgram(
      final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) {
    int programHandle = GLES20.glCreateProgram();

    if (programHandle != 0) {
      // Bind the vertex shader to the program.
      GLES20.glAttachShader(programHandle, vertexShaderHandle);

      // Bind the fragment shader to the program.
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);

      // Bind attributes
      if (attributes != null) {
        final int size = attributes.length;
        for (int i = 0; i < size; i++) {
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
        }
      }

      // Link the two shaders together into a program.
      GLES20.glLinkProgram(programHandle);

      // Get the link status.
      final int[] linkStatus = new int[1];
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);

      // If the link failed, delete the program.
      if (linkStatus[0] == 0) {
        GLES20.glDeleteProgram(programHandle);
        programHandle = 0;
      }
    }

    if (programHandle == 0) {
      throw new RuntimeException("Error creating program.");
    }

    return programHandle;
  }
Exemple #2
0
 @Override
 public void glBindAttribLocation(int program, int index, String name) {
   GLES20.glBindAttribLocation(program, index, name);
 }
  public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // Set the background frame color
    GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);

    // Set the viewport size.
    GLES20.glViewport(0, 0, 2444, 2444);

    // instantiate the vertex and frag shaders
    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    // Create program handle to vertex and frag shaders
    mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
    GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
    GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
    GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables
    GLES20.glBindAttribLocation(mProgram, 0, "vPosition");
    GLES20.glBindAttribLocation(mProgram, 1, "a_TexCoordinate");

    // Creating and loaded the vertex buffer and texture buffer
    initBuffers();

    // Load the texture
    // gl.glEnable(GL10.GL_TEXTURE_2D);
    AssetManager am = mcontext.getAssets();
    try {
      InputStream is = am.open("jalal_strawberry_2444x2444.png");

      Bitmap bitmap = BitmapFactory.decodeStream(is);
      // Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.android);
      // ByteBuffer imageBuffer = ByteBuffer.allocateDirect(bitmap.getByteCount()); //one float size
      // is 4 bytes
      // imageBuffer.order(ByteOrder.nativeOrder()); //byte order must be native
      // bitmap.copyPixelsToBuffer(imageBuffer);

      // generate one texture pointer
      gl.glGenTextures(1, textures, 0);
      // ...and bind it to our array
      gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

      // create nearest filtered texture
      gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
      gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

      // Use Android GLUtils to specify a two-dimensional texture image from our bitmap
      // GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
      // (target, level, internalformat, width, height, border, format, type, pixels)
      System.out.println("width is " + bitmap.getWidth() + " and height is " + bitmap.getHeight());
      System.out.println("max texture size is " + GLES10.GL_MAX_TEXTURE_SIZE);
      ByteArrayOutputStream os = new ByteArrayOutputStream();
      bitmap.compress(Bitmap.CompressFormat.WEBP, 1, os);
      byte[] array = os.toByteArray();
      // ByteBuffer imageBuffer = ByteBuffer.allocateDirect(array.length);
      // imageBuffer.order(ByteOrder.nativeOrder());
      // imageBuffer.put(array);
      Bitmap compressedImage = BitmapFactory.decodeByteArray(array, 0, array.length);
      // GL_PALETTE8_RGBA8_OES
      // GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0, GLES10.GL_RGBA, bitmap.getWidth(),
      // bitmap.getHeight(), 0, GLES10.GL_RGBA, GLES10.GL_UNSIGNED_BYTE, imageBuffer);
      GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, compressedImage, 0);

      // Clean up
      bitmap.recycle();
      if (textures[0] == 0) {
        throw new RuntimeException("Error loading texture.");
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
Exemple #4
0
  @Override
  public boolean initObject() {
    if (imageResId != 0) {
      vertexCount = coordinates.length / COORDS_PER_VERTEX;
      ByteBuffer buff = ByteBuffer.allocateDirect(coordinates.length * 4);
      buff.order(ByteOrder.nativeOrder());
      vBuffer = buff.asFloatBuffer();
      vBuffer.put(coordinates);
      vBuffer.position(0);

      ByteBuffer drawBuff = ByteBuffer.allocateDirect(drawOrder.length * 2);
      drawBuff.order(ByteOrder.nativeOrder());
      drawBuffer = drawBuff.asShortBuffer();
      drawBuffer.put(drawOrder);
      drawBuffer.position(0);

      ByteBuffer textBuff = ByteBuffer.allocateDirect(textureCoordinates.length * 4);
      textBuff.order(ByteOrder.nativeOrder());
      textureBuffer = textBuff.asFloatBuffer();
      textureBuffer.put(textureCoordinates);
      textureBuffer.position(0);

      // Prepare shaders and OpenGL program
      vertexShaderHandle = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
      fragShaderHandle = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

      programHandle = GLES20.glCreateProgram(); // create empty OpenGL Program
      GLES20.glAttachShader(programHandle, vertexShaderHandle); // add the vertex shader to program
      GLES20.glAttachShader(programHandle, fragShaderHandle); // add the fragment shader to program

      GLES20.glBindAttribLocation(programHandle, 0, "aPosition");
      GLES20.glBindAttribLocation(programHandle, 1, "aTexCoordinate");

      GLES20.glLinkProgram(programHandle); // create OpenGL program executables

      final int[] status = new int[1];
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, status, 0);

      if (status[0] == 0) {
        Log.e(
            "GLSprite",
            "Error compiling OpenGL program: " + GLES20.glGetProgramInfoLog(programHandle));
        GLES20.glDeleteProgram(programHandle);
        programHandle = 0;
        throw new RuntimeException("Error creating OpenGL program.");
      }

      textureDataHandle = GLUtil.loadTexture(mCanvas.getView().getContext(), imageResId);

      // get handle to vertex shader's aPosition member
      positionHandle = GLES20.glGetAttribLocation(programHandle, "aPosition");
      textureCoordinateHandle = GLES20.glGetAttribLocation(programHandle, "aTexCoordinate");
      mModelMatrixHandle = GLES20.glGetUniformLocation(programHandle, "uMVMatrix");
      GLES20Renderer.checkGLError("glUniformLocation uMVMatrix");
      mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "uMVPMatrix");
      GLES20Renderer.checkGLError("glUniformLocation uMVPMatrix");
      textureUniformHandle = GLES20.glGetUniformLocation(programHandle, "u_Texture");
      GLES20Renderer.checkGLError("glUniformLocation u_Texture");

      GLES20.glEnable(GLES20.GL_BLEND);

      GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

      initialized = true;
      return true;
    } else {
      return false;
    }
  }
  @Override
  public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {

    SurfaceCreated(glUnused, config);
    // Set the background clear color to gray.
    GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);

    // Position the eye behind the origin.
    final float eyeX = 0.0f;
    final float eyeY = 0.0f;
    final float eyeZ = 1.5f;

    // We are looking toward the distance
    final float lookX = 0.0f;
    final float lookY = 0.0f;
    final float lookZ = -5.0f;

    // Set our up vector. This is where our head would be pointing were we holding the camera.
    final float upX = 0.0f;
    final float upY = 1.0f;
    final float upZ = 0.0f;

    // Set the view matrix. This matrix can be said to represent the camera position.
    // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
    // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
    Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);

    final String vertexShader =
        "uniform mat4 u_MVPMatrix;      \n" // A constant representing the combined
            // model/view/projection matrix.
            + "attribute vec4 a_Position;     \n" // Per-vertex position information we will pass
            // in.
            + "attribute vec4 a_Color;        \n" // Per-vertex color information we will pass in.
            + "varying vec4 v_Color;          \n" // This will be passed into the fragment shader.
            + "void main()                    \n" // The entry point for our vertex shader.
            + "{                              \n"
            + "   v_Color = a_Color;          \n" // Pass the color through to the fragment shader.
            // It will be interpolated across the triangle.
            + "   gl_Position = u_MVPMatrix   \n" // gl_Position is a special variable used to store
            // the final position.
            + "               * a_Position;   \n" // Multiply the vertex by the matrix to get the
            // final point in
            + "}                              \n"; // normalized screen coordinates.

    final String fragmentShader =
        "precision mediump float;       \n" // Set the default precision to medium. We don't need as
            // high of a
            // precision in the fragment shader.
            + "varying vec4 v_Color;          \n" // This is the color from the vertex shader
            // interpolated across the
            // triangle per fragment.
            + "void main()                    \n" // The entry point for our fragment shader.
            + "{                              \n"
            + "   gl_FragColor = v_Color;     \n" // Pass the color directly through the pipeline.
            + "}                              \n";

    // Load in the vertex shader.
    int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

    if (vertexShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(vertexShaderHandle, vertexShader);

      // Compile the shader.
      GLES20.glCompileShader(vertexShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(vertexShaderHandle);
        vertexShaderHandle = 0;
      }
    }

    if (vertexShaderHandle == 0) {
      throw new RuntimeException("Error creating vertex shader.");
    }

    // Load in the fragment shader shader.
    int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

    if (fragmentShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);

      // Compile the shader.
      GLES20.glCompileShader(fragmentShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(fragmentShaderHandle);
        fragmentShaderHandle = 0;
      }
    }

    if (fragmentShaderHandle == 0) {
      throw new RuntimeException("Error creating fragment shader.");
    }

    // Create a program object and store the handle to it.
    int programHandle = GLES20.glCreateProgram();

    if (programHandle != 0) {
      // Bind the vertex shader to the program.
      GLES20.glAttachShader(programHandle, vertexShaderHandle);

      // Bind the fragment shader to the program.
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);

      // Bind attributes
      GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
      GLES20.glBindAttribLocation(programHandle, 1, "a_Color");

      // Link the two shaders together into a program.
      GLES20.glLinkProgram(programHandle);

      // Get the link status.
      final int[] linkStatus = new int[1];
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);

      // If the link failed, delete the program.
      if (linkStatus[0] == 0) {
        GLES20.glDeleteProgram(programHandle);
        programHandle = 0;
      }
    }

    if (programHandle == 0) {
      throw new RuntimeException("Error creating program.");
    }

    // Set program handles. These will later be used to pass in values to the program.
    mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
    mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
    mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");

    // Tell OpenGL to use this program when rendering.
    GLES20.glUseProgram(programHandle);
  }
  /** Method for creating the most basic of shaders. */
  private void createShaders() {
    final String vertexShader =
        "uniform mat4 u_MVPMatrix;      \n"
            + "attribute vec4 a_Position;     \n"
            + "attribute vec4 a_Color;        \n"
            + "varying vec4 v_Color;          \n"
            + "void main()                    \n"
            + "{                              \n"
            + "   v_Color = a_Color;          \n"
            + "   gl_Position = u_MVPMatrix   \n"
            + "               * a_Position;   \n"
            + "}                              \n";

    final String fragmentShader =
        "precision mediump float;       \n"
            + "varying vec4 v_Color;          \n"
            + "void main()                    \n"
            + "{                              \n"
            + "   gl_FragColor = v_Color;     \n"
            + "}                              \n";

    // Load in the vertex shader.
    int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

    if (vertexShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(vertexShaderHandle, vertexShader);

      // Compile the shader.
      GLES20.glCompileShader(vertexShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(vertexShaderHandle);
        vertexShaderHandle = 0;
      }
    }

    if (vertexShaderHandle == 0) {
      throw new RuntimeException("Error creating vertex shader.");
    }

    // Load in the fragment shader shader.
    int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

    if (fragmentShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);

      // Compile the shader.
      GLES20.glCompileShader(fragmentShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(fragmentShaderHandle);
        fragmentShaderHandle = 0;
      }
    }

    if (fragmentShaderHandle == 0) {
      throw new RuntimeException("Error creating fragment shader.");
    }

    // Create a program object and store the handle to it.
    int programHandle = GLES20.glCreateProgram();

    if (programHandle != 0) {
      // Bind the vertex shader to the program.
      GLES20.glAttachShader(programHandle, vertexShaderHandle);

      // Bind the fragment shader to the program.
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);

      // Bind attributes
      GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
      GLES20.glBindAttribLocation(programHandle, 1, "a_Color");

      // Link the two shaders together into a program.
      GLES20.glLinkProgram(programHandle);

      // Get the link status.
      final int[] linkStatus = new int[1];
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);

      // If the link failed, delete the program.
      if (linkStatus[0] == 0) {
        GLES20.glDeleteProgram(programHandle);
        programHandle = 0;
      }
    }

    if (programHandle == 0) {
      throw new RuntimeException("Error creating program.");
    }

    // Set program handles. These will later be used to pass in values to the program.
    mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
    mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
    mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");

    // Tell OpenGL to use this program when rendering.
    GLES20.glUseProgram(programHandle);
  }
  @Override
  public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {

    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    // Use culling to remove back faces.
    //		GLES20.glEnable(GLES20.GL_CULL_FACE);
    GLES20.glEnable(GLES20.GL_DEPTH_TEST);

    //		// Position the eye behind the origin.
    // Position the eye behind the origin.
    final float eyeX = 0.0f;
    final float eyeY = 0.0f;
    final float eyeZ = 2f; // 1.5f;

    // We are looking toward the distance
    final float lookX = 0.0f;
    final float lookY = 0.0f;
    final float lookZ = -5.0f;

    // Set our up vector. This is where our head would be pointing were we holding the camera.
    final float upX = 0.0f;
    final float upY = 1.0f;
    final float upZ = 0.0f;

    // Set the view matrix. This matrix can be said to represent the camera position.
    // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
    // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
    Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);

    // please note i am making view matrix as identity matrix intentionally here to avoid the
    // effects of view matrix. if you want explore the effect of view matrix you can uncomment this
    // line
    // Matrix.setIdentityM(mViewMatrix, 0);

    final String vertexShader =
        "uniform mat4 u_MVPMatrix;      \n" // A constant representing the combined
            // model/view/projection matrix.
            + "attribute vec4 a_Position;     \n" // Per-vertex position information we will pass
            // in.
            + "attribute vec4 a_Color;        \n" // Per-vertex color information we will pass in.
            + "attribute vec2 a_TexCoordinate;\n"
            + "varying vec2 v_TexCoordinate;  \n"
            + "varying vec4 v_Color;          \n" // This will be passed into the fragment shader.
            + "void main()                    \n" // The entry point for our vertex shader.
            + "{                              \n"
            + "   v_Color = a_Color;          \n" // Pass the color through to the fragment shader.
            + "v_TexCoordinate = a_TexCoordinate;\n" // It will be interpolated across the triangle.
            + "   gl_Position = u_MVPMatrix   \n" // gl_Position is a special variable used to store
            // the final position.
            + "               * a_Position;   \n" // Multiply the vertex by the matrix to get the
            // final point in
            + "}                              \n"; // normalized screen coordinates.

    final String fragmentShader =
        "precision mediump float;       \n" // Set the default precision to medium. We don't need as
            // high of a
            // precision in the fragment shader.
            + "varying vec4 v_Color;          \n" // This is the color from the vertex shader
            // interpolated across the
            + "uniform sampler2D u_Texture;   \n"
            + "varying vec2 v_TexCoordinate;   \n" // triangle per fragment.
            + "void main()                    \n" // The entry point for our fragment shader.
            + "{                              \n"
            + " gl_FragColor = (v_Color * texture2D(u_Texture, v_TexCoordinate));     \n" // Pass
            // the
            // color
            // directly through the pipeline.
            + "}                              \n";

    // Load in the vertex shader.
    int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

    if (vertexShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(vertexShaderHandle, vertexShader);

      // Compile the shader.
      GLES20.glCompileShader(vertexShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(vertexShaderHandle);
        vertexShaderHandle = 0;
      }
    }

    if (vertexShaderHandle == 0) {
      throw new RuntimeException("Error creating vertex shader.");
    }

    // Load in the fragment shader shader.
    int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

    if (fragmentShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);

      // Compile the shader.
      GLES20.glCompileShader(fragmentShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(fragmentShaderHandle);
        fragmentShaderHandle = 0;
      }
    }

    if (fragmentShaderHandle == 0) {
      throw new RuntimeException("Error creating fragment shader.");
    }

    // Create a program object and store the handle to it.
    mProgramHandle = GLES20.glCreateProgram();

    if (mProgramHandle != 0) {
      // Bind the vertex shader to the program.
      GLES20.glAttachShader(mProgramHandle, vertexShaderHandle);

      // Bind the fragment shader to the program.
      GLES20.glAttachShader(mProgramHandle, fragmentShaderHandle);

      // Bind attributes
      GLES20.glBindAttribLocation(mProgramHandle, 0, "a_Position");
      GLES20.glBindAttribLocation(mProgramHandle, 1, "a_Color");
      GLES20.glBindAttribLocation(mProgramHandle, 2, "a_TexCoordinate");

      // Link the two shaders together into a program.
      GLES20.glLinkProgram(mProgramHandle);

      mTextureDataHandle0 = loadTexture(mActivityContext, R.drawable.nature1);
      mTextureDataHandle1 = loadTexture(mActivityContext, R.drawable.nature2);
      mTextureDataHandle2 = loadTexture(mActivityContext, R.drawable.nature3);
      mTextureDataHandle3 = loadTexture(mActivityContext, R.drawable.nature4);
      mTextureDataHandle4 = loadTexture(mActivityContext, R.drawable.nature5);
      mTextureDataHandle5 = loadTexture(mActivityContext, R.drawable.nature6);

      // Get the link status.
      final int[] linkStatus = new int[1];
      GLES20.glGetProgramiv(mProgramHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);

      // If the link failed, delete the program.
      if (linkStatus[0] == 0) {
        GLES20.glDeleteProgram(mProgramHandle);
        mProgramHandle = 0;
      }
    }

    if (mProgramHandle == 0) {
      throw new RuntimeException("Error creating program.");
    }
  }