failed to bind EAGLDrawable in CADisplayLink render loop - ios

i use CADisplayLink render-loop callback to render a serial of image textures with openGLes.
after CADisplayLink's first callback called, i just get these error output
Failed to bind EAGLDrawable: <CAEAGLLayer: 0x946bb40> to GL_RENDERBUFFER 2
Failed to make complete framebuffer object 8cd6
i setup the renderBuffer&frameBuffer and call glFramebufferRenderbuffer in controller's viewDidLoad stage, the return of glCheckFramebufferStatus is fine in that stage.
this is the code I'm using.
//GLKViewController.m
typedef struct {
GLKVector3 positionCoords;
GLKVector2 textureCoords;
}SceneVertex;
static const SceneVertex vertices[] =
{
{{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}}, // lower left corner
{{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}}, // lower right corner
{{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}}, // upper left corner
{{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
};
#interface myViewController () //glkViewController
#property (nonatomic) GLuint renderBuffer;
#property (nonatomic) GLuint frameBuffer;
#property (nonatomic) GLuint glBuffer;
#property (nonatomic) int renderWidth;
#property (nonatomic) int renderHeight;
#property(strong, nonatomic) CADisplayLink* displayLink;
#property(strong, nonatomic) EAGLContext* context;
#end
#implementation myViewController
-(void)setupBuffers
{
glGenFramebuffers(1, &_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
glGenRenderbuffers(1, &_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.view.layer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _renderBuffer);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_renderWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_renderHeight);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(#"AAfailed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
glGenBuffers(1,&_glBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _glBuffer);
glBufferData(
GL_ARRAY_BUFFER, // Initialize buffer contents
sizeof(vertices), // Number of bytes to copy
vertices, // Address of bytes to copy
GL_STATIC_DRAW); // Hint: cache in GPU memory
}
-(void)loadView
{
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
self.view = [[myView alloc] initWithFrame:[[UIScreen mainScreen] bounds] context:_context];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[EAGLContext setCurrentContext:self.context];
[self setupBuffers];
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(render)];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
_displayLink.frameInterval = 1;
}
- (void)render
{
myView *view = (myView*)self.view;
NSData *image = [view getOneImage]; //if nil, return or sleep&reget;
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, _renderWidth, _renderHeight);
GLuint texture = -1;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, texture);
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 );
glTexImage2D(
GL_TEXTURE_2D, 0, /* target, level */
GL_RGB, /* internal format */
_renderWidth, _renderHeight, 0, /* width, height, border */
GL_RGB, GL_UNSIGNED_BYTE, /* external format, type */
image.bytes /* pixels */
);
glBindBuffer(GL_ARRAY_BUFFER,_glBuffer);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, // Identifies the attribute to use
3, // number of coordinates for attribute
GL_FLOAT, // data is floating point
GL_FALSE, // no fixed point scaling
sizeof(SceneVertex), // total num bytes stored
NULL+offsetof(SceneVertex, positionCoords));
glBindBuffer(GL_ARRAY_BUFFER, _glBuffer);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, // Identifies the attribute to use
2, // number of coordinates for attribute
GL_FLOAT, // data is floating point
GL_FALSE, // no fixed point scaling
sizeof(SceneVertex), // total num bytes stored per vertex
NULL+offsetof(SceneVertex, textureCoords));
glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawArrays(GL_TRIANGLES, 1, 4);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
[_context presentRenderbuffer:GL_RENDERBUFFER];
glFlush();
glDeleteTextures(1, &texture);
}
#end
//GLKView
#implementation myView
+ (Class)layerClass {
return [CAEAGLLayer class];
}
- (id)initWithFrame:(CGRect)frame context:(EAGLContext *)context
{
self = [super initWithFrame:frame];
if (self) {
self.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
self.context = context;
CAEAGLLayer *layer= (CAEAGLLayer *)self.layer;
self.images = [[NSMutableArray alloc] init];
layer.opaque = YES;
layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
}
return self;
}
#end
//APPDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[QCPViewController alloc]init];
[self.window makeKeyAndVisible];
return YES;
}

This occurs when you try to draw an openGL view that isn't currently on screen. In my case, the view wasn't installed in the current size class in my storyboard.
If you are using storyboards, double check that the view is installed in the current size class
Select the open GL view in the storyboard
Go to the Attributes inspector
Verify the view is installed in all size classes like below

I'm experiencing the same thing. I'm creating and adding the SCNView on a callback from CADisplayLink. It is very laggy, it is not visible and the error shows up in console. When I just say dispatch_async(dispatch_get_main_queue(), ^{ /* code */ }); Then everything is fine.

