Hi,

I'm just getting started with OpenGL ES 2.0, and trying to blend a series of 5 or 6 layers onto a quad. I am attempting to load each texture into a texture slot, and combine them in a fragment shader. Unfortunately, only the texture loaded into GL_TEXTURE0 is appearing (even if I try to display another slot's texture independently).

I'm not sure if I am loading the other textures correctly. Could somebody take a quick look at my code (especially the render method and fragment shader), and let me know where I'm going wrong?

Many thanks

Code :
//
//  OpenGLView.h
//
 
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
 
@interface OpenGLView : UIView {
 
    CAEAGLLayer* _eaglLayer;
    EAGLContext* _context;
 
    GLuint _colorRenderBuffer;
    GLuint _vertexBuffer;
    GLuint _indexBuffer;
 
    GLuint _positionSlot;
    GLuint _colorSlot;
    GLuint _texCoordSlot; 
 
    GLuint _bgTexture;
    GLuint _layer1Texture;
    GLuint _layer2Texture;
    GLuint _layer3Texture;
    GLuint _layer4Texture;
    GLuint _layer5Texture;
 
    GLuint _textureBackground;
    GLuint _textureLayer1;    
    GLuint _textureLayer2;
    GLuint _textureLayer3;
    GLuint _textureLayer4;
    GLuint _textureLayer5;
 
}
 
@end

Code :
//
//  OpenGLView.m
 
#import "OpenGLView.h"
 
@implementation OpenGLView
 
typedef struct {
    float Position[3];
    float Color[4];
    float TexCoord[2];
} Vertex;
 
#define TEX_COORD_MAX 1
 
const Vertex Vertices[] = {
    // 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}},
 
};
 
const GLubyte Indices[] = {
    // Front
    0, 1, 2,
    2, 3, 0
};
 
 
+ (Class)layerClass {
    return [CAEAGLLayer class];
}
 
- (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, &_colorRenderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);        
    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];    
}
 
- (void)setupFrameBuffer {    
    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);   
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);
}
 
- (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:&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, &shaderStringUTF8, &shaderStringLength);
 
    glCompileShader(shaderHandle);
 
    GLint compileSuccess;
    glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
    if (compileSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }
 
    return shaderHandle;
 
}
 
- (void)compileShaders {
 
    GLuint vertexShader = [self compileShader:@"SimpleVertex" withType:GL_VERTEX_SHADER];
    GLuint fragmentShader = [self compileShader:@"SimpleFragment" withType:GL_FRAGMENT_SHADER];
 
    GLuint programHandle = glCreateProgram();
    glAttachShader(programHandle, vertexShader);
    glAttachShader(programHandle, fragmentShader);
    glLinkProgram(programHandle);
 
    GLint linkSuccess;
    glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }
 
    glUseProgram(programHandle);
 
    _positionSlot = glGetAttribLocation(programHandle, "Position");
    _colorSlot = glGetAttribLocation(programHandle, "SourceColor");
    glEnableVertexAttribArray(_positionSlot);
    glEnableVertexAttribArray(_colorSlot);
 
    _texCoordSlot = glGetAttribLocation(programHandle, "TexCoordIn");
    glEnableVertexAttribArray(_texCoordSlot);
 
    _textureBackground = glGetUniformLocation(programHandle, "TextureBackground");
    _textureLayer1 = glGetUniformLocation(programHandle, "TextureLayer1");
    _textureLayer2 = glGetUniformLocation(programHandle, "TextureLayer2");  
    _textureLayer3 = glGetUniformLocation(programHandle, "TextureLayer3");  
    _textureLayer4 = glGetUniformLocation(programHandle, "TextureLayer4");  
    _textureLayer5 = glGetUniformLocation(programHandle, "TextureLayer5");  
}
 
- (void)setupVBOs {
 
    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
 
    glGenBuffers(1, &_indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
 
 
}
 
- (void)render:(CADisplayLink*)displayLink {
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
 
    glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
 
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
 
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
 
    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, _bgTexture);
    glUniform1i(_textureBackground, 0); 
 
    glActiveTexture(GL_TEXTURE1); 
    glBindTexture(GL_TEXTURE_2D, _layer1Texture);
    glUniform1i(_textureLayer1, 0);  
 
    glActiveTexture(GL_TEXTURE2); 
    glBindTexture(GL_TEXTURE_2D, _layer2Texture);
    glUniform1i(_textureLayer2, 0); 
 
    glActiveTexture(GL_TEXTURE3); 
    glBindTexture(GL_TEXTURE_2D, _layer3Texture);
    glUniform1i(_textureLayer3, 0); 
 
    glActiveTexture(GL_TEXTURE4); 
    glBindTexture(GL_TEXTURE_2D, _layer4Texture);
    glUniform1i(_textureLayer4, 0); 
 
    glActiveTexture(GL_TEXTURE5); 
    glBindTexture(GL_TEXTURE_2D, _layer5Texture);
    glUniform1i(_textureLayer5, 0); 
 
    glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
 
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}
 
 
- (void)setupDisplayLink {
    CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];    
}
 
 
- (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);    
 
    CGContextTranslateCTM (spriteContext, 0, height);
    CGContextScaleCTM (spriteContext
                       , 1.0, -1.0);    
 
    CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
 
    CGContextRelease(spriteContext);
 
    GLuint texName;
    glGenTextures(1, &texName);
    glBindTexture(GL_TEXTURE_2D, texName);
 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
 
    free(spriteData);        
    return texName;
 
}
 
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {        
        [self setupLayer];        
        [self setupContext];    
        [self setupRenderBuffer];        
        [self setupFrameBuffer];     
        [self compileShaders];
        [self setupVBOs];
        [self setupDisplayLink];
        _bgTexture = [self setupTexture:@"background.tif"];
        _layer1Texture = [self setupTexture:@"layer1.tif"];
        _layer2Texture = [self setupTexture:@"layer2.tif"];
        _layer3Texture = [self setupTexture:@"layer3.tif"];
        _layer4Texture = [self setupTexture:@"layer4.tif"];
        _layer5Texture = [self setupTexture:@"layer5.tif"];
    }
    return self;
}
 
@end

Code :
// SimpleVertex.glsl
 
attribute vec4 Position;
attribute vec4 SourceColor; 
 
varying vec4 DestinationColor; 
 
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
 
void main(void) {
    DestinationColor = SourceColor; 
    gl_Position = Position;
    TexCoordOut = TexCoordIn;
}


Code :
// SimpleFragment.glsl
 
varying lowp vec2 TexCoordOut;
varying lowp vec4 DestinationColor;
 
uniform sampler2D TextureBackground;
uniform sampler2D TextureLayer1;
uniform sampler2D TextureLayer2;
uniform sampler2D TextureLayer3;
uniform sampler2D TextureLayer4;
uniform sampler2D TextureLayer5;
 
void main(void) {
    gl_FragColor = DestinationColor *  texture2D(TextureLayer1, TexCoordOut);
    // if i replace TextureLayer1 with TextureBackground, it will display that image as it is in GLTEXTURE_0
}