title: I want to make a more than two input texture filter with GPUImage. but I got a black output
body: <p>I want to make a new filter like GPUImage's GPUImageTwoInputFilter.</p>
  
  <p>here is my code . A base class named IFFourInputFilter, it is likely GPUImageTwoInputFilter.</p>
  
  <pre><code>#import "IFFourInputFilter.h"
  
  NSString *const kIFFourInputTextureVertexShaderString = SHADER_STRING
  (
   attribute vec4 position;
   attribute vec4 inputTextureCoordinate;
   attribute vec4 inputTextureCoordinate2;
   attribute vec4 inputTextureCoordinate3;
   attribute vec4 inputTextureCoordinate4;
  
   varying vec2 textureCoordinate;
   varying vec2 textureCoordinate2;
   varying vec2 textureCoordinate3;
   varying vec2 textureCoordinate4;
  
   void main()
   {
       gl_Position = position;
       textureCoordinate = inputTextureCoordinate.xy;
       textureCoordinate2 = inputTextureCoordinate2.xy;
       textureCoordinate3 = inputTextureCoordinate3.xy;
       textureCoordinate4 = inputTextureCoordinate4.xy;
   }
  );
  
  
  @implementation IFFourInputFilter
  
  #pragma mark -
  #pragma mark Initialization and teardown
  
  - (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
  {
      if (!(self = [self initWithVertexShaderFromString:kIFFourInputTextureVertexShaderString fragmentShaderFromString:fragmentShaderString]))
      {
          return nil;
      }
  
      return self;
  }
  
  - (id)initWithVertexShaderFromString:(NSString *)vertexShaderString fragmentShaderFromString:(NSString *)fragmentShaderString;
  {
      if (!(self = [super initWithVertexShaderFromString:vertexShaderString fragmentShaderFromString:fragmentShaderString]))
      {
          return nil;
      }
  
      inputRotation2 = kGPUImageNoRotation;
      inputRotation3 = kGPUImageNoRotation;
      inputRotation4 = kGPUImageNoRotation;
  
      hasSetTexture1 = NO;
      hasSetTexture2 = NO;
      hasSetTexture3 = NO;
  
      hasReceivedFrame1 = NO;
      hasReceivedFrame2 = NO;
      hasReceivedFrame3 = NO;
      hasReceivedFrame4 = NO;
      frameWasVideo1 = NO;
      frameWasVideo2 = NO;
      frameWasVideo3 = NO;
      frameWasVideo4 = NO;
      frameCheckDisabled1 = NO;
      frameCheckDisabled2 = NO;
      frameCheckDisabled3 = NO;
      frameCheckDisabled4 = NO;
  
      frameTime1 = kCMTimeInvalid;
      frameTime2 = kCMTimeInvalid;
      frameTime3 = kCMTimeInvalid;
      frameTime4 = kCMTimeInvalid;
  
      runSynchronouslyOnVideoProcessingQueue(^{
          [GPUImageOpenGLESContext useImageProcessingContext];
          filterTextureCoordinateAttribute2 = [filterProgram attributeIndex:@"inputTextureCoordinate2"];
  
          filterInputTextureUniform2 = [filterProgram uniformIndex:@"inputImageTexture2"]; // This does assume a name of "inputImageTexture2" for second input texture in the fragment shader
          glEnableVertexAttribArray(filterTextureCoordinateAttribute2);
  
          filterTextureCoordinateAttribute3 = [filterProgram attributeIndex:@"inputTextureCoordinate3"];
  
          filterInputTextureUniform3 = [filterProgram uniformIndex:@"inputImageTexture3"]; // This does assume a name of "inputImageTexture2" for second input texture in the fragment shader
          glEnableVertexAttribArray(filterTextureCoordinateAttribute3);
  
          filterTextureCoordinateAttribute4 = [filterProgram attributeIndex:@"inputTextureCoordinate4"];
  
          filterInputTextureUniform4 = [filterProgram uniformIndex:@"inputImageTexture4"]; // This does assume a name of "inputImageTexture2" for second input texture in the fragment shader
          glEnableVertexAttribArray(filterTextureCoordinateAttribute4);
      });
  
      return self;
  }
  
  - (void)initializeAttributes;
  {
      [super initializeAttributes];
      [filterProgram addAttribute:@"inputTextureCoordinate2"];
      [filterProgram addAttribute:@"inputTextureCoordinate3"];
      [filterProgram addAttribute:@"inputTextureCoordinate4"];
  }
  
  - (void)disableFrameCheck1;
  {
      frameCheckDisabled1 = YES;
  }
  
  - (void)disableFrameCheck2;
  {
      frameCheckDisabled2 = YES;
  }
  
  - (void)disableFrameCheck3;
  {
      frameCheckDisabled3 = YES;
  }
  
  - (void)disableFrameCheck4;
  {
      frameCheckDisabled4 = YES;
  }
  
  #pragma mark -
  #pragma mark Rendering
  
  - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates sourceTexture:(GLuint)sourceTexture;
  {
      if (self.preventRendering)
      {
          return;
      }
  
      [GPUImageOpenGLESContext setActiveShaderProgram:filterProgram];
      [self setUniformsForProgramAtIndex:0];
  
      [self setFilterFBO];
  
      glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha);
      glClear(GL_COLOR_BUFFER_BIT);
  
      glActiveTexture(GL_TEXTURE2);
      glBindTexture(GL_TEXTURE_2D, sourceTexture);
      glUniform1i(filterInputTextureUniform, 2);
  
      glActiveTexture(GL_TEXTURE3);
      glBindTexture(GL_TEXTURE_2D, filterSourceTexture2);
      glUniform1i(filterInputTextureUniform2, 3);
  
      glActiveTexture(GL_TEXTURE4);
      glBindTexture(GL_TEXTURE_2D, filterSourceTexture3);
      glUniform1i(filterInputTextureUniform3, 4);
  
      glActiveTexture(GL_TEXTURE5);
      glBindTexture(GL_TEXTURE_2D, filterSourceTexture4);
      glUniform1i(filterInputTextureUniform4, 5);
  
      glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
      glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
      glVertexAttribPointer(filterTextureCoordinateAttribute2, 2, GL_FLOAT, 0, 0, [[self class] textureCoordinatesForRotation:inputRotation2]);
      glVertexAttribPointer(filterTextureCoordinateAttribute3, 2, GL_FLOAT, 0, 0, [[self class] textureCoordinatesForRotation:inputRotation3]);
      glVertexAttribPointer(filterTextureCoordinateAttribute4, 2, GL_FLOAT, 0, 0, [[self class] textureCoordinatesForRotation:inputRotation4]);
  
      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  }
  
  - (void)releaseInputTexturesIfNeeded;
  {
      if (shouldConserveMemoryForNextFrame)
      {
          [firstTextureDelegate textureNoLongerNeededForTarget:self];
          [textureDelegate2 textureNoLongerNeededForTarget:self];
          [textureDelegate3 textureNoLongerNeededForTarget:self];
          [textureDelegate4 textureNoLongerNeededForTarget:self];
          shouldConserveMemoryForNextFrame = NO;
      }
  }
  
  #pragma mark -
  #pragma mark GPUImageInput
  
  - (NSInteger)nextAvailableTextureIndex;
  {
      if (!hasSetTexture1){
          return 0;
      }else if (!hasSetTexture2) {
          return 1;
      }else if (!hasSetTexture3) {
          return 2;
      }else{
          return 3;
      }
  }
  
  - (void)setInputTexture:(GLuint)newInputTexture atIndex:(NSInteger)textureIndex;
  {
      switch (textureIndex) {
          case 0:
              filterSourceTexture = newInputTexture;
              hasSetTexture1 = YES;
              break;
          case 1:
              filterSourceTexture2 = newInputTexture;
              hasSetTexture2 = YES;
              break;
          case 2:
              filterSourceTexture3 = newInputTexture;
              hasSetTexture3 = YES;
              break;
          case 3:
              filterSourceTexture4 = newInputTexture;
              break;
          default:
              break;
      }
  }
  
  - (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
  {
      if (textureIndex == 0)
      {
          [super setInputSize:newSize atIndex:textureIndex];
  
          if (CGSizeEqualToSize(newSize, CGSizeZero))
          {
              hasSetTexture1 = NO;
          }
      }
  }
  
  - (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
  {
      switch (textureIndex) {
          case 0:
              inputRotation = newInputRotation;
              break;
          case 1:
              inputRotation2 = newInputRotation;
              break;
          case 2:
              inputRotation3 = newInputRotation;
              break;
          case 3:
              inputRotation4 = newInputRotation;
              break;
          default:
              break;
      }
  }
  
  - (CGSize)rotatedSize:(CGSize)sizeToRotate forIndex:(NSInteger)textureIndex;
  {
      CGSize rotatedSize = sizeToRotate;
  
      GPUImageRotationMode rotationToCheck;
      switch (textureIndex) {
          case 0:
              rotationToCheck = inputRotation;
              break;
          case 1:
              rotationToCheck = inputRotation2;
              break;
          case 2:
              rotationToCheck = inputRotation3;
              break;
          case 3:
              rotationToCheck = inputRotation4;
              break;
          default:
              break;
      }
  
      if (GPUImageRotationSwapsWidthAndHeight(rotationToCheck))
      {
          rotatedSize.width = sizeToRotate.height;
          rotatedSize.height = sizeToRotate.width;
      }
  
      return rotatedSize;
  }
  
  - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
  {
      outputTextureRetainCount = [targets count];
  
      // You can set up infinite update loops, so this helps to short circuit them
      if (hasReceivedFrame1 &amp;&amp; hasReceivedFrame2 &amp;&amp; hasReceivedFrame3 &amp;&amp; hasReceivedFrame4)
      {
          return;
      }
  
      BOOL updatedMovieFrameOppositeStillImage = NO;
  
      switch (textureIndex) {
          case 0:
              hasReceivedFrame1 = YES;
              frameTime1 = frameTime;
              if (frameCheckDisabled2)
              {
                  hasReceivedFrame2 = YES;
              }
              if (frameCheckDisabled3)
              {
                  hasReceivedFrame3 = YES;
              }
              if (frameCheckDisabled4)
              {
                  hasReceivedFrame4 = YES;
              }
  
              if (!CMTIME_IS_INDEFINITE(frameTime))
              {
                  if (CMTIME_IS_INDEFINITE(frameTime2) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime3) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime4))
                  {
                      updatedMovieFrameOppositeStillImage = YES;
                  }
              }
              break;
          case 1:
              hasReceivedFrame2 = YES;
              frameTime2 = frameTime;
              if (frameCheckDisabled1)
              {
                  hasReceivedFrame1 = YES;
              }
              if (frameCheckDisabled3)
              {
                  hasReceivedFrame3 = YES;
              }
              if (frameCheckDisabled4)
              {
                  hasReceivedFrame4 = YES;
              }
  
              if (!CMTIME_IS_INDEFINITE(frameTime))
              {
                  if (CMTIME_IS_INDEFINITE(frameTime1) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime3) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime4))
                  {
                      updatedMovieFrameOppositeStillImage = YES;
                  }
              }
              break;
          case 2:
              hasReceivedFrame3 = YES;
              frameTime3 = frameTime;
              if (frameCheckDisabled1)
              {
                  hasReceivedFrame1 = YES;
              }
              if (frameCheckDisabled2)
              {
                  hasReceivedFrame2 = YES;
              }
              if (frameCheckDisabled4)
              {
                  hasReceivedFrame4 = YES;
              }
  
              if (!CMTIME_IS_INDEFINITE(frameTime))
              {
                  if (CMTIME_IS_INDEFINITE(frameTime1) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime2) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime4))
                  {
                      updatedMovieFrameOppositeStillImage = YES;
                  }
              }
              break;
          case 3:
              hasReceivedFrame4 = YES;
              frameTime4 = frameTime;
              if (frameCheckDisabled1)
              {
                  hasReceivedFrame1 = YES;
              }
              if (frameCheckDisabled3)
              {
                  hasReceivedFrame3 = YES;
              }
              if (frameCheckDisabled2)
              {
                  hasReceivedFrame2 = YES;
              }
  
              if (!CMTIME_IS_INDEFINITE(frameTime))
              {
                  if (CMTIME_IS_INDEFINITE(frameTime1) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime3) &amp;&amp; CMTIME_IS_INDEFINITE(frameTime2))
                  {
                      updatedMovieFrameOppositeStillImage = YES;
                  }
              }
              break;
          default:
              break;
      }
  
      // || (hasReceivedFirstFrame &amp;&amp; secondFrameCheckDisabled) || (hasReceivedSecondFrame &amp;&amp; firstFrameCheckDisabled)
      if ((hasReceivedFrame1 &amp;&amp; hasReceivedFrame2 &amp;&amp; hasReceivedFrame3 &amp;&amp; hasReceivedFrame4) || updatedMovieFrameOppositeStillImage)
      {
          [super newFrameReadyAtTime:frameTime atIndex:0];
          hasReceivedFrame1 = NO;
          hasReceivedFrame2 = NO;
          hasReceivedFrame3 = NO;
          hasReceivedFrame4 = NO;
      }
  }
  
  - (void)setTextureDelegate:(id&lt;GPUImageTextureDelegate&gt;)newTextureDelegate atIndex:(NSInteger)textureIndex;
  {
      switch (textureIndex) {
          case 0:
              firstTextureDelegate = newTextureDelegate;
              break;
          case 1:
              textureDelegate2 = newTextureDelegate;
              break;
          case 2:
              textureDelegate3 = newTextureDelegate;
              break;
          case 3:
              textureDelegate4 = newTextureDelegate;
              break;
          default:
              break;
      }
  }
  
  @end
  </code></pre>
  
  <p>A class named IFAmaroFilter extend IFFourInputFilter.</p>
  
  <pre><code>#import "IFAmaroFilter.h"
  
  NSString *const kIFAmaroFilterFragmentShaderString = SHADER_STRING
  (
   precision lowp float;
  
   varying highp vec2 textureCoordinate;
  
   uniform sampler2D inputImageTexture;
   uniform sampler2D inputImageTexture2; //blowout;
   uniform sampler2D inputImageTexture3; //overlay;
   uniform sampler2D inputImageTexture4; //map
  
   void main()
   {
       vec4 texel = texture2D(inputImageTexture, textureCoordinate);
       vec3 bbTexel = texture2D(inputImageTexture2, textureCoordinate).rgb;
  
       texel.r = texture2D(inputImageTexture3, vec2(bbTexel.r, texel.r)).r;
       texel.g = texture2D(inputImageTexture3, vec2(bbTexel.g, texel.g)).g;
       texel.b = texture2D(inputImageTexture3, vec2(bbTexel.b, texel.b)).b;
  
       vec4 mapped;
       mapped.r = texture2D(inputImageTexture4, vec2(texel.r, 0.16666)).r;
       mapped.g = texture2D(inputImageTexture4, vec2(texel.g, .5)).g;
       mapped.b = texture2D(inputImageTexture4, vec2(texel.b, .83333)).b;
       mapped.a = 1.0;
  
       gl_FragColor = texel;
   }
   );
  
  @implementation IFAmaroFilter
  
  - (id)init;
  {
      if (!(self = [super initWithFragmentShaderFromString:kIFAmaroFilterFragmentShaderString]))
      {
          return nil;
      }
  
      return self;
  }
  
  @end
  </code></pre>
  
  <p>when I use the filter I got a black output. code below:</p>
  
  <pre><code>    filter = [[IFAmaroFilter alloc] init];
      GPUImagePicture *gp1 = [[GPUImagePicture alloc] initWithImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"blackboard1024" ofType:@"png"]]];
      GPUImagePicture *gp2 = [[GPUImagePicture alloc] initWithImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"overlayMap" ofType:@"png"]]];
      GPUImagePicture *gp3 = [[GPUImagePicture alloc] initWithImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"amaroMap" ofType:@"png"]]];
  
      [stillCamera addTarget:filter atTextureLocation:0];
      [gp1 addTarget:filter atTextureLocation:1];
      [gp1 processImage];
      [gp2 addTarget:filter atTextureLocation:2];
      [gp2 processImage];
      [gp3 addTarget:filter atTextureLocation:3];
      [gp3 processImage];
      [filter addTarget:(GPUImageView *)self.view];
  </code></pre>