OpenGL: MultiTexture misbehaving on ios - ios

I'm having a problem sending more than one texture to a fragment shader. Attached is a barebones test script. I'm using the GLProgram script to do the shader loading (http://iphonedevelopment.blogspot.com.au/2010/11/opengl-es-20-for-ios-chapter-4.html).
You will see in the Fragment Shader that I'm simply combining the two textures using mix, but it seems that I get the same texture for both texture1 and texture2.
// Extends GLKViewController
#import "ViewControllerImage.h"
#import <OpenGLES/ES2/glext.h>
#import "GLProgram.h"
typedef struct {
float Position[2];
float UV[2];
} VertexStruct;
const VertexStruct TestData[] = {
{{-1.0, 1.0},{0.0, 0.0}},
{{-1.0, -1.0},{0.0, 1.0}},
{{1.0, -1.0},{1.0, 1.0}}
};
const GLuint TestIndices[] = {
0,1,2
};
#implementation ViewControllerImage{
GLProgram *glProgram;
EAGLContext *context;
GLuint uvBuffer;
GLuint indexBuffer;
GLuint positionBuffer;
GLint positionID;
GLuint uvID;
GLint textureID1;
GLint textureID2;
int indiciesCount;
int vertexCount;
GLKTextureInfo *textureInfo1;
GLKTextureInfo *textureInfo2;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupContext];
[self setupShaderProgram];
[self setupGL];
}
- (void) setupContext{
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:context];
GLKView *glkView = (GLKView *)self.view;
glkView.context = context;
}
-(void) setupShaderProgram{
glProgram = [[GLProgram alloc] initWithVertexShaderFilename:#"TestVertex" fragmentShaderFilename:#"TestFragment"];
//reguster Shader attributes
[glProgram addAttribute:#"position"];
[glProgram addAttribute:#"uvPosition"];
[glProgram addAttribute:#"texture1"];
[glProgram addAttribute:#"texture2"];
// Link Program and output log if fails
if (![glProgram link]){
NSLog(#"Failed to link");
glProgram = nil;
return;
}
[glProgram use];
//retireve attribute indicies
positionID = [glProgram attributeIndex:#"position"];
uvID = [glProgram attributeIndex:#"uvPosition"];
textureID1 = [glProgram attributeIndex:#"texture1"];
textureID2 = [glProgram attributeIndex:#"texture2"];
}
-(void) setupGL{
vertexCount = sizeof(TestData)/sizeof(TestData[0]);
indiciesCount = sizeof(TestIndices)/sizeof(TestIndices[0]);
glGenBuffers(1, &positionBuffer);
glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexStruct)*vertexCount, &TestData[0].Position, GL_STATIC_DRAW);
glGenBuffers(1, &uvBuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexStruct)*vertexCount, &TestData[0].UV, GL_STATIC_DRAW);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(TestIndices), &TestIndices[0], GL_STATIC_DRAW);
//Bind indicies
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
//Bind position and UV buffers
glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glVertexAttribPointer(positionID, 2, GL_FLOAT, GL_FALSE, sizeof(VertexStruct), 0);
glEnableVertexAttribArray(positionID);
glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
glVertexAttribPointer(uvID, 2, GL_FLOAT, GL_FALSE, sizeof(VertexStruct), 0);
glEnableVertexAttribArray(uvID);
//Load textures
NSError *error;
textureInfo1 = [GLKTextureLoader textureWithCGImage:[[UIImage imageNamed:#"image1.png"] CGImage] options:nil error:&error];
if(error.description) NSLog(#"Texture 1 error: %#", error.description);
textureInfo2 = [GLKTextureLoader textureWithCGImage:[[UIImage imageNamed:#"image2.png"] CGImage] options:nil error:&error];
if(error.description) NSLog(#"Texture 2 error: %#", error.description);
//Bind Textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(textureInfo1.target, textureInfo1.name);
glUniform1i( textureID1, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(textureInfo2.target, textureInfo2.name);
glUniform1i( textureID2, 1);
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear( GL_COLOR_BUFFER_BIT );
glDrawElements(GL_TRIANGLES, indiciesCount, GL_UNSIGNED_INT, 0);
}
#end
VertexShader:
precision mediump float;
attribute vec4 position;
attribute vec2 uvPosition;
varying vec2 uv;
void main(void){
uv = uvPosition;
gl_Position = position;
}
FragmentShader
precision mediump float;
uniform sampler2D texture1;
uniform sampler2D texture2;
varying vec2 uv;
void main(void){
gl_FragColor = mix(texture2D(texture1, uv),texture2D(texture2, uv),0.5);
}
I would really appreciate it if someone could help me with this problem I'm having. I'm sure it must be simple. I've also checked GL_MAX_TEXTURE_IMAGE_UNITS which returns 8.

Oh Snap! I figured it out. I was accessing the attribute and uniform locations incorrectly. Attributes use glBindAttribLocation and uniforms use glGetUniformLocation I'm using the GLProgram class which use convenience methods addAttribute / attributeIndex and uniformIndex
This is the offending code:
-(void) setupShaderProgram{
glProgram = [[GLProgram alloc] initWithVertexShaderFilename:#"TestVertex" fragmentShaderFilename:#"TestFragment"];
//reguster Shader attributes
[glProgram addAttribute:#"position"];
[glProgram addAttribute:#"uvPosition"];
[glProgram addAttribute:#"texture1"]; //wrong - this is not an attribute
[glProgram addAttribute:#"texture2"]; //wrong - this is not an attribute
// Link Program and output log if fails
if (![glProgram link]){
NSLog(#"Failed to link");
glProgram = nil;
return;
}
[glProgram use];
//retireve attribute indicies
positionID = [glProgram attributeIndex:#"position"];
uvID = [glProgram attributeIndex:#"uvPosition"];
textureID1 = [glProgram attributeIndex:#"texture1"]; //Wrong - this is a uniform
textureID2 = [glProgram attributeIndex:#"texture2"];//Wrong - this is a uniform
}
And this is what it should be:
-(void) setupShaderProgram{
glProgram = [[GLProgram alloc] initWithVertexShaderFilename:#"EmptyVertex" fragmentShaderFilename:#"EmptyFragment"];
//register Shader attributes
[glProgram addAttribute:#"position"];
[glProgram addAttribute:#"uvPosition"];
// Link Program and output log if fails
if (![glProgram link]){
NSLog(#"Failed to link");
glProgram = nil;
return;
}
[glProgram use];
//retrieve attribute indicies
positionID = [glProgram attributeIndex:#"position"];
uvID = [glProgram attributeIndex:#"uvPosition"];
//retrieve uniform indicies
textureID1 = [glProgram uniformIndex:#"texture1"];
textureID2 = [glProgram uniformIndex:#"texture2"];
}
I'm such a noob.

Okay, I think I have solved it. I need to start the texture unit indexing at 1 not 0. Otherwise TEXTURE1 is the same texture as TEXTURE0 !!
Incorrect:
glActiveTexture(GL_TEXTURE0);
glBindTexture(textureInfo1.target, textureInfo1.name);
glUniform1i(textureID1, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(textureInfo2.target, textureInfo2.name);
glUniform1i( textureID2, 1);
Correct:
glActiveTexture(GL_TEXTURE1);
glBindTexture(textureInfo1.target, textureInfo1.name);
glUniform1i(textureID1, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(textureInfo2.target, textureInfo2.name);
glUniform1i( textureID2, 2);
Weird.

Related

Open GL ES png texture rendering as black

I am just trying to draw a full screen .png into openGL as a texture. However, I am met with a black screen. My code works fine with jpegs, so I can only assume it's an issue with transparency.
Here is an example png, which it isn't working for: http://cl.ly/e5x4 (although it is opaque, it still doesn't render) (and no, it needs to be a .png)
Here is my code:
glView.m
struct vertex {
float position[3];
float color[4];
float texCoord[2];
};
typedef struct vertex vertex;
const vertex vertices[] = {
{{1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {1, 0}}, // BR (0)
{{1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {1, 1}}, // TR (1)
{{-1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {0, 1}}, // TL (2)
{{-1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {0, 0}}, // BL (3)
};
const GLubyte indicies[] = {
0, 1, 2,
0, 3, 2
};
#implementation glView {
EAGLContext* context;
GLuint positionSlot, colorSlot, textureCoordSlot;
GLuint texture, textureUniform;
GLuint vertexBuffer, indexBuffer;
}
-(GLuint) compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:#"glsl"];
NSError* err;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&err];
NSAssert(shaderString, #"Failed to load shader string: %#", err.localizedDescription);
GLuint shaderHandle = glCreateShader(shaderType);
const char* shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = (int)[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);
#throw NSInternalInconsistencyException;
}
return shaderHandle;
}
-(void) complileShaders {
GLuint vertexShader = [self compileShader:#"vertexShader" withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShader:#"fragmentShader" 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);
#throw NSInternalInconsistencyException;
}
glUseProgram(programHandle);
positionSlot = glGetAttribLocation(programHandle, "position");
colorSlot = glGetAttribLocation(programHandle, "sourceColor");
textureCoordSlot = glGetAttribLocation(programHandle, "texCoordIn");
glEnableVertexAttribArray(positionSlot);
glEnableVertexAttribArray(colorSlot);
glEnableVertexAttribArray(textureCoordSlot);
textureUniform = glGetUniformLocation(programHandle, "tex");
}
-(instancetype) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.layer.opaque = YES;
// Setup context
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
NSAssert(context, #"Failed to initialise context.");
NSAssert([EAGLContext setCurrentContext:context], #"Failed to set the current context.");
// Setup render buffer
GLuint colorBuffer;
glGenRenderbuffers(1, &colorBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.layer];
// Setup frame buffer
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
// Setup vertex buffer
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Setup index buffer
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies, GL_STATIC_DRAW);
[self complileShaders];
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
texture = [self loadTexture:#"justapng.png"];
[self render];
}
return self;
}
-(GLuint) loadTexture:(NSString*)fileName {
UIImage* textureImage = [UIImage imageNamed:fileName];
NSAssert1(textureImage, #"Unable to load texture %#.", fileName);
return [self loadTextureFromImage:textureImage];
}
-(GLuint) loadTextureFromImage:(UIImage*)image {
CGImageRef textureImage = image.CGImage;
size_t width = CGImageGetWidth(textureImage);
size_t height = CGImageGetHeight(textureImage);
GLubyte* spriteData = (GLubyte*) malloc(width*height*4);
CGColorSpaceRef cs = CGImageGetColorSpace(textureImage);
CGContextRef c = CGBitmapContextCreate(spriteData, width, height, 8, width*4, cs, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(cs);
CGContextDrawImage(c, (CGRect){CGPointZero, {width, height}}, textureImage);
CGContextRelease(c);
GLuint glTex;
glGenTextures(1, &glTex);
glBindTexture(GL_TEXTURE_2D, glTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return glTex;
}
-(void) render {
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);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(textureUniform, 0);
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), 0);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*3));
glVertexAttribPointer(textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*7));
glDrawElements(GL_TRIANGLES, sizeof(indicies)/sizeof(indicies[0]), GL_UNSIGNED_BYTE, 0);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
#end
vertexShader.glsl
attribute vec4 position;
attribute vec4 sourceColor;
varying vec4 destinationColor;
attribute vec2 texCoordIn;
varying vec2 texCoordOut;
void main() {
destinationColor = sourceColor;
gl_Position = position;
texCoordOut = texCoordIn;
}
fragmentShader.glsl
varying lowp vec4 destinationColor;
varying lowp vec2 texCoordOut;
uniform sampler2D tex;
void main() {
gl_FragColor = destinationColor*texture2D(tex, texCoordOut);
}
Sorry for dumping all this code, but I am unsure where the problem is originating from. Any ideas as to what I am doing wrong?
This appears to be the classic non-power-of-two issue (eg. see Android OpenGL2.0 showing black textures). The texture you linked is 1005x335.
The OpenGL ES 2.0 specification only allows for NPOT textures to be 'complete' with glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S/T, GL_CLAMP_TO_EDGE). Various extensions allow NPOT texturing, however, your device doesn't necessarily support these, and likely doesn't if you're getting black textures. If a texture isn't complete, it is sampled as black per the spec.

GPUImageMovieWriter changing filter settings. When and How?

I can't figure out what happens when I'm starting recording with GPUImageMovieWriter. While it's just preview everything is OK, but when I'm pressing record button my filter breaks up. I can't find how GPUImageMovieWriter changing my filter, help me.
My start record action:
- (void)cameraButton:(id)sender
{
// Recording.
NSString *pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Movie.m4v"];
unlink([pathToMovie UTF8String]);
NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];
self.movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(1080.0, 1920.0)];
self.movieWriter.encodingLiveVideo = YES;
self.movieWriter.shouldPassthroughAudio = YES;
[self.blendFilter addTarget:self.movieWriter];
self.videoCamera.audioEncodingTarget = self.movieWriter;
// Start recording.
[self.movieWriter startRecording];
double delayInSeconds = 10.0;
dispatch_time_t stopTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(stopTime, dispatch_get_main_queue(), ^(void){
[self.blendFilter removeTarget:self.movieWriter];
self.videoCamera.audioEncodingTarget = nil;
[self.movieWriter finishRecording];
UISaveVideoAtPathToSavedPhotosAlbum(pathToMovie, nil, NULL, NULL);
NSLog(#"Movie completed");
});
}
My filter cropping input image by shape which is uniform sampler2D shape.
My filter code:
#import "GPUImageShapeCropFilter.h"
NSString *const kGPUImageShapeCropShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float aspectRatio;
uniform highp vec2 center;
uniform highp float radius;
uniform highp float scale;
uniform sampler2D shape;
void main()
{
highp vec2 textureCoordinateToUse = vec2(textureCoordinate.x, (textureCoordinate.y * aspectRatio + 0.5 - 0.5 * aspectRatio));
lowp vec4 shapeColor = texture2D(shape, textureCoordinateToUse);
textureCoordinateToUse = textureCoordinate;
textureCoordinateToUse -= center;
textureCoordinateToUse = textureCoordinateToUse * scale;
textureCoordinateToUse += center;
if (shapeColor.w > 0.0) {
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinateToUse);
textureColor.w = shapeColor.w;
gl_FragColor = textureColor;
} else {
gl_FragColor = shapeColor;
}
}
);
#interface GPUImageShapeCropFilter ()
{
GLint _aspectRatioUniform;
GLint _radiusUniform;
GLint _centerUniform;
GLint _scaleUniform;
GLuint _oldTextureIndex;
}
- (void)adjustAspectRatio;
#property (readwrite, nonatomic) CGFloat aspectRatio;
#end
#implementation GPUImageShapeCropFilter
#synthesize aspectRatio = _aspectRatio;
#synthesize center = _center;
#synthesize radius = _radius;
#synthesize scale = _scale;
#pragma mark -
#pragma mark Initialization and teardown
- (id)init
{
self = [super initWithFragmentShaderFromString:kGPUImageShapeCropShaderString];
if (!self) {
return nil;
}
// Get uniforms.
_aspectRatioUniform = [filterProgram uniformIndex: #"aspectRatio"];
_radiusUniform = [filterProgram uniformIndex: #"radius"];
_scaleUniform = [filterProgram uniformIndex: #"scale"];
_centerUniform = [filterProgram uniformIndex: #"center"];
// Set default values.
self.scale = 0.5;
self.center = CGPointMake(0.5, 0.5);
[self setShape:[UIImage imageNamed:#"circle"]];
return self;
}
- (void)setShape:(UIImage *)shape
{
[self setTexture:shape forUniform:#"shape"];
}
- (void)setTexture:(UIImage *)image forUniform:(NSString *)uniform
{
// Find texture size.
CGSize sizeOfImage = [image size];
CGFloat scaleOfImage = [image scale];
CGSize pixelSizeOfImage = CGSizeMake(scaleOfImage * sizeOfImage.width, scaleOfImage * sizeOfImage.height);
// Create context.
GLubyte * spriteData = (GLubyte *)malloc(pixelSizeOfImage.width * pixelSizeOfImage.height * 4 * sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, pixelSizeOfImage.width, pixelSizeOfImage.height, 8, pixelSizeOfImage.width * 4, CGImageGetColorSpace(image.CGImage), (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
// Draw image into context.
CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, pixelSizeOfImage.width, pixelSizeOfImage.height), image.CGImage);
// Get uniform of texture
GLuint uniformIndex = [filterProgram uniformIndex:uniform];
// Generate texture.
GLuint textureIndex;
glGenTextures(1, &textureIndex);
glBindTexture(GL_TEXTURE_2D, textureIndex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Create texture.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pixelSizeOfImage.width, pixelSizeOfImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureIndex);
// "Send" to shader.
glUniform1i(uniformIndex, 1);
// Remove old texture if it is.
if (_oldTextureIndex) {
glDeleteTextures(1, &_oldTextureIndex);
}
_oldTextureIndex = textureIndex;
free(spriteData);
CGContextRelease(spriteContext);
}
#pragma mark -
#pragma mark Accessors
- (void)adjustAspectRatio;
{
if (GPUImageRotationSwapsWidthAndHeight(inputRotation)) {
[self setAspectRatio:inputTextureSize.width / inputTextureSize.height];
} else {
[self setAspectRatio:inputTextureSize.height / inputTextureSize.width];
}
}
- (void)forceProcessingAtSize:(CGSize)frameSize;
{
[super forceProcessingAtSize:frameSize];
[self adjustAspectRatio];
}
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex
{
CGSize oldInputSize = inputTextureSize;
[super setInputSize:newSize atIndex:textureIndex];
if ( (!CGSizeEqualToSize(oldInputSize, inputTextureSize)) && (!CGSizeEqualToSize(newSize, CGSizeZero)) ) {
[self adjustAspectRatio];
}
}
- (void)setAspectRatio:(CGFloat)newValue
{
_aspectRatio = newValue;
[self setFloat:self.aspectRatio forUniform:_aspectRatioUniform program:filterProgram];
}
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
{
[super setInputRotation:newInputRotation atIndex:textureIndex];
[self setCenter:self.center];
[self adjustAspectRatio];
}
- (void)setScale: (CGFloat)newValue;
{
_scale = newValue;
[self setFloat:self.scale forUniform:_scaleUniform program:filterProgram];
}
- (void)setCenter: (CGPoint)newValue;
{
_center = newValue;
CGPoint rotatedPoint = [self rotatedPoint:self.center forRotation:inputRotation];
[self setPoint:rotatedPoint forUniform:_centerUniform program:filterProgram];
}
#end

Rendering OpenGL ES 2.0 to UIImage

I am interested in writing some code which processes an image using OpenGL ES 2.0 and then reads out the image back to memory again (to eventually be saved). I have some other code which does some more complex processing and just renders the image to display which works. I now want some code which runs the same processing but just saves the image. It doesn't need to be rendered to the screen.
I have created a frame buffer object which outputs to a texture. I then want to use glReadPixels to get the contents of the frame buffer back into memory. The following code snippet should just take a UIImage, resize it to fit onto a 512x512 canvas and then write it out as a 512x512 UIImage. I can't seem to get anything meaningful to display though. If I use an image smaller than 512x512 it does seem to render, but it's as if it is drawing straight from the texture storage (because I pad images to always be a power of 2 size). The image displays but if I change any of the drawing code, it doesn't affect it.
I'd really appreciate if you could give me some insight?
Thanks!
Here is my Vertex Shader:
attribute vec4 Position;
attribute vec4 SourceColour;
varying vec4 DestinationColour;
uniform vec2 ScreenSize;
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
void main(void) {
DestinationColour = SourceColour;
vec4 newPosition = Position;
newPosition.x = Position.x / ScreenSize.x;
newPosition.y = Position.y / ScreenSize.y;
gl_Position = Position;
TexCoordOut = TexCoordIn;
}
Here is my fragment shader:
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void)
{
lowp vec4 pixel = texture2D(Texture, TexCoordOut);
gl_FragColor = pixel;
}
Here is the main code body:
#import "SERootViewController.h"
#import <OpenGLES/ES2/gl.h>
typedef struct {
CGSize size;
CGPoint percentage;
GLuint id;
} Texture;
typedef struct {
float Position[3];
float TexCoord[2];
} Vertex;
size_t nextPowerOfTwo(size_t n)
{
size_t po2 = 2;
while(po2<n) {
po2 = po2*2;
}
return po2;
}
const GLubyte Indices[] = {
0, 1, 2,
2, 3, 0
};
#interface SERootViewController ()
{
EAGLContext *_context;
//OpenGL
GLuint _vertexBufferHandle;
GLuint _indexBufferHandle;
GLuint _vertexShaderHandle;
GLuint _fragmentShaderHandle;
GLuint _programHandle;
GLuint _positionHandle;
GLuint _texCoordHandle;
GLuint _textureHandle;
GLuint _screenSizeHandle;
GLuint _fbo;
GLuint _fboTexture;
Texture _imageTexture;
GLfloat _defaultScale;
Vertex *_vertices;
CGSize _screenSize;
//Cocoa
UIImageView *_imageView;
}
#end
#implementation SERootViewController
//////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Lifecycle
//////////////////////////////////////////////////////////////////////////
- (id)init
{
if ( (self = [super init]) != nil)
{
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
_vertices = malloc(sizeof(Vertex) * 4);
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self __setupFrameBuffer];
[self __setupShaders];
CGSize size = CGSizeMake(512, 512);
[self __setScreenSize:size];
UIImage *image = [UIImage imageNamed:#"christmas.jpg"];
GLubyte *bytes = [self __bytesFromImage:image];
_imageTexture = [self __newTexture:bytes size:image.size];
free(bytes);
[self __setupVBOs:_imageTexture screenSize:size];
[self __renderTexture:_imageTexture];
[self __bindFrameBuffer];
GLubyte *imageBytes = [self __renderImage:size];
UIImage *newImage = [self __imageFromBytes:imageBytes size:size];
free(imageBytes);
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
_imageView.image = newImage;
_imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:_imageView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Methods
//////////////////////////////////////////////////////////////////////////
- (void)__setScreenSize:(CGSize)screenSize
{
_screenSize = screenSize;
glUniform2f(_screenSizeHandle, _screenSize.width, _screenSize.height);
}
- (void)__setupFrameBuffer
{
glGenFramebuffers(1, &_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &_fboTexture);
glBindTexture(GL_TEXTURE_2D, _fboTexture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_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, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _fboTexture, 0);
}
- (void)__bindFrameBuffer
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _fboTexture);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _fboTexture, 0);
}
- (GLuint) __compileShader:(NSString *)shaderStr type:(GLenum) type
{
const char *str = shaderStr.UTF8String;
int shaderStrLength = strlen(str);
GLuint shaderHandle = glCreateShader(type);
glShaderSource(shaderHandle, 1, &str, &shaderStrLength);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE)
{
GLchar messages[512];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSLog(#"Shader Error: %s", messages);
}
return shaderHandle;
}
- (void)__setupVBOs:(Texture)texture screenSize:(CGSize)screenSize
{
glDeleteBuffers(1, &_vertexBufferHandle);
glDeleteBuffers(1, &_indexBufferHandle);
_defaultScale = MIN( (screenSize.height/texture.size.height), (screenSize.width/texture.size.width) );
GLfloat width = texture.size.width*_defaultScale;
GLfloat height = texture.size.height*_defaultScale;
Vertex vertices[] = {{{ width/2, height/2, 0}, {texture.percentage.x, texture.percentage.y}},
{{ width/2, 0, 0}, {texture.percentage.x, 0}},
{{0, 0, 0}, {0, 0}},
{{0, height/2, 0}, {0, texture.percentage.y}}
};
memcpy(_vertices, vertices, sizeof(vertices));
glGenBuffers(1, &_vertexBufferHandle);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferHandle);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), _vertices, GL_STATIC_DRAW);
glGenBuffers(1, &_indexBufferHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
- (void) __setupShaders
{
NSString *vertexFilePath = [[NSBundle mainBundle] pathForResource:#"vertexShader" ofType:#"glsl"];
NSString *vertexShader = [NSString stringWithContentsOfFile:vertexFilePath encoding:NSUTF8StringEncoding error:nil];
NSString *fragmentFilePath = [[NSBundle mainBundle] pathForResource:#"fragmentShader" ofType:#"glsl"];
NSString *fragmentShader = [NSString stringWithContentsOfFile:fragmentFilePath encoding:NSUTF8StringEncoding error:nil];
_vertexShaderHandle = [self __compileShader:vertexShader type:GL_VERTEX_SHADER];
_fragmentShaderHandle = [self __compileShader:fragmentShader type:GL_FRAGMENT_SHADER];
_programHandle = glCreateProgram();
glAttachShader(_programHandle, _vertexShaderHandle);
glAttachShader(_programHandle, _fragmentShaderHandle);
glLinkProgram(_programHandle);
GLint linkSuccess;
glGetProgramiv(_programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE)
{
GLchar messages[512];
glGetProgramInfoLog(_programHandle, sizeof(messages), 0, &messages[0]);
NSLog(#"GLSL ERROR: %s", messages);
}
glUseProgram(_programHandle);
_positionHandle = glGetAttribLocation(_programHandle, "Position");
_texCoordHandle = glGetAttribLocation(_programHandle, "TexCoordIn");
_screenSizeHandle = glGetUniformLocation(_programHandle, "ScreenSize");
_textureHandle = glGetUniformLocation(_programHandle, "Texture");
glEnableVertexAttribArray(_positionHandle);
glEnableVertexAttribArray(_texCoordHandle);
}
- (Texture)__newTexture:(void *)bytes size:(CGSize)size
{
Texture texture;
size_t wpo2 = nextPowerOfTwo(size.width);
size_t hpo2 = nextPowerOfTwo(size.height);
texture.size = size;
texture.percentage = CGPointMake((float)size.width / (float)wpo2, (float)size.height / (float)hpo2);
void * texData = (GLubyte *) malloc(wpo2*hpo2*4*sizeof(GLubyte));
memset(texData, 1, sizeof(GLubyte)*size.width*4);
for(GLuint i=0;i<size.height;i++)
{
memcpy(&texData[wpo2*i*4], &bytes[(int)size.width*i*4], sizeof(GLubyte)*(int)size.width*4);
}
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &texture.id);
glBindTexture(GL_TEXTURE_2D, texture.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
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_RGBA, (GLsizei)wpo2, (GLsizei)hpo2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
free(texData);
return texture;
}
-(void) __renderTexture:(Texture) texture
{
glClearColor(0.0f, 0.5, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glVertexAttribPointer(_positionHandle, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_texCoordHandle, 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture.id);
glUniform1i(_textureHandle, 1);
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),GL_UNSIGNED_BYTE, 0);
}
- (GLubyte *)__renderImage:(CGSize)size
{
GLubyte *image = malloc(size.width*size.height*4*sizeof(GLubyte));
glReadPixels(0, 0, size.width, size.height, GL_RGBA, GL_UNSIGNED_BYTE, image);
return image;
}
- (GLubyte *)__bytesFromImage:(UIImage *)image
{
GLint maxTextureSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
CGFloat maxSize = MAX(image.size.width, image.size.height);
CGFloat width = image.size.width;
CGFloat height = image.size.height;
if (maxSize > maxTextureSize)
{
CGFloat scale = maxTextureSize/maxSize;
width = roundf(width*scale);
height = roundf(height*scale);
}
CGImageRef spriteImage = image.CGImage;
if (!spriteImage) {
NSLog(#"Failed to load image");
exit(1);
}
GLubyte * imgData = (GLubyte *) malloc(width*height*4*sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(imgData, width, height, 8, width*4,
CGImageGetColorSpace(spriteImage), (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
UIGraphicsPushContext(spriteContext);
CGContextSaveGState(spriteContext);
[image drawInRect:CGRectMake(0, 0, width, height)];
CGContextRestoreGState(spriteContext);
UIGraphicsPopContext();
CGContextRelease(spriteContext);
return imgData;
}
- (UIImage *)__imageFromBytes:(GLubyte *)bytes size:(CGSize)size
{
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bytes, (size.width * size.height * 4), NULL);
// set up for CGImage creation
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * size.width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef imageRef = CGImageCreate(size.width, size.height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// make UIImage from CGImage
UIImage *newUIImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return newUIImage;
}
#end

OpenGL ES 2.0 draw simple triangle not showing anything

I am completely out of ideas.
I have the OpenGL ES 2.0 Programming Guide book, I have Apple's template OpenGL ES 2.0 code, I have followed this page: http://open.gl/drawing
I can't seem to get a simple triangle drawn, I am not sure what I am doing wrong.
Note: I have a GameViewController.xib file too that is a subclass of GLKView just like Apple's template code.
My vertex shader:
attribute vec4 position;
void main()
{
gl_Position = position;
}
My fragment shader:
void main()
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
My GameViewController Header File:
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#interface GameViewController : GLKViewController
#end
My GameViewController Implementation File:
#import "GameViewController.h"
const GLfloat vertices[] =
{
0.0f, 0.5f,
-0.5f, -0.5f,
0.5f, -0.5f
};
// Class extension to keep data private
#interface GameViewController()
{
// vertex buffer object
GLuint vbo;
// vertex array object
GLuint vao;
// shader program
GLuint shaderProgram;
}
#property (strong, nonatomic) EAGLContext *context;
#property (strong, nonatomic) GLKBaseEffect *effect;
#end
#implementation GameViewController
// setup our OpenGL context
-(void)viewDidLoad
{
[super viewDidLoad];
[self setupContext];
// -------------------------------------------------------------
// create a vertex array object that will hold
// all the linking between attributes and vertex data
// -------------------------------------------------------------
[self createVertexArrayObject];
[self createVertexBufferObject];
[self loadShaders];
[self checkErrors];
}
-(void)checkErrors
{
GLenum error = glGetError();
switch (error) {
case GL_NO_ERROR:
NSLog(#"No errors");
break;
case GL_INVALID_ENUM:
NSLog(#"An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_INVALID_VALUE:
NSLog(#"A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_INVALID_OPERATION:
NSLog(#"The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
NSLog(#"The frameBuffer object is not complete. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_OUT_OF_MEMORY:
NSLog(#"There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded.");
break;
case GL_STACK_UNDERFLOW:
NSLog(#"An attempt has been made to perform an operation that would cause an internal stack to underflow.");
break;
case GL_STACK_OVERFLOW:
NSLog(#"An attempt has been made to perform an operation that would cause an internal stack to overflow.");
break;
default:
break;
}
}
#pragma mark - Setup Context -
-(void)setupContext
{
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if(!self.context)
{
NSLog(#"Failed to create OpenGL ES Context");
}
// tell our view the context is an OpenGL context
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[EAGLContext setCurrentContext:self.context];
self.effect = [[GLKBaseEffect alloc] init];
glEnable(GL_DEPTH_TEST);
}
#pragma mark - Vertex Array Object Methods -
-(void)createVertexArrayObject
{
glGenVertexArraysOES(1, &vao);
glBindVertexArrayOES(vao);
}
#pragma mark - Create Vertex Buffer Object Method -
-(void)createVertexBufferObject;
{
glGenBuffers(1, &vbo); // Generate 1 memory buffer for the VBO
// Make our VBO the current buffer object to receive data
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// -------------------------------------------------------------
// Start copying data to our VBO
//
// GL_STATIC_DRAW will upload once and drawn many times
// -------------------------------------------------------------
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
}
#pragma mark - Shader Code -
-(void)loadShaders
{
// load both vertex shader and fragment shader
GLuint vertexShader = [self compileVertexShader];
GLuint fragmentShader = [self compileFragmentShader];
// create a shader program from the vertex shader and fragment shader
shaderProgram = [self combineVertexShader:vertexShader AndFragmentShader:fragmentShader];
// linking vertex data and attributes
GLint positionAttrib = glGetAttribLocation(shaderProgram, "position");
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionAttrib);
}
-(GLuint)compileVertexShader
{
// get the path to the shader file as a C string
const GLchar *vertexShaderPath = (GLchar *)[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"vsh"]
encoding:NSUTF8StringEncoding
error:nil] UTF8String];
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderPath, NULL);
glCompileShader(vertexShader);
// Checking if shader compiled properly
GLint status;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Vertex shader compiled correctly");
}
else
{
NSLog(#"Vertex shader compiled failed");
}
GLint logLength;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(vertexShader, logLength, &logLength, log);
NSLog(#"Vertex Shader compile log:\n%s", log);
free(log);
}
return vertexShader;
}
-(GLuint)compileFragmentShader
{
// get the path to the shader as a C string
const GLchar *fragmentShaderPath = (GLchar *)[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"fsh"]
encoding:NSUTF8StringEncoding
error:nil] UTF8String];
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderPath, NULL);
glCompileShader(fragmentShader);
// Checking if shader compiled properly
GLint status;
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Fragment shader compiled correctly");
}
else
{
NSLog(#"Fragment shader compiled failed");
}
GLint logLength;
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(fragmentShader, logLength, &logLength, log);
NSLog(#"Fragment Shader compile log:\n%s", log);
free(log);
}
return fragmentShader;
}
-(void)linkProgram:(GLuint)paramShaderProgram
{
// link shader to program now and become active shader program
glLinkProgram(paramShaderProgram);
GLint status;
// test if the program linked correctly
glGetProgramiv(paramShaderProgram, GL_LINK_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Shader program linked correctly");
}
else
{
NSLog(#"Shader program linked failed");
}
}
-(void)validateProgram:(GLuint)paramShaderProgram
{
GLint status, logLength;
glValidateProgram(paramShaderProgram);
glGetProgramiv(paramShaderProgram, GL_VALIDATE_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Shader program validated correctly");
}
else
{
NSLog(#"Shader program validate failed");
}
glGetProgramiv(paramShaderProgram, GL_INFO_LOG_LENGTH, &logLength);
if(logLength > 0)
{
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(paramShaderProgram, logLength, &logLength, log);
NSLog(#"Program validate log:\n%s", log);
free(log);
}
}
-(GLuint)combineVertexShader:(GLuint)paramVertexShader AndFragmentShader:(GLuint)paramFragmentShader
{
GLuint newShaderProgram = glCreateProgram();
glAttachShader(newShaderProgram, paramVertexShader);
glAttachShader(newShaderProgram, paramFragmentShader);
[self linkProgram:newShaderProgram];
[self validateProgram:newShaderProgram];
// start using shader now, will use the active shader program
glUseProgram(newShaderProgram);
return newShaderProgram;
}
#pragma mark - GLKViewController Render Delegate Methods -
-(void)setupViewport
{
glViewport(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
self.effect.transform.projectionMatrix = GLKMatrix4MakeOrtho(-3, 3, -2, 2, 1, -1);
}
-(void)update
{
[self setupViewport];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 3);
}
#pragma mark - Cleanup Memory -
-(void)tearDownContext
{
[EAGLContext setCurrentContext:self.context];
// delete vertex buffer object and vertex array object
glDeleteBuffers(1, &vbo);
glDeleteVertexArraysOES(1, &vao);
// delete shader program
if(shaderProgram)
{
glDeleteProgram(shaderProgram);
shaderProgram = 0;
}
// unset OpenGL context
if ([EAGLContext currentContext] == self.context)
{
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
-(void)dealloc
{
[self tearDownContext];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if([self isViewLoaded] && self.view.window == nil)
{
self.view = nil;
[self tearDownContext];
if([EAGLContext currentContext] == self.context)
{
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
// Dispose of any resources that can be recreated
}
#end
All I see is my grey background screen that I cleared color to.
If anyone can point out where I went wrong, that would be great.
EDIT
I've decided to put my personal dislike for Ray's site aside and just bit the bullet, went through this tutorial:
http://www.raywenderlich.com/3664/opengl-es-2-0-for-iphone-tutorial
This tutorial does not use GLKit which is an extra bonus because the learner write their own frame buffer and render buffer, present them to screen.
I feel this is closer to the OpenGL ES 2.0 pipeline than using GLKit.
As Ricardo suggested, using GLKit is more effort to understand than its worth at this point in time.
I also strongly recommend using above Ray's website tutorial link to get a working program going rather than trying to follow Apple's template code.
In my case, Apple's template code and the book had too much contradiction, causing many wasted hours.
I have also solved it using GLKit for those that are still interested in using the GLKit method.
The solution was a mix of setting my projection matrix and binding the correct vertex shader position attribute location.
Also one very important note I want to point out is:
MAKE SURE COPY BUNDLE RESOURCES FOR TARGET HAS VERTEX AND FRAGMENT SHADER!
I was at the point where the triangle was a black color and I couldn't change the color of my triangle no matter what I typed in my fragment shader file. The problem was these shader files are not copied to the bundle resource by default.
Anyhow, my working updated code is provided in full below:
#import "GameViewController.h"
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
// Uniform index.
enum
{
UNIFORM_MODELVIEWPROJECTION_MATRIX,
UNIFORM_NORMAL_MATRIX,
NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];
// Attribute index.
enum
{
ATTRIB_VERTEX,
ATTRIB_NORMAL,
NUM_ATTRIBUTES
};
const GLfloat vertices[] =
{
0.0f, 0.5f,
-0.5f, -0.5f,
0.5f, -0.5f
};
// Class extension to keep data private
#interface GameViewController()
{
// vertex buffer object
GLuint vbo;
// vertex array object
GLuint vao;
// shader program
GLuint shaderProgram;
GLKMatrix4 modelViewProjectionMatrix;
GLKMatrix3 normalMatrix;
}
#property (strong, nonatomic) EAGLContext *context;
#property (strong, nonatomic) GLKBaseEffect *effect;
#end
#implementation GameViewController
// setup our OpenGL context
-(void)viewDidLoad
{
[super viewDidLoad];
[self setupContext];
[self loadShaders];
// -------------------------------------------------------------
// create a vertex array object that will hold
// all the linking between attributes and vertex data
// -------------------------------------------------------------
[self createVertexArrayObject];
[self createVertexBufferObject];
[self linkAttributes];
[self checkErrors];
}
-(void)checkErrors
{
GLenum error = glGetError();
switch (error) {
case GL_NO_ERROR:
NSLog(#"No errors");
break;
case GL_INVALID_ENUM:
NSLog(#"An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_INVALID_VALUE:
NSLog(#"A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_INVALID_OPERATION:
NSLog(#"The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
NSLog(#"The frameBuffer object is not complete. The offending command is ignored and has no other side effect than to set the error flag.");
break;
case GL_OUT_OF_MEMORY:
NSLog(#"There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded.");
break;
case GL_STACK_UNDERFLOW:
NSLog(#"An attempt has been made to perform an operation that would cause an internal stack to underflow.");
break;
case GL_STACK_OVERFLOW:
NSLog(#"An attempt has been made to perform an operation that would cause an internal stack to overflow.");
break;
default:
break;
}
}
#pragma mark - Setup Context -
-(void)setupContext
{
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if(!self.context)
{
NSLog(#"Failed to create OpenGL ES Context");
}
// tell our view the context is an OpenGL context
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[EAGLContext setCurrentContext:self.context];
self.effect = [[GLKBaseEffect alloc] init];
//glEnable(GL_DEPTH_TEST);
}
#pragma mark - Vertex Array Object Methods -
-(void)createVertexArrayObject
{
glGenVertexArraysOES(1, &vao);
glBindVertexArrayOES(vao);
}
#pragma mark - Create Vertex Buffer Object Method -
-(void)createVertexBufferObject;
{
glGenBuffers(1, &vbo); // Generate 1 memory buffer for the VBO
// Make our VBO the current buffer object to receive data
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// -------------------------------------------------------------
// Start copying data to our VBO
//
// GL_STATIC_DRAW will upload once and drawn many times
// -------------------------------------------------------------
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
}
-(void)linkAttributes
{
/*
// linking vertex data and attributes
GLint positionAttrib = glGetAttribLocation(shaderProgram, "position");
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(positionAttrib);
*/
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribPosition);
glBindVertexArrayOES(0);
}
#pragma mark - Shader Code -
-(void)loadShaders
{
shaderProgram = glCreateProgram();
// load both vertex shader and fragment shader
GLuint vertexShader = [self compileVertexShader];
GLuint fragmentShader = [self compileFragmentShader];
// create a shader program from the vertex shader and fragment shader
[self combineVertexShader:vertexShader AndFragmentShader:fragmentShader];
[self linkProgram:shaderProgram];
// Release vertex and fragment shaders.
if (vertexShader) {
glDetachShader(shaderProgram, vertexShader);
glDeleteShader(vertexShader);
}
if (fragmentShader) {
glDetachShader(shaderProgram, fragmentShader);
glDeleteShader(fragmentShader);
}
//NSLog(#"uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] before = %d", uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX]);
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(shaderProgram, "modelViewProjectionMatrix");
//NSLog(#"uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] after = %d", uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX]);
}
-(GLuint)compileVertexShader
{
// get the path to the shader file as a C string
const GLchar *vertexShaderPath = (GLchar *)[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"vsh"]
encoding:NSUTF8StringEncoding
error:nil] UTF8String];
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderPath, NULL);
glCompileShader(vertexShader);
// Checking if shader compiled properly
GLint status;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Vertex shader compiled correctly");
}
else
{
NSLog(#"Vertex shader compiled failed");
}
GLint logLength;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(vertexShader, logLength, &logLength, log);
NSLog(#"Vertex Shader compile log:\n%s", log);
free(log);
}
return vertexShader;
}
-(GLuint)compileFragmentShader
{
// get the path to the shader as a C string
const GLchar *fragmentShaderPath = (GLchar *)[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"fsh"]
encoding:NSUTF8StringEncoding
error:nil] UTF8String];
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderPath, NULL);
glCompileShader(fragmentShader);
// Checking if shader compiled properly
GLint status;
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Fragment shader compiled correctly");
}
else
{
NSLog(#"Fragment shader compiled failed");
}
GLint logLength;
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(fragmentShader, logLength, &logLength, log);
NSLog(#"Fragment Shader compile log:\n%s", log);
free(log);
}
return fragmentShader;
}
-(void)combineVertexShader:(GLuint)paramVertexShader AndFragmentShader:(GLuint)paramFragmentShader
{
glAttachShader(shaderProgram, paramVertexShader);
glAttachShader(shaderProgram, paramFragmentShader);
glBindAttribLocation(shaderProgram, GLKVertexAttribPosition, "position");
//[self validateProgram:shaderProgram];
}
-(void)linkProgram:(GLuint)paramShaderProgram
{
// link shader to program now and become active shader program
glLinkProgram(paramShaderProgram);
GLint status;
// test if the program linked correctly
glGetProgramiv(paramShaderProgram, GL_LINK_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Shader program linked correctly");
}
else
{
NSLog(#"Shader program linked failed");
}
GLint logLength;
glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(shaderProgram, logLength, &logLength, log);
NSLog(#"Program link log:\n%s", log);
free(log);
}
}
-(void)validateProgram:(GLuint)paramShaderProgram
{
GLint status, logLength;
glValidateProgram(paramShaderProgram);
glGetProgramiv(paramShaderProgram, GL_VALIDATE_STATUS, &status);
if(status == GL_TRUE)
{
NSLog(#"Shader program validated correctly");
}
else
{
NSLog(#"Shader program validate failed");
}
glGetProgramiv(paramShaderProgram, GL_INFO_LOG_LENGTH, &logLength);
if(logLength > 0)
{
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(paramShaderProgram, logLength, &logLength, log);
NSLog(#"Program validate log:\n%s", log);
free(log);
}
}
#pragma mark - GLKViewController Render Delegate Methods -
-(void)setupViewport
{
//float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
//GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 1.0f, -10.0f);
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(-1, 1, -1, 1, 1, -1);
self.effect.transform.projectionMatrix = projectionMatrix;
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 0.0f);
self.effect.transform.modelviewMatrix = modelViewMatrix;
modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, GL_FALSE, modelViewProjectionMatrix.m);
glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0, normalMatrix.m);
}
-(void)update
{
[self setupViewport];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArrayOES(vao);
[self.effect prepareToDraw];
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
#pragma mark - Cleanup Memory -
-(void)tearDownContext
{
[EAGLContext setCurrentContext:self.context];
// delete vertex buffer object and vertex array object
glDeleteBuffers(1, &vbo);
glDeleteVertexArraysOES(1, &vao);
// delete shader program
if(shaderProgram)
{
glDeleteProgram(shaderProgram);
shaderProgram = 0;
}
// unset OpenGL context
if ([EAGLContext currentContext] == self.context)
{
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
-(void)dealloc
{
[self tearDownContext];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if([self isViewLoaded] && self.view.window == nil)
{
self.view = nil;
[self tearDownContext];
if([EAGLContext currentContext] == self.context)
{
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
// Dispose of any resources that can be recreated
}
#end
Hope that helps everyone else.
Forget about Apple's OpenGL ES 2.0 template code. If you are a beginner, there is far too much going on in there to understand properly and it mixes OpenGL ES 1.1 (GLKBaseEffect) and 2.0 (shaders).
You're better off creating an app from scratch and getting to know the pros/cons of GLKit and shaders.
This tutorial gives a great introduction to GLKit using the fixed-pipeline functionality of GLKBaseEffect:
http://www.raywenderlich.com/5223/beginning-opengl-es-2-0-with-glkit-part-1
http://www.raywenderlich.com/5235/beginning-opengl-es-2-0-with-glkit-part-2
And if you want more in-depth knowledge and learn how to use shaders in an iPhone environment, I strongly recommend this book:
http://ofps.oreilly.com/titles/9780596804824/
Ultimately, you would want to combine a GLKView to bypass all the buffer boilerplate code, and shaders to have the best control over your program.
OpenGL ES has a difficult learning curve, but once you get over the first hump things become a lot clearer.

OpenGL ES 2.0 How to Draw Multiple VBOs with Different Shader Programs in iOS

I'm trying to hack the generic Xcode iOS OpenGL Game template to draw two vertex buffer objects and render them with different GLSL shaders.
I 'think' I'm rendering the two VBOs correctly? (Because I see them both when running both VBOs through the first shader program) However, my second shader does not appear to be rendering my second object at all.
Here is the vertex data for the two squares:
GLfloat gCubeVertexData[36] =
{
// Data layout for each line below is:
// positionX, positionY, positionZ, normalX, normalY, normalZ,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f
};
GLfloat fooVertexData[36] =
{
// Data layout for each line below is:
// positionX, positionY, positionZ, normalX, normalY, normalZ
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f
};
Here is where I am trying to generate two VBOs and bind them to the data. Not sure what the purpose of the 'glBindVertexArrayOES(0)' is at the end though?:
- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
[self loadShaders];
//---- First Vertex Array Object --------
glGenVertexArraysOES(1, &_vertexArray1);
glGenBuffers(1, &_vertexBuffer1);
glBindVertexArrayOES(_vertexArray1);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer1);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData), gCubeVertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
//----- Second Vertex Array Object ----------
glGenVertexArraysOES(1, &_vertexArray2);
glGenBuffers(1, &_vertexBuffer2);
glBindVertexArrayOES(_vertexArray2);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBufferData(GL_ARRAY_BUFFER, sizeof(fooVertexData), fooVertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArrayOES(0);
}
I'm using this update code to animate the model-view-projection matrixes:
- (void)update
{
_rotation += self.timeSinceLastUpdate * 0.2f;
float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(-1.0f, 1.0f, -1.0f / aspect, 1.0f / aspect, -10.0f, 10.0f);
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.5f, 0.0f, 0.0f);
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, GLKMatrix4MakeZRotation(0.0 - _rotation));
_modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
GLKMatrix4 modelViewMatrix2 = GLKMatrix4MakeTranslation(-0.5f, 0.0f, 0.0f);
modelViewMatrix2 = GLKMatrix4Multiply(modelViewMatrix2, GLKMatrix4MakeZRotation(_rotation));
_modelViewProjectionMatrix2 = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix2);
}
When I call the '_program2' shader I don't see the second square:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArrayOES(_vertexArray1);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer1);
glUseProgram(_program);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
glDrawArrays(GL_TRIANGLES, 0, 6);
///////// second object and shader program:
glBindVertexArrayOES(_vertexArray2);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glUseProgram(_program2);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX2], 1, 0, _modelViewProjectionMatrix2.m);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
I've basically tried duplicating the code for loading the first shader, to load the second. I suspect I may be doing something wrong here.. but I'm not sure what:
- (BOOL)loadShaders
{
GLuint vertShader, fragShader, vertShader2, fragShader2;
NSString *vertShaderPathname, *fragShaderPathname, *vertShaderPathname2, *fragShaderPathname2;
// Create shader program.
_program = glCreateProgram();
// Create and compile vertex shader.
vertShaderPathname = [[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"vsh"];
if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) {
NSLog(#"Failed to compile vertex shader");
return NO;
}
// Create and compile fragment shader.
fragShaderPathname = [[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"fsh"];
if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) {
NSLog(#"Failed to compile fragment shader");
return NO;
}
// Attach vertex shader to program.
glAttachShader(_program, vertShader);
// Attach fragment shader to program.
glAttachShader(_program, fragShader);
// Bind attribute locations.
// This needs to be done prior to linking.
glBindAttribLocation(_program, ATTRIB_VERTEX, "position");
// Link program.
if (![self linkProgram:_program]) {
NSLog(#"Failed to link program: %d", _program);
if (vertShader) {
glDeleteShader(vertShader);
vertShader = 0;
}
if (fragShader) {
glDeleteShader(fragShader);
fragShader = 0;
}
if (_program) {
glDeleteProgram(_program);
_program = 0;
}
return NO;
}
// Get uniform locations.
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(_program, "modelViewProjectionMatrix");
// Release vertex and fragment shaders.
if (vertShader) {
glDetachShader(_program, vertShader);
glDeleteShader(vertShader);
}
if (fragShader) {
glDetachShader(_program, fragShader);
glDeleteShader(fragShader);
}
///////////////// the second shader:
_program2 = glCreateProgram();
vertShaderPathname2 = [[NSBundle mainBundle] pathForResource:#"Shader2" ofType:#"vsh"];
if (![self compileShader:&vertShader2 type:GL_VERTEX_SHADER file:vertShaderPathname2]) {
NSLog(#"Failed to compile vertex shader2");
return NO;
}
fragShaderPathname2 = [[NSBundle mainBundle] pathForResource:#"Shader2" ofType:#"fsh"];
if (![self compileShader:&fragShader2 type:GL_FRAGMENT_SHADER file:fragShaderPathname2]) {
NSLog(#"Failed to compile fragment shader2");
return NO;
}
glAttachShader(_program2, vertShader2);
glAttachShader(_program2, fragShader2);
glBindAttribLocation(_program2, ATTRIB_VERTEX2, "position2");
if (![self linkProgram:_program2]) {
NSLog(#"Failed to link program: %d", _program2);
if (vertShader2) {
glDeleteShader(vertShader2);
vertShader2 = 0;
}
if (fragShader2) {
glDeleteShader(fragShader2);
fragShader2 = 0;
}
if (_program2) {
glDeleteProgram(_program2);
_program2 = 0;
}
return NO;
}
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX2] = glGetUniformLocation(_program2, "modelViewProjectionMatrix2");
if (vertShader2) {
glDetachShader(_program2, vertShader2);
glDeleteShader(vertShader2);
}
if (fragShader2) {
glDetachShader(_program2, fragShader2);
glDeleteShader(fragShader2);
}
return YES;
}
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
GLint status;
const GLchar *source;
source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String];
if (!source) {
NSLog(#"Failed to load vertex shader");
return NO;
}
*shader = glCreateShader(type);
glShaderSource(*shader, 1, &source, NULL);
glCompileShader(*shader);
#if defined(DEBUG)
GLint logLength;
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(*shader, logLength, &logLength, log);
NSLog(#"Shader compile log:\n%s", log);
free(log);
}
#endif
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
if (status == 0) {
glDeleteShader(*shader);
return NO;
}
return YES;
}
- (BOOL)linkProgram:(GLuint)prog
{
GLint status;
glLinkProgram(prog);
#if defined(DEBUG)
GLint logLength;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(prog, logLength, &logLength, log);
NSLog(#"Program link log:\n%s", log);
free(log);
}
#endif
glGetProgramiv(prog, GL_LINK_STATUS, &status);
if (status == 0) {
return NO;
}
return YES;
}
- (BOOL)validateProgram:(GLuint)prog
{
GLint logLength, status;
glValidateProgram(prog);
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(prog, logLength, &logLength, log);
NSLog(#"Program validate log:\n%s", log);
free(log);
}
glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
if (status == 0) {
return NO;
}
return YES;
}
My vert and fragment shaders are simple:
// vert shader1:
attribute vec4 position;
uniform mat4 modelViewProjectionMatrix;
void main()
{
gl_Position = modelViewProjectionMatrix * position;
}
// vert shader2:
attribute vec4 position2;
uniform mat4 modelViewProjectionMatrix2;
void main()
{
gl_Position = modelViewProjectionMatrix2 * position2;
}
// frag shader(s):
void main()
{
gl_FragColor = vec4(0.12,0.32,0.54,1.0);
}
The most important thing to remember with OpenGL ES, is that you're using a procedural language within an OOP language.
You can only bind one vertex array to the vertex buffer at a time.
Binding two vertex arrays to the VBO, one after the other, and then applying transformations, will only transform the last vertex array attached to the VBO.
In your main loop, you have to iterate through your list of your vertex arrays. For each vertex array, bind it to the VBO, and then carry out any transformations.
The main problem was where I bound the 'position' attribute location for the second vertex shader. I had been using a 'separate' ATTRIB_VERTEX2 in my enum. Once I bound the 'position' attribute location to ATTRIB_VERTEX I was able to see the second VBO with the other shader program applied... Here is the code tidied up for anyone with the same problem/question:
// Uniform index.
enum
{
UNIFORM_MODELVIEWPROJECTION_MATRIX,
UNIFORM_MODELVIEWPROJECTION_MATRIX2,
NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];
// Attribute index.
enum
{
ATTRIB_VERTEX,
NUM_ATTRIBUTES
};
GLfloat square1Data[18] =
{
// Data layout for each line below is:
// positionX, positionY, positionZ
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f
};
GLfloat square2Data[18] =
{
// Data layout for each line below is:
// positionX, positionY, positionZ
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f
};
- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
[self loadShaders];
//---- First Vertex Array Object --------
glGenVertexArraysOES(1, &_vertexArray1);
glGenBuffers(1, &_vertexBuffer1);
glBindVertexArrayOES(_vertexArray1);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer1);
glBufferData(GL_ARRAY_BUFFER, sizeof(square1Data), square1Data, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
// glEnableVertexAttribArray(GLKVertexAttribNormal);
// glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
//----- Second Vertex Array Object ----------
glGenBuffers(1, &_vertexBuffer2);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBufferData(GL_ARRAY_BUFFER, sizeof(square2Data), square2Data, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12, BUFFER_OFFSET(0));
// glEnableVertexAttribArray(GLKVertexAttribNormal);
// glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArrayOES(0);
}
- (void)update
{
_rotation += self.timeSinceLastUpdate * 0.2f;
float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(-1.0f, 1.0f, -1.0f / aspect, 1.0f / aspect, -10.0f, 10.0f);
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.5f, 0.0f, 0.0f);
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, GLKMatrix4MakeZRotation(0.0 - _rotation));
_modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
GLKMatrix4 modelViewMatrix2 = GLKMatrix4MakeTranslation(-0.5f, 0.0f, 0.0f);
modelViewMatrix2 = GLKMatrix4Multiply(modelViewMatrix2, GLKMatrix4MakeZRotation(_rotation));
_modelViewProjectionMatrix2 = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix2);
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArrayOES(_vertexArray1);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer1);
glUseProgram(_program);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
glDrawArrays(GL_TRIANGLES, 0, 6);
///////// second VBO and shader program:
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glUseProgram(_program2);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX2], 1, 0, _modelViewProjectionMatrix2.m);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
- (BOOL)loadShaders
{
GLuint vertShader, fragShader, vertShader2, fragShader2;
NSString *vertShaderPathname, *fragShaderPathname, *vertShaderPathname2, *fragShaderPathname2;
// Create shader program.
_program = glCreateProgram();
_program2 = glCreateProgram();
// Create and compile vertex shader.
vertShaderPathname = [[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"vsh"];
if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) {
NSLog(#"Failed to compile vertex shader");
return NO;
}
// Create and compile fragment shader.
fragShaderPathname = [[NSBundle mainBundle] pathForResource:#"Shader" ofType:#"fsh"];
if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) {
NSLog(#"Failed to compile fragment shader");
return NO;
}
// Create and compile vertex shader.
vertShaderPathname2 = [[NSBundle mainBundle] pathForResource:#"Shader2" ofType:#"vsh"];
if (![self compileShader:&vertShader2 type:GL_VERTEX_SHADER file:vertShaderPathname2]) {
NSLog(#"Failed to compile vertex shader");
return NO;
}
// Create and compile fragment shader.
fragShaderPathname2 = [[NSBundle mainBundle] pathForResource:#"Shader2" ofType:#"fsh"];
if (![self compileShader:&fragShader2 type:GL_FRAGMENT_SHADER file:fragShaderPathname2]) {
NSLog(#"Failed to compile fragment shader");
return NO;
}
// Attach vertex shader to program.
glAttachShader(_program, vertShader);
glAttachShader(_program2, vertShader2);
// Attach fragment shader to program.
glAttachShader(_program, fragShader);
glAttachShader(_program2, fragShader2);
// Bind attribute locations.
// This needs to be done prior to linking.
glBindAttribLocation(_program, ATTRIB_VERTEX, "position");
glBindAttribLocation(_program2, ATTRIB_VERTEX, "position");
// Link program.
if (![self linkProgram:_program]) {
NSLog(#"Failed to link program: %d", _program);
if (vertShader) {
glDeleteShader(vertShader);
vertShader = 0;
}
if (fragShader) {
glDeleteShader(fragShader);
fragShader = 0;
}
if (_program) {
glDeleteProgram(_program);
_program = 0;
}
return NO;
}
if (![self linkProgram:_program2]) {
NSLog(#"Failed to link program: %d", _program2);
if (vertShader2) {
glDeleteShader(vertShader2);
vertShader2 = 0;
}
if (fragShader2) {
glDeleteShader(fragShader2);
fragShader2 = 0;
}
if (_program2) {
glDeleteProgram(_program2);
_program2 = 0;
}
return NO;
}
// Get uniform locations.
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(_program, "modelViewProjectionMatrix");
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX2] = glGetUniformLocation(_program2, "modelViewProjectionMatrix2");
// Release vertex and fragment shaders.
if (vertShader) {
glDetachShader(_program, vertShader);
glDeleteShader(vertShader);
}
if (fragShader) {
glDetachShader(_program, fragShader);
glDeleteShader(fragShader);
}
if (vertShader2) {
glDetachShader(_program2, vertShader2);
glDeleteShader(vertShader2);
}
if (fragShader2) {
glDetachShader(_program2, fragShader2);
glDeleteShader(fragShader2);
}
return YES;
}

Resources