Related

Get empty buffer use glReadPixels with GLKit

In an OpenGL ES app I'm working on, when i use glReadPixels to get pixel but got empty buffer.now i don't know what's wrong in my code。Thanks for any help.
- (void)setTextureImage:(UIImage *)image {
self.textureID = [self createTextureWithImage:image];
CAEAGLLayer *layer = [[CAEAGLLayer alloc] init];
layer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
layer.contentsScale = [[UIScreen mainScreen] scale];
layer.opaque = NO;
[self.layer addSublayer:layer];
[self bindRenderLayer:layer];
}
- (void)bindRenderLayer:(CALayer <EAGLDrawable> *)layer {
glGenRenderbuffers(1, &renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
renderBuffer);
}
- (GLint)drawableWidth {
GLint backingWidth;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
return backingWidth;
}
- (GLint)drawableHeight {
GLint backingHeight;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
return backingHeight;
}
above sample code just part of display texture and it's works fine. The renderBuffer and framebuffer is property of my class.
sample get pixel code here, buffer is empty after use glReadPixels? Is anything I missed to setup?
glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
// I'm try to use one or both of the bind method but not worked
//glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
NSInteger dataLength = self.drawableWidth * self.drawableHeight * 4;
GLubyte *buffer = (GLubyte *)malloc(dataLength * sizeof(GLubyte));
glReadPixels(0,
0,
self.drawableWidth,
self.drawableHeight,
GL_RGBA,
GL_UNSIGNED_BYTE,
buffer);
I found a solution. When you set CAEAGLLayer's drawableProperties like this:
layer.drawableProperties = #{
kEAGLDrawablePropertyRetainedBacking: #(YES),
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
};
kEAGLDrawablePropertyRetainedBacking = YES makes it so you can get the buffer when finshed rendering.

UIImage passed to method on another thread crashes CGContextDraw with EXC_BAD_ACCESS

I am currently working on an app which requires me to do some image processing. So I have the user take an image and all steps of the processing are shown. Thus I create a grey-scale version of the image, pass it to the processing class (by setting of its property) and then call its processing method on another thread (by using dispatch_async()). The problem is when I try to draw the passed image into the context to retrieve the data in order to use it with OpenGL, the app crashes with EXC_BAD_ACCESS code=1. Below is the code in question:
ProcessorViewController.m
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Create Grayscale
UIImage *grayScaleImage = [self createGrayScaleOfImage:theImage];
processor = [[GLKProcessor alloc] init];
[processor setOriginalImage:grayScaleImage];
[self.view addSubview:processor.Step1View];
// Run OpenGL ES processing on seperate dispatch_queue
dispatch_queue_t processing = dispatch_queue_create("com.CannyEdgeDetectoriOS.processing", nil);
dispatch_async(processing, ^{
[processor beginImageProcessingWithGrayScaleImage];
});
}
GLKProcessor.h
#interface GLKProcessor : NSObject <GLKViewDelegate> {
GLKView *Step1View;
GLKView *Step2View;
GLKView *Step3View;
#private
//__strong UIImage *originalImage;
CIContext *ciContext;
}
#property (strong) GLKView *Step1View;
#property (strong) GLKView *Step2View;
#property (strong) GLKView *Step3View;
#property (strong) EAGLContext *currentEAGLContext;
#property (strong) UIImage *originalImage;
- (void)beginImageProcessingWithGrayScaleImage;
#end
GLKProcessor.m
#pragma mark - Start Call
- (void)beginImageProcessingWithGrayScaleImage {
// Draw originalImage(grayScale) in GLKView and blur
[self glkView:Step1View drawInRect:Step1View.frame];
[Step1View display];
}
#pragma mark - GLKViewDelegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// Check which glkView
switch ([view tag]) {
case 0: {
// Step1 GLKView
// Draw originalImage(grayScale) in GLKView
ciContext = [CIContext contextWithEAGLContext:self.currentEAGLContext];
// Check EAGLContext
if (self.currentEAGLContext != [EAGLContext currentContext]) {
[EAGLContext setCurrentContext:self.currentEAGLContext];
}
[Step1View bindDrawable];
// Clear EAGLContext to white
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/* * * * * * * Draw grayscale image * * * * * * */
CGImageRef cgOriginalImage = [originalImage CGImage];
// Declare variables
GLuint texture;
size_t width = CGImageGetWidth(cgOriginalImage);
size_t height = CGImageGetHeight(cgOriginalImage);
size_t bytesPerPixel = CGImageGetBitsPerPixel(cgOriginalImage);
size_t bytesPerRow = bytesPerPixel * width;
size_t bitsPerComponenet = CGImageGetBitsPerComponent(cgOriginalImage);
GLubyte *glBytes = (GLubyte *)malloc(bytesPerRow * height);
// Draw image to context
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
CGContextRef context = CGBitmapContextCreate(&glBytes, width, height, bitsPerComponenet, bytesPerRow, colorSpace, kCGImageAlphaNone);
CGColorSpaceRelease(colorSpace); // Release color space to clear unneeded memory allocation
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgOriginalImage);
CGContextRelease(context); // Release context to clear uneeded memory allocation
// OpenGL ES workings
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, glBytes);
glBindTexture(GL_TEXTURE_2D, 0);
break;
}
case 1:
// Step2 GLKView
break;
case 2:
// Step3 GLKView
break;
default:
// Error
break;
}
}
I have tried to analyse the issue myself, by setting a breakpoint in the ProcessorViewController and then 'stepping' through the code. What I have noticed is that the variables (size_t width, size_t height, etc...) I set are all expected values until the CGImageRef is reached in the CGContextDrawImage() method. Once that occurs the values skyrocket and all properties of the NSObject are set to nil.
I am completely new to threads, and don't really understand what is going on. Might it have something to do with the fact that the grey-scale image was allocated and initialised on another thread? If so how could I get around this issue, I really want to have the processing done on another thread so that the UI is still responsive.
Thank you so much for any insights/suggestions/help, I really appreciate it!

