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
//
// 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
//
// 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
// 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;
}
// 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
}