title: OpenGL ES 2.0 - iOS - Render to texture using FBO
body: <p>Based on the tutorial : </p>
  
  <p>"<a href="http://www.raywenderlich.com/4404/opengl-es-2-0-for-iphone-tutorial-part-2-textures" rel="nofollow">http://www.raywenderlich.com/4404/opengl-es-2-0-for-iphone-tutorial-part-2-textures</a>" </p>
  
  <p>and the book "Open GL ES 2.0 Programming Guide"</p>
  
  <p>I'm trying to render a scene to a FBO texture, and then render that FBO texture on the display window. </p>
  
  <p>I don't know why, but I have a white screen..</p>
  
  <p>Edit 1 : The white screen was due to binding the depth buffer in the render loop. Now the problem is that the cube rendered in the FBO texture is not rendered on screen.. </p>
  
  <p>Edit 2 : Updated the code and added shader files</p>
  
  <p>Here is my code :</p>
  
  <p>OpenGLView.h :</p>
  
  <pre><code>#import &lt;UIKit/UIKit.h&gt;
  #import &lt;QuartzCore/QuartzCore.h&gt;
  #include &lt;OpenGLES/ES2/gl.h&gt;
  #include &lt;OpenGLES/ES2/glext.h&gt;
  
  @interface OpenGLView : UIView {
      CAEAGLLayer* _eaglLayer;
      EAGLContext* _context;
  
      GLuint _viewFrameBuffer;
      GLuint _colorRenderBuffer;
      GLuint _depthRenderBuffer;
  
  
  
      float _cubeCurrentRotation;
  
      GLuint _cubeTexture;
  
      GLuint _cubeVertexBuffer;
      GLuint _cubeIndexBuffer;
      GLuint _viewportSizedQuadVertexBuffer;
      GLuint _viewportSizedQuadIndexBuffer;
  
      GLuint _FBO;
      GLuint _FBOTexture;
      GLuint _FBODepthBuffer;
  
      // shaders
  
      // simple shader
      GLuint _simpleShaderProgram;
      GLuint _simpleVertexShader;
      GLuint _simpleFragmentShader;
      GLuint _positionSlot;
      GLuint _colorSlot;
      GLuint _projectionUniform;
      GLuint _modelViewUniform;
      GLuint _texCoordSlot;
      GLuint _textureUniform;
      // texture shader
      GLuint _textureShaderProgram;
      GLuint _textureVertexShader;
      GLuint _texturePositionSlot;
      GLuint _textureColorSlot;
      GLuint _textureTexCoordSlot;
      GLuint _textureTextureUniform;
  }
  
  @end
  </code></pre>
  
  <p>OpenGLView.m :</p>
  
  <pre><code>    //
  //  OpenGLView.m
  //  HelloOpenGL
  //
  //  Created by Ray Wenderlich on 5/24/11.
  //  Copyright 2011 __MyCompanyName__. All rights reserved.
  //
  
  #import "OpenGLView.h"
  #import "CC3GLMatrix.h"
  
  @implementation OpenGLView
  
  typedef struct {
      float Position[3];
      float Color[4];
      float TexCoord[2];
  } Vertex;
  
  #define TEX_COORD_MAX 1
  
  const Vertex cubeVertices[] = {
      // Front
      {{1, -1, 0}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}},
      {{1, 1, 0}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
      {{-1, 1, 0}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}},
      {{-1, -1, 0}, {0, 0, 0, 1}, {0, 0}},
      // Back
      {{1, 1, -2}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}},
      {{-1, -1, -2}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
      {{1, -1, -2}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}},
      {{-1, 1, -2}, {0, 0, 0, 1}, {0, 0}},
      // Left
      {{-1, -1, 0}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}},
      {{-1, 1, 0}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
      {{-1, 1, -2}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}},
      {{-1, -1, -2}, {0, 0, 0, 1}, {0, 0}},
      // Right
      {{1, -1, -2}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}},
      {{1, 1, -2}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
      {{1, 1, 0}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}},
      {{1, -1, 0}, {0, 0, 0, 1}, {0, 0}},
      // Top
      {{1, 1, 0}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}},
      {{1, 1, -2}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
      {{-1, 1, -2}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}},
      {{-1, 1, 0}, {0, 0, 0, 1}, {0, 0}},
      // Bottom
      {{1, -1, -2}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}},
      {{1, -1, 0}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
      {{-1, -1, 0}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}},
      {{-1, -1, -2}, {0, 0, 0, 1}, {0, 0}}
  };
  
  const GLubyte cubeIndices[] = {
      // Front
      0, 1, 2,
      2, 3, 0,
      // Back
      4, 5, 6,
      6, 7, 4,
      // Left
      8, 9, 10,
      10, 11, 8,
      // Right
      12, 13, 14,
      14, 15, 12,
      // Top
      16, 17, 18,
      18, 19, 16,
      // Bottom
      20, 21, 22,
      22, 23, 20
  };
  
  const Vertex viewPortQuadVertices[] = {
      {{1, -1, 0}, {1, 1, 1, 1}, {TEX_COORD_MAX, 0}},
      {{1, 1, 0}, {1, 1, 1, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
      {{-1, 1, 0}, {1, 1, 1, 1}, {0, TEX_COORD_MAX}},
      {{-1, -1, 0}, {1, 1, 1, 1}, {0, 0}}
  };
  
  const GLubyte viewPortQuadIndices[] = {
      0, 1, 2,
      2, 3, 0,
  };
  
  + (Class)layerClass {
      return [CAEAGLLayer class];
  }
  
  - (void)dealloc {
      [_context release];
      _context = nil;
      [super dealloc];
  }
  
  - (id)initWithFrame:(CGRect)frame {
      self = [super initWithFrame:frame];
      if (self) {
          [self setupLayer];
          [self setupContext];
          [self setupDepthBuffer];
          [self setupRenderBuffer];
          [self setupFrameBuffer];
          [self setupFBOs];
          [self compileShaders];
          [self setupVBOs];
          [self setupDisplayLink];
          _cubeTexture = [self setupTexture:@"tile_floor.png"];
      }
      return self;
  }
  
  - (void)setupDisplayLink {
      CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
      [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  }
  
  - (void)setupLayer {
      _eaglLayer = (CAEAGLLayer*) self.layer;
      _eaglLayer.opaque = YES;
  }
  
  - (void)setupContext {
      EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
      _context = [[EAGLContext alloc] initWithAPI:api];
      if (!_context) {
          NSLog(@"Failed to initialize OpenGLES 2.0 context");
          exit(1);
      }
  
      if (![EAGLContext setCurrentContext:_context]) {
          NSLog(@"Failed to set current OpenGL context");
          exit(1);
      }
  }
  
  - (void)setupRenderBuffer {
      glGenRenderbuffers(1, &amp;_colorRenderBuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
      [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
  }
  
  - (void)setupDepthBuffer {
      glGenRenderbuffers(1, &amp;_depthRenderBuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
      glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, self.frame.size.width, self.frame.size.height);
  }
  
  - (void)setupFrameBuffer {
      glGenFramebuffers(1, &amp;_viewFrameBuffer);
      glBindFramebuffer(GL_FRAMEBUFFER, _viewFrameBuffer);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
  }
  
  -(void)setupFBOs {
      GLint maxRenderBufferSize;
      glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &amp;maxRenderBufferSize);
  
      GLuint textureWidth = self.frame.size.width;
      GLuint textureHeight = self.frame.size.height;
  
      if(maxRenderBufferSize &lt;= textureWidth
         || maxRenderBufferSize &lt;= textureHeight) {
              NSLog(@"FBO cant allocate that much space");
      }
  
      glGenFramebuffers(1, &amp;_FBO);
      glGenRenderbuffers(1, &amp;_FBODepthBuffer);
      glGenTextures(1, &amp;_FBOTexture);
  
      glBindTexture(GL_TEXTURE_2D, _FBOTexture);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureHeight,
                   0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
  
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  
      glBindRenderbuffer(GL_RENDERBUFFER, _FBODepthBuffer);
      glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, textureWidth, textureHeight);
  
      glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _FBOTexture, 0);
  
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _FBODepthBuffer);
  
      GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
      if(status != GL_FRAMEBUFFER_COMPLETE) {
          NSLog(@"FBO is not complete :%u", status);
      }
  }
  
  - (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
      NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:@"glsl"];
      NSError* error;
      NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&amp;error];
      if (!shaderString) {
          NSLog(@"Error loading shader: %@", error.localizedDescription);
          exit(1);
      }
  
      GLuint shaderHandle = glCreateShader(shaderType);
  
      const char * shaderStringUTF8 = [shaderString UTF8String];
      int shaderStringLength = [shaderString length];
      glShaderSource(shaderHandle, 1, &amp;shaderStringUTF8, &amp;shaderStringLength);
  
      glCompileShader(shaderHandle);
  
      GLint compileSuccess;
      glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &amp;compileSuccess);
      if (compileSuccess == GL_FALSE) {
          GLchar messages[256];
          glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &amp;messages[0]);
          NSString *messageString = [NSString stringWithUTF8String:messages];
          NSLog(@"%@", messageString);
          exit(1);
      }
  
      return shaderHandle;
  }
  
  - (void)compileShaders {
      _simpleVertexShader = [self compileShader:@"SimpleVertex" withType:GL_VERTEX_SHADER];
      _simpleFragmentShader = [self compileShader:@"SimpleFragment" withType:GL_FRAGMENT_SHADER];
      _textureVertexShader = [self compileShader:@"TextureVertex" withType:GL_VERTEX_SHADER];
  
      _simpleShaderProgram = glCreateProgram();
      glAttachShader(_simpleShaderProgram, _simpleVertexShader);
      glAttachShader(_simpleShaderProgram, _simpleFragmentShader);
      glLinkProgram(_simpleShaderProgram);
  
      GLint linkSuccess;
      glGetProgramiv(_simpleShaderProgram, GL_LINK_STATUS, &amp;linkSuccess);
      if (linkSuccess == GL_FALSE) {
          GLchar messages[256];
          glGetProgramInfoLog(_simpleShaderProgram, sizeof(messages), 0, &amp;messages[0]);
          NSString *messageString = [NSString stringWithUTF8String:messages];
          NSLog(@"%@", messageString);
          exit(1);
      }
  
      glUseProgram(_simpleShaderProgram);
  
      _positionSlot = glGetAttribLocation(_simpleShaderProgram, "Position");
      _colorSlot = glGetAttribLocation(_simpleShaderProgram, "SourceColor");
      glEnableVertexAttribArray(_positionSlot);
      glEnableVertexAttribArray(_colorSlot);
  
      _projectionUniform = glGetUniformLocation(_simpleShaderProgram, "Projection");
      _modelViewUniform = glGetUniformLocation(_simpleShaderProgram, "Modelview");
  
      _texCoordSlot = glGetAttribLocation(_simpleShaderProgram, "TexCoordIn");
      glEnableVertexAttribArray(_texCoordSlot);
      _textureUniform = glGetUniformLocation(_simpleShaderProgram, "Texture");
  
  
      // Texture shader
      _textureShaderProgram = glCreateProgram();
      glAttachShader(_textureShaderProgram, _textureVertexShader);
      glAttachShader(_textureShaderProgram, _simpleFragmentShader);
      glLinkProgram(_textureShaderProgram);
  
      glGetProgramiv(_textureShaderProgram, GL_LINK_STATUS, &amp;linkSuccess);
      if (linkSuccess == GL_FALSE) {
          GLchar messages[256];
          glGetProgramInfoLog(_textureShaderProgram, sizeof(messages), 0, &amp;messages[0]);
          NSString *messageString = [NSString stringWithUTF8String:messages];
          NSLog(@"%@", messageString);
          exit(1);
      }
  
      glUseProgram(_textureShaderProgram);
  
      _texturePositionSlot = glGetAttribLocation(_textureShaderProgram, "texPosition");
      _textureColorSlot = glGetAttribLocation(_textureShaderProgram, "texSourceColor");
      glEnableVertexAttribArray(_texturePositionSlot);
      glEnableVertexAttribArray(_textureColorSlot);
  
      _textureTexCoordSlot = glGetAttribLocation(_textureShaderProgram, "texTexCoordIn");
      glEnableVertexAttribArray(_textureTexCoordSlot);
      _textureTextureUniform = glGetUniformLocation(_textureShaderProgram, "Texture");
  }
  
  - (void)setupVBOs {
      glGenBuffers(1, &amp;_cubeVertexBuffer);
      glBindBuffer(GL_ARRAY_BUFFER, _cubeVertexBuffer);
      glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);
  
      glGenBuffers(1, &amp;_cubeIndexBuffer);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _cubeIndexBuffer);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW);
  
      glGenBuffers(1, &amp;_viewportSizedQuadVertexBuffer);
      glBindBuffer(GL_ARRAY_BUFFER, _viewportSizedQuadVertexBuffer);
      glBufferData(GL_ARRAY_BUFFER, sizeof(viewPortQuadVertices), viewPortQuadVertices, GL_STATIC_DRAW);
  
      glGenBuffers(1, &amp;_viewportSizedQuadIndexBuffer);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _viewportSizedQuadIndexBuffer);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(viewPortQuadIndices), viewPortQuadIndices, GL_STATIC_DRAW);
  }
  
  - (GLuint)setupTexture:(NSString *)fileName {
      CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
      if (!spriteImage) {
          NSLog(@"Failed to load image %@", fileName);
          exit(1);
      }
  
      size_t width = CGImageGetWidth(spriteImage);
      size_t height = CGImageGetHeight(spriteImage);
  
      GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
  
      CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
  
      CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
  
      CGContextRelease(spriteContext);
  
      GLuint texName;
      glGenTextures(1, &amp;texName);
      glBindTexture(GL_TEXTURE_2D, texName);
  
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
  
      free(spriteData);
      return texName;
  }
  
  - (void)render:(CADisplayLink*)displayLink {
  
      glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
      //glBindRenderbuffer(GL_RENDERBUFFER, _FBODepthBuffer);
  
      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      glEnable(GL_BLEND);
  
      glClearColor(255.0/255.0, 255.0/255.0, 255.0/255.0, 1.0);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glEnable(GL_DEPTH_TEST);
  
      glViewport(0, 0, self.frame.size.width, self.frame.size.height);
  
      glUseProgram(_simpleShaderProgram);
  
      // Cube draw in FBO
      CC3GLMatrix *projection = [CC3GLMatrix matrix];
      float h = 4.0f * self.frame.size.height / self.frame.size.width;
      [projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:1000];
      glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
  
      CC3GLMatrix *modelView = [CC3GLMatrix matrix];
      [modelView populateFromTranslation:CC3VectorMake(sin(CACurrentMediaTime()), 0, -7)];
      _cubeCurrentRotation += displayLink.duration * 90;
      [modelView rotateBy:CC3VectorMake(_cubeCurrentRotation, _cubeCurrentRotation, 0)];
      glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
  
      glBindBuffer(GL_ARRAY_BUFFER, _cubeVertexBuffer);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _cubeIndexBuffer);
  
      glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
      glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
  
      glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
  
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, _cubeTexture);
      glUniform1i(_textureUniform, 0);
  
      glDrawElements(GL_TRIANGLES, sizeof(cubeIndices)/sizeof(cubeIndices[0]), GL_UNSIGNED_BYTE, 0);
  
      glBindFramebuffer(GL_FRAMEBUFFER, 0);
  
      // ---------------------------------------------------------------
      // FBO -&gt; window
  
      glBindFramebuffer(GL_FRAMEBUFFER, _viewFrameBuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
  
      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      glEnable(GL_BLEND);
  
      glClearColor(0.0/255.0, 0.0/255.0, 0.0/255.0, 1.0);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glEnable(GL_DEPTH_TEST);
  
      glViewport(0, 0, self.frame.size.width, self.frame.size.height);
  
      glUseProgram(_textureShaderProgram);
  
      glBindBuffer(GL_ARRAY_BUFFER, _viewportSizedQuadVertexBuffer);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _viewportSizedQuadIndexBuffer);
  
      glVertexAttribPointer(_texturePositionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
      glVertexAttribPointer(_textureColorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
  
      glVertexAttribPointer(_textureTexCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
  
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, _FBOTexture);
      glUniform1i(_textureTextureUniform, 0);
      glDrawElements(GL_TRIANGLES, sizeof(viewPortQuadIndices)/sizeof(viewPortQuadIndices[0]), GL_UNSIGNED_BYTE, 0);
  
      [_context presentRenderbuffer:GL_RENDERBUFFER];
  
      glBindFramebuffer(GL_FRAMEBUFFER, 0);
      glBindRenderbuffer(GL_RENDERBUFFER, 0);
  }
  
  @end
  </code></pre>
  
  <p>SimpleVertex.glsl :</p>
  
  <pre><code>attribute vec4 Position; 
  attribute vec4 SourceColor; 
  
  varying vec4 DestinationColor; 
  
  uniform mat4 Projection;
  uniform mat4 Modelview;
  
  attribute vec2 TexCoordIn;
  varying vec2 TexCoordOut;
  
  void main(void) { 
      DestinationColor = SourceColor; 
      gl_Position = Projection * Modelview * Position;
      TexCoordOut = TexCoordIn;
  }
  </code></pre>
  
  <p>TextureVertex.glsl :</p>
  
  <pre><code>attribute vec4 texPosition;
  attribute vec4 texSourceColor;
  
  varying vec4 DestinationColor;
  
  attribute vec2 texTexCoordIn;
  varying vec2 TexCoordOut;
  
  void main(void) { 
      DestinationColor = texSourceColor;
      gl_Position = texPosition;
      TexCoordOut = texTexCoordIn;
  }
  </code></pre>
  
  <p>SimpleFragment.glsl :</p>
  
  <pre><code>varying lowp vec4 DestinationColor;
  
  varying lowp vec2 TexCoordOut;
  uniform sampler2D Texture;
  
  void main(void) {
      gl_FragColor = DestinationColor * texture2D(Texture, TexCoordOut);
  }
  </code></pre>