How can I render an RGB bitmap into a GLKView using 2D textures?

I have an in-memory "bitmap" which is just a malloced Byte * array that contains pixel data in a simple RGB format (so the size of the byte array is 3 * the number of pixels).
My app is just a view controller with an instance of GLKView. I have implemented its delegate like so:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
and as expected this sets the background color of the GLKView.
What I would now like to do is add code to this implementation of glkView:drawInRect: so that my "bitmap" is rendered into this GLKView. But I can't seem to find any way of doing that simply; I'm kind of overwhelmed by all the different things OpenGL can do, all of which are much more complex than what I'm trying to do here.
glReadPixels seems to sort of be what I'm after here as it seems to provide a pointer to the buffer data.
Edit: apparently this can only be accomplished via the use of textures. I have attempted to implement this with this sample code (note that my "bitmap" here is 4 bytes per sample, matching the format parameters):
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// raw data
int width = 30;
int height = 30;
int pixelCount = width * height;
int byteSize = pixelCount * 4;
GLubyte *textureData = (GLubyte *)malloc(byteSize);
for (int i = 0; i < byteSize; i++) {
textureData[i] = 255; // white
}
glEnable(GL_TEXTURE_2D);
GLuint textureID;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, textureData);
free(textureData);
}
... but it is not working. The glClear() call works as expected and sets the entire background red; but if I understand the texture samples correctly, the code below that should be drawing a 30x30 white square in the corner, but all I'm getting is the solid red background.
Can anybody spot what I'm doing wrong here?
Sorry about pushing into OpenGL :)
I would create CGImage and then UIImage
void *baseAddress = & textureData;
size_t bytesPerRow = width * 4;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGImageRef cgImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
UIImage *image = [UIImage imageWithCGImage:cgImage];
and then draw it with drawInRect:
Edit:
Here is code I've written. This is not production code: it lacks checking for openGL errors, some minor leaks and have global variables but it's good place to enhance it.
#import "ViewController.h"
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#interface ViewController ()
#end
#implementation ViewController
GLuint tex;
float vertices[] = {
// Position Texcoords
-1.0f, 1.0f, 0.0f, 0.0f, // Top-left
1.0f, 1.0f, 1.0f, 0.0f, // Top-right
1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right
-1.0, -1.0f, 0.0f, 1.0f // Bottom-left
};
const char * vertexShader = "attribute vec2 position;\n"
"attribute vec2 TexCoordIn;\n"
"varying vec2 TexCoordOut;\n"
"void main() {\n"
"gl_Position = vec4(position, 0.0, 1.0);\n"
"TexCoordOut = TexCoordIn;\n"
"}\n";
const char * fragmentShader = "precision mediump float;\n"
"varying lowp vec2 TexCoordOut;\n"
"uniform sampler2D Texture;\n"
"void main() {\n"
"gl_FragColor = texture2D(Texture, TexCoordOut);\n"
"}\n";
GLuint shaderProgram;
GLuint vao;
GLuint vbo;
-(void) initOpenGLObjects {
glGenVertexArraysOES(1, &vao);
glBindVertexArrayOES(vao);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertexShader, NULL);
glCompileShader(vs);
GLint status;
glGetShaderiv(vs, GL_COMPILE_STATUS, &status);
char buffer[512];
glGetShaderInfoLog(vs, 512, NULL, buffer);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragmentShader, NULL);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &status);
glGetShaderInfoLog(fs, 512, NULL, buffer);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vs);
glAttachShader(shaderProgram, fs);
glLinkProgram(shaderProgram);
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
GLint texAttrib = glGetAttribLocation(shaderProgram, "TexCoordIn");
glGenBuffers(1, &vbo); // Generate 1 buffer
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)(2*sizeof(float)));
glEnableVertexAttribArray(posAttrib);
glEnableVertexAttribArray(texAttrib);
}
-(void) viewDidLoad{
[super viewDidLoad];
self.glkView.context = [[EAGLContext alloc] initWithAPI:
kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:self.glkView.context];
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(update)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
self.glkView.delegate = self;
[self initOpenGLObjects];
}
- (void)update
{
[self.glkView setNeedsDisplay];
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
int width = 300;
int height = 300;
int pixelCount = width * height;
int byteSize = pixelCount * 4;
GLubyte *textureData = (GLubyte *)malloc(byteSize);
static int time = 0;
time = (time+1)%256;
for (int i = 0; i < byteSize; i+=4) {
textureData[i] = 255;
textureData[i+1] = time;
textureData[i+2] = 255;
textureData[i+3] = 255;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, textureData);
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
free(textureData);
}
#end
I don't know how do you want to update data, maybe you don't have to malloc/free it every time and just do small changes and update it with glTexSubImage2D. Most of the time is spend on filling data.
This code worked on my MacBook with Xcode 7 in simulators and iPhone 6 with iOS 9.0.2.

