title: iOS - How to draw a YUV image using openGL
body: <p>Currently, I am trying to draw an image using openGL (the image updates very often, and thus must be redrawn).  Previously, I was converting my image from YUV to RGB, and then using this new image to draw with openGL.  All worked fine, but the conversion process was not particularly fast.</p>
  
  <p>I am now attempting to change the code so that the conversion is taken care of in the openGL shaders.  After looking around, I've found a couple code snippets (particularly the shaders and the bulk of my renderImage function) that have helped me get a baseline, but I can't seem to actually get the image to draw properly - all I ever see is a black image.</p>
  
  <p>It's quite likely that I'm missing something extremely simple and important - My experience with openGL is rather limited.  If anyone can take a look and see if they recognize anything wrong, please let me know.</p>
  
  <p>I should point out, I'm trying to support iOS 4.x, so CVOpenGLESTextureCacheCreateTextureFromImage  shouldn't be usable (and I'm not really positive how to setup to use it even if I wanted to).</p>
  
  <p>Any help would be appreciated.  Code below - </p>
  
  <p>My Vertex shader:</p>
  
  <pre><code>attribute vec4 position;
  attribute vec4 inputTextureCoordinate;
  
  varying vec2 textureCoordinate;
  
  void main()
  {
      gl_Position = position;
      textureCoordinate = inputTextureCoordinate.xy;
  }
  </code></pre>
  
  <p>Fragment Shader:</p>
  
  <pre><code>#ifdef GL_ES
  precision mediump float;
  #endif
  
  varying vec2 textureCoordinate;
  
  uniform sampler2D videoFrame;
  uniform sampler2D videoFrameUV;
  
  const mat3 yuv2rgb = mat3(
                            1, 0, 1.2802,
                            1, -0.214821, -0.380589,
                            1, 2.127982, 0
                            );
  
  void main() {
      vec3 yuv = vec3(
                      1.1643 * (texture2D(videoFrame, textureCoordinate).r - 0.0625),
                      texture2D(videoFrameUV, textureCoordinate).r - 0.5,
                      texture2D(videoFrameUV, textureCoordinate).a - 0.5
                      );
      vec3 rgb = yuv * yuv2rgb;
  
      gl_FragColor = vec4(rgb, 1.0);
  }
  </code></pre>
  
  <p>The renderImage function:</p>
  
  <pre><code>-(void)renderImage:(ImageBuffer *)image
  {
      if (image)
      {        
          int bufferHeight = image-&gt;GetHeight();
          int bufferWidth = image-&gt;GetWidth();
  
          if(!imageTexture){
              // Dealing with the Y portion of the YCbCr
              glActiveTexture(GL_TEXTURE0);
              glGenTextures(1, &amp;imageTexture);
              //Bind Y texture
              glBindTexture(GL_TEXTURE_2D, imageTexture);
              glUniform1i(uniforms[UNIFORM_VIDEOFRAME], 0);
              // For fitting
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
              // This is necessary for non-power-of-two textures
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  
              glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, bufferWidth, bufferHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image-&gt;GetPlanePointer(0));
  
              // Dealing with the CbCr potion of the YCbCr
              glActiveTexture(GL_TEXTURE1);
              glGenTextures(1, &amp;imageTextureUV);
              //Bind CbCr texture
              glBindTexture(GL_TEXTURE_2D, imageTextureUV);
              glUniform1i(uniforms[UNIFORM_VIDEOFRAMEUV], 1);
              // For fitting
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
              // This is necessary for non-power-of-two textures
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  
              glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, bufferWidth/2, bufferHeight/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, image-&gt;GetPlanePointer(1));
  
          }
  
          [self drawFrame];
      }
  }
  </code></pre>
  
  <p>And finally, the drawFrame function:</p>
  
  <pre><code>- (void)drawFrame
  {
      [self setFramebuffer];
  
      // Replace the implementation of this method to do your own custom drawing.
      static const GLfloat squareVertices[] = {
          -1.0f, -1.0f,
          1.0f, -1.0f,
          -1.0f, 1.0f,
          1.0f, 1.0f
      };
  
      static const GLfloat textureVertices[] = {
  //        1.0f, 1.0f,
  //        1.0f, 0.0f,
  //        0.0f,  1.0f,
  //        0.0f,  0.0f,
          0.0f, 1.0f,
          1.0f, 1.0f,
          0.0f, 0.0f,
          1.0f, 0.0f
  //        0.0f, 0.8f,
  //        1.0f, 0.8f,
  //        0.0f, 0.2f,
  //        1.0f, 0.2f
      };
  
      static float transY = 0.0f;
  
  
      if ([context API] == kEAGLRenderingAPIOpenGLES2) {
          // Use shader program.
          glUseProgram(program);
  
          glActiveTexture(GL_TEXTURE0);
          glBindTexture(GL_TEXTURE_2D, imageTexture);
  
          // Update attribute values.
          glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
          glEnableVertexAttribArray(ATTRIB_VERTEX);
          glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
          glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
  
          glActiveTexture(GL_TEXTURE1);
          glBindTexture(GL_TEXTURE_2D, imageTextureUV);
  
          // Update attribute values.
          glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
          glEnableVertexAttribArray(ATTRIB_VERTEX);
          glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
          glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
  
          // Validate program before drawing. This is a good check, but only really necessary in a debug build.
          // DEBUG macro must be defined in your debug configurations if that's not already the case.
  #if defined(DEBUG)
          if (![self validateProgram:program]) {
              NSLog(@"Failed to validate program: %d", program);
              return;
          }
  #endif
      } else {
          glMatrixMode(GL_PROJECTION);
          glLoadIdentity();
          glMatrixMode(GL_MODELVIEW);
          glLoadIdentity();
          glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
          transY += 0.075f;
  
          glVertexPointer(2, GL_FLOAT, 0, squareVertices);
          glEnableClientState(GL_VERTEX_ARRAY);
      } 
  
      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  
      [self presentFramebuffer];
  }
  </code></pre>