OpenGL View shows only empty(pink) screen

I'm going to show pixel data on opengl view.
But I can't see anything, only can see the empty pink gl screen.
Please check my codes and let me know what it is wrong.
#implementation GLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Do OpenGL Core Animation layer setup
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context || ![EAGLContext setCurrentContext:context]
|| ![self createFramebuffers])
return nil;
}
return self;
}
- (BOOL)createFramebuffers
{
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
// Onscreen framebuffer object
glGenFramebuffers(1, &viewFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
glGenRenderbuffers(1, &viewRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
NSLog(#"Backing width: %d, height: %d", backingWidth, backingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
NSLog(#"Failure with framebuffer generation");
return NO;
}
return YES;
}
- (void)setDisplayFramebuffer;
{
if (context)
{
// [EAGLContext setCurrentContext:context];
if (!viewFramebuffer)
[self createFramebuffers];
glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
// glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
glViewport(0, 0, backingWidth, backingHeight);
}
}
- (BOOL)presentFramebuffer;
{
BOOL success = FALSE;
if (context)
{
// [EAGLContext setCurrentContext:context];
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
success = [context presentRenderbuffer:GL_RENDERBUFFER];
}
return success;
}
and in viewcontroller
m_glView = [[GLView alloc] initWithFrame:CGRectMake(0, 0, m_viewPlayer.frame.size.width, m_viewPlayer.frame.size.height)];
[m_viewPlayer addSubview:m_glView];
[self loadVertexShader:#"DirectDisplayShader" fragmentShader:#"DirectDisplayShader" forProgram:&m_directDisplayProgram];
and in timer loop
CMSampleBufferRef buf = [m_trackOutput copyNextSampleBuffer];
if (buf == nil)
return;
[self processFrame:buf]; // draw frame to opengl view
CFRelease(buf);
- (void)processFrame:(CMSampleBufferRef)sampleBuffer
{
if (m_videoFrameTexture)
glDeleteTextures(1, &m_videoFrameTexture);
// Get a CMSampleBuffer's Core Video image buffer for the media data
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer, 0);
CGFloat width = CVPixelBufferGetWidth(imageBuffer);
CGFloat height = CVPixelBufferGetHeight(imageBuffer);
// Create a new texture from the camera frame data, display that using the shaders
glGenTextures(1, &m_videoFrameTexture);
glBindTexture(GL_TEXTURE_2D, m_videoFrameTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// This is necessary for non-power-of-two textures
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Using BGRA extension to pull in video frame data directly
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, CVPixelBufferGetBaseAddress(imageBuffer));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, CVPixelBufferGetBaseAddress(imageBuffer));
[self drawFrame];
// glDeleteTextures(1, &videoFrameTexture);
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
}
- (void)drawFrame
{
[m_glView setDisplayFramebuffer];
[self drawCapturedScreen];
[m_glView presentFramebuffer];
}
- (void)drawCapturedScreen
{
glUseProgram(m_directDisplayProgram);
glBindTexture(GL_TEXTURE_2D, m_videoFrameTexture);
glUniform1i(uniforms[UNIFORM_VIDEOFRAME], 0);
// Update attribute values.
static const GLfloat squareVertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
float m_fScale = 1.0f; //
GLfloat kRate = 1.0f/m_fScale;
GLfloat kX = (1.0-kRate)/2;
GLfloat kY = (1.0-kRate)/2;
GLfloat kS = kX+kRate;
GLfloat kT = kY+kRate;
{
{
GLfloat textureVertices[] = {
kS, kT,
kS, kY,
kX, kT,
kX, kY,
};
glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
}
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
Have you managed to render anything in OpenGL so far? I would suggest breaking what you're doing down into smaller steps and get those working first. First try rendering just using GL_POINTS and see if you can get anything and then build up from there. Make your shaders as simple as possible too to check for issues there. You should try to get the bare minimum rendering working and then build up the complexity (for example, after points try lines, then triangles, then textured triangles). By breaking down the rendering you can isolate what's causing the draw call to not show anything.

Finding Exact Screen Dimensions

Totally new to iOS OpenGL ES 2.0. I'm trying to precisely identify the boundaries of my screen (iPad) by drawing a simple rectangle. From what I have found, it looks to me like the dimensions of an iPad screen are 768 x 1024. However, my screen is not being covered correctly (note that I am drawing in landscape mode if that matters).
I'm not sure of the interaction between the vertices and how I'm using the projection matrix command.
'self.baseEffect.transform.projectionMatrix = GLKMatrix4MakeOrtho(0, FRAME_WIDTH*2, 0, FRAME_HEIGHT*2, 0, 0);'
If I delete this line, my rectangle renders with the lower left at the origin. But if I leave it in, it seems to render from the lower left, but the dimensions are too large and I can't seem to figure out how to change them predictably.
As you can see, I'm very confused. What is the best way of getting the exact screen dimensions. I need this to properly place other objects on the screen. Thanks!
#import "ViewController.h"
typedef struct {
GLKVector3 positionCoordinates;
GLKVector2 textureCoordinates;
} VertexData;
#define FRAME_HEIGHT 768.0f
#define FRAME_WIDTH 1024.0f
VertexData vertices[] = {
{ { 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f} }, // bottom left
{ {FRAME_WIDTH, 0.0f, 0.0f}, {1.0f, 0.0f} }, // bottom right
{ { 0.0f, FRAME_HEIGHT, 0.0f}, {0.0f, 1.0f} }, // top left
{ { 0.0f, FRAME_HEIGHT, 0.0f}, {0.0f, 1.0f} }, // top left
{ {FRAME_WIDTH, 0.0f, 0.0f}, {1.0f, 0.0f} }, // bottom right
{ {FRAME_WIDTH, FRAME_HEIGHT, 0.0f}, {1.0f, 1.0f} } // top right
};
#interface ViewController ()
#property (nonatomic, strong) EAGLContext *context;
#property (nonatomic, strong) GLKBaseEffect *baseEffect;
#end
#implementation ViewController {
GLuint _vertexBufferID;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
GLKView *view = (GLKView *) self.view;
view.context = self.context;
[EAGLContext setCurrentContext:self.context];
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = YES;
self.baseEffect.constantColor = GLKVector4Make(1.0f, 0.0f, 0.0f, 1.0f);
self.baseEffect.transform.projectionMatrix = GLKMatrix4MakeOrtho(0, FRAME_WIDTH*2, 0, FRAME_HEIGHT*2, 0, 0);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glGenBuffers(1, &_vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), offsetof(VertexData, positionCoordinates));
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - GKLView delegate methods
- (void) glkView: (GLKView *) view drawInRect:(CGRect)rect{
glClear(GL_COLOR_BUFFER_BIT);
[self.baseEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
- (void) update {
}
#end
You should use FRAME_HEIGHT and FRAME_WIDTH, not twice them.
You can obtain the screen dimensions with:
CGSize screenSize = [UIScreen mainScreen].bounds.size;
however, be wary of using this during application start-up.
To get the orientation of the current view controller:
[UIViewController interfaceOrientation]
To obtain the device orientation:
[[UIDevice currentDevice] orientation]
... or ...
[[UIApplication sharedApplication] statusBarOrientation];
In your case, you can simply use the bounds of EAGLView that you have:
CGSize viewSize = self.view.bounds.size

Resources