Whenever I attempt to render a textured quad, I end up with a triangular section of the texture distorted:
.
The texture is a PNG created in GIMP, and I've tried two seperate methods of loading the texture (both from Apple's own sample code). Each method of loading the texture produced different results (I don't know if it is different default settings, or if there is a problem with the texture), but I couldn't get either to render proper.
I've tried setting up my indices/verticles/texices in multiple different ways, from suggestions posted in Fastest way to draw quads in OpenGL ES? yet still no luck.
What could I be missing?
Code to Load Texture
- (GLuint)setupTexture:(NSString *)fileName {
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(#"Failed to load image %#", fileName);
exit(1);
}
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,
CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextRelease(spriteContext);
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return texName;
}
Texture Coordinates and Verticles
const GLfloat texices[] =
{ 0,1,
1,1,
0,0,
1,0 };
glActiveTexture(GL_TEXTURE0);
glUniform1i(_texturedTextureUniformSlot, 0);
glVertexAttribPointer(_texturedTextureSlot, 2, GL_FLOAT, GL_FALSE, 0, texices);
GLfloat vertices[] = {-1, -1, 0, //bottom left corner
-1, 1, 0, //top left corner
1, 1, 0, //top right corner
1, -1, 0}; // bottom right rocner
GLubyte indices[] = {0,1,2, // first triangle (bottom left - top left - top right)
0,2,3}; // second triangle (bottom left - top right - bottom right)
glVertexAttribPointer(3, GL_FLOAT, 0, vertices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
It looks like your texture coordinates may be wrong (notice the texture appears to be wrapping around the left side).
Here is a snippet of code that I've used in the past:
const float quadPositions[] = { 1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0 };
const float quadTexcoords[] = { 1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0 };
// stop using VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
// setup buffer offsets
glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), quadPositions);
glVertexAttribPointer(ATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), quadTexcoords);
// ensure the proper arrays are enabled
glEnableVertexAttribArray(ATTRIB_VERTEX);
glEnableVertexAttribArray(ATTRIB_TEXCOORD0);
// draw
glDrawArrays(GL_TRIANGLES, 0, 2*3);
That should draw a two triangles at z=0. You'll want to setup your projection from -1 to 1 in width and height.
Edit:
Here's a working version of your code:
const GLfloat texices[] = { 0, 0,
0, 1,
1, 1,
1, 0 };
const GLfloat vertices[] = { -1, -1, 0, // bottom left corner
-1, 1, 0, // top left corner
1, 1, 0, // top right corner
1, -1, 0}; // bottom right corner
const GLubyte indices[] = { 0, 2, 1, // first triangle (bottom left - top left - top right)
0, 3, 2 };
// ensure no VBOs or IBOs are bound
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), vertices);
glVertexAttribPointer(ATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), texices);
// ensure the proper arrays are enabled
glEnableVertexAttribArray(ATTRIB_VERTEX);
glEnableVertexAttribArray(ATTRIB_TEXCOORD0);
glDisable(GL_CULL_FACE);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
Related
I have been working on getting screenshot from OpenGL and then also rendering the same on screen. For this purpose i have created two framebuffers. 1 For rendering(ratinaFramebuffer) and 1 for using the apple texture cache drawing(textureFramebuffer). Created both using below code(except variable names).
glGenFramebuffers(1, &ratinaFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, ratinaFramebuffer);
glGenRenderbuffers(1, &ratinaColorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, ratinaColorRenderbuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
glGenRenderbuffers(1, &ratinaDepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, ratinaDepthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ratinaDepthRenderbuffer);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
if(status != GL_FRAMEBUFFER_COMPLETE)
{
NSLog(#"failed to make complete framebuffer object %x", status);
}
Here is what my render code looks like
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Clear content
glBindFramebuffer(GL_FRAMEBUFFER, ratinaFramebuffer);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, textureFramebuffer);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Start drawing
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
static int nColor = 104;
glClearColor(0, (++nColor & 0xff)/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:10];
glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
[modelView populateFromTranslation:CC3VectorMake(sin(CACurrentMediaTime()), 0, -7)];
_currentRotation += displayLink.duration * 90;
[modelView rotateBy:CC3VectorMake(_currentRotation, _currentRotation, 0)];
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
// 1
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
// 2
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _floorTexture);
glUniform1i(_textureUniform, 0);
// 3
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer2);
glActiveTexture(GL_TEXTURE0); // unneccc in practice
glBindTexture(GL_TEXTURE_2D, _fishTexture);
glUniform1i(_textureUniform, 0); // unnecc in practice
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glDrawElements(GL_TRIANGLE_STRIP, sizeof(Indices2)/sizeof(Indices2[0]), GL_UNSIGNED_BYTE, 0);
// Get the image
CGImageRef cgImage = NULL;
UIImage *screenImageimage = nil;
OSStatus res = CreateCGImageFromCVPixelBuffer(renderTarget,&cgImage);
if (res == noErr)
{
screenImageimage = [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp];
}
At this point, image is perfectly drawn into the texture. But when i try to render it on screen, the same texture, it is not getting drawn :(. It shows the blue color that i have given in clear color but nothing else.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ratinaFramebuffer);
glBindTexture(GL_TEXTURE_2D, 0);
glClearColor(0, 255/255.0, 255/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, renderTexture);
// Display the buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, ratinaColorRenderbuffer);
[_context presentRenderbuffer:GL_RENDERBUFFER_OES];
When you draw to the separate frame buffer you may attach a texture to it where in the end you may affectively draw to the texture itself. Once this drawing is complete you may then use the texture as you would any other which includes drawing it to your main onscreen buffer.
There are a few points you should check when doing so:
Do not forget to rebind the frame buffer when switching
You must set the viewport according to the bound frame buffer when switching
Possibly you may need to clear the buffer
It is possible the texture is drawn only on a part of the buffer for instance drawing a 600x600 screen on an 1024x1024 texture. In this case you need to generate the vertex coordinates in range [0, 600/1024] to draw the correct part of it. This can be checked by comparing the viewport values from the texture bound frame buffer and the texture size.
It is hard to say what you are missing now but the best shot at the moment would be to check the viewport. Beside that you do not need to complicate with the main buffer if you are simply drawing a fullscreen texture. You may disable the matrix and simply use the [-1,1] coordinate system which is the default.
Problem is resolved, as i was new to openGL and i was not drawing the screen framebuffer, which i did by using below code in the render on screen call. Just posting if anyone need to look at that at beginer level
GLfloat coordinates[] = { 0, 1,
1, 1,
0, 0,
1, 0 };
GLfloat width = self.frame.size.width,
height = self.frame.size.height;
GLfloat vertices[] = { -width / 2 + point.x, -height / 2 + point.y, 0.0,
width / 2 + point.x, -height / 2 + point.y, 0.0,
-width / 2 + point.x, height / 2 + point.y, 0.0,
width / 2 + point.x, height / 2 + point.y, 0.0 };
glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Although, this shows the texture but in a small box that is drawn in on the other frame buffer. It does not draw to complete screen although the vertexs and co-ords are of complete screen. Looking into that, will edit this if i found.
I've been trying for the last two and a half hours to get a simple 2D texture renderer working using opengl es on ios. I've tried following this tutorial to no avail. I've checked my gl errors , checked my triangle ordering, even tried switching back to ES1 to use the fixed function pipeline to absolutely no avail. I'm at a complete loss here, what is going wrong? For completeness' sake, here is my setup and draw code:
- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
GLenum err = 0;
glEnable(GL_TEXTURE_2D);
CHECK_GL_ERR();
glEnable(GL_BLEND);
CHECK_GL_ERR();
glBlendFunc(GL_ONE, GL_SRC_COLOR);
CHECK_GL_ERR();
glGenTextures(1, &texture);
CHECK_GL_ERR();
glBindTexture(GL_TEXTURE_2D, texture);
CHECK_GL_ERR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
CHECK_GL_ERR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
CHECK_GL_ERR();
UIImage *image = [UIImage imageNamed:#"GL_Target"];
if (image == nil)
NSLog(#"Do real error checking here");
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef context = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( context, 0, height - height );
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
CHECK_GL_ERR();
CGContextRelease(context);
free(imageData);
effect = [[GLKBaseEffect alloc] init];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
GLfloat rot = 0.0;
GLenum err = 0;
glColor4f(0.0, 0.0, 0.0, 0.0);
CHECK_GL_ERR();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
CHECK_GL_ERR();
glEnableClientState(GL_VERTEX_ARRAY);
CHECK_GL_ERR();
glEnableClientState(GL_NORMAL_ARRAY);
CHECK_GL_ERR();
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
CHECK_GL_ERR();
static const GLfloat vertices[] = {
-1.0, 1.0, -0.0,
1.0, 1.0, -0.0,
-1.0, -1.0, -0.0,
1.0, -1.0, -0.0
};
static const GLfloat normals[] = {
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0
};
static const GLfloat texCoords[] = {
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0
};
glLoadIdentity();
CHECK_GL_ERR();
glTranslatef(0.0, 0.0, -3.0);
CHECK_GL_ERR();
glRotatef(rot, 1.0, 1.0, 1.0);
CHECK_GL_ERR();
glBindTexture(GL_TEXTURE_2D, texture);
CHECK_GL_ERR();
glVertexPointer(3, GL_FLOAT, 0, vertices);
CHECK_GL_ERR();
glNormalPointer(GL_FLOAT, 0, normals);
CHECK_GL_ERR();
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
CHECK_GL_ERR();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
CHECK_GL_ERR();
glDisableClientState(GL_VERTEX_ARRAY);
CHECK_GL_ERR();
glDisableClientState(GL_NORMAL_ARRAY);
CHECK_GL_ERR();
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
CHECK_GL_ERR();
static NSTimeInterval lastDrawTime;
if (lastDrawTime)
{
NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
rot+= 60 * timeSinceLastDraw;
}
lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
}
EDIT
After scrapping that approach, I am now trying to mirror this project because I was actually able to get that to build, run, and render on my machine. I started by copying the code I thought was important, then copied line by line, then copied entire file contents, then copied entire files, and then created a brand new project, copied over the view controller, sprite, and storyboard files and it still doesn't work. What part of project setup am I missing that is making new projects not work with 100% the exact same code as a project that does work?
From just that code, it seems you are missing binding the shaders.
glUseProgram(program);
Also make sure your shaders compile without error when you run the program.
The author of that blog has also a sample OpenGL ES 2.0 project for XCode in github. Please take a look at it, https://github.com/jlamarche/iOS-OpenGLES-Stuff
I use the following code to render a UIImage using openGL. the Texture2D class is from Apple, so I assume it's correct. The image does not get displayed. I just get the background color produced by glClearColor. My app is based on GLpaint sample code from Apple, so the setup is correct and I am able to draw lines using openGL just fine.
Is this render code below missing something?
- (void) render:(UIImage *)image
{
[EAGLContext setCurrentContext:context];
glViewport(0, 0, backingWidth, backingHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// texturing will need these
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_TEXTURE_2D);
glOrthof(0, backingWidth, 0, backingHeight, -1, 1);
glMatrixMode(GL_MODELVIEW);
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
Texture2D *texture = [[Texture2D alloc] initWithImage:image];
[texture drawInRect: self.frame];
// This application only creates a single color renderbuffer which is already bound at this point.
// This call is redundant, but needed if dealing with multiple renderbuffers.
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
update: replaced the broken link in the original answer with actual code.
Here is the code that worked for me:
-(void) mergeWithImage:(UIImage*) image
{
if(image==nil)
{
return;
}
glPushMatrix();
glColor4f(256,
256,
256,
1.0);
GLuint stampTexture; // = texture.id;
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glGenTextures(1, &stampTexture);
glBindTexture(GL_TEXTURE_2D, stampTexture);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
GLuint imgwidth = CGImageGetWidth(image.CGImage);
GLuint imgheight = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( imgheight * imgwidth * 4 );
CGContextRef context2 = CGBitmapContextCreate( imageData, imgwidth, imgheight, 8, 4 * imgwidth, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGContextTranslateCTM (context2, 0, imgheight);
CGContextScaleCTM (context2, 1.0, -1.0);
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context2, CGRectMake( 0, 0, imgwidth, imgheight ) );
CGContextTranslateCTM( context2, 0, imgheight - imgheight );
CGContextDrawImage( context2, CGRectMake( 0, 0, imgwidth, imgheight ), image.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgwidth, imgheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
CGContextRelease(context2);
free(imageData);
static const GLfloat texCoords[] = {
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0
};
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
/*
These arrays would need to be changed if the size of the paintview changes. You must make sure that all image imput is 64x64, 256x256, 512x512 or 1024x1024. Here we are using 512, but you can use 1024 as follows:
use the numbers:
{
0.0, height, 0.0,
1024, height, 0.0,
0.0, height-1024, 0.0,
1024, height-1024, 0.0
}
*/
NSLog(#"height of me: %f", self.bounds.size.height);
static const GLfloat vertices[] = {
0.0, 643, 0.0,
1024, 643, 0.0,
0.0, -381, 0.0,
1024, -381, 0.0
};
static const GLfloat normals[] = {
0.0, 0.0, 1024,
0.0, 0.0, 1024,
0.0, 0.0, 1024,
0.0, 0.0, 1024
};
glBindTexture(GL_TEXTURE_2D, stampTexture);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glNormalPointer(GL_FLOAT, 0, normals);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glPopMatrix();
glDeleteTextures( 1, &stampTexture );
// Display the buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
-(void) addImageToView{
UIImage* imageToAdd= [UIImage imageNamed:#"IMAGE_TO_ADD_TO_GL_VIEW.png"];
// all images added to the paining view MUST be 512x512.
// You can also add smaller images (even transformed ones) using this method - just add it to a UIView and then get it's graphics context
UIView* imageView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 512, 512)];
UIImageView* subView = [[UIImageView alloc] initWithImage:imageToAdd];
[imageView addSubview:subView];
UIImage* blendedImage =nil;
UIGraphicsBeginImageContext(imageView.frame.size);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
blendedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self mergeWithImage: blendedImage ];
I'm working on a simple little game for the iPhone, and I'd like to use textures, however I can't quite seem to get it working...
After some research I found this page and this site. Both are great references, and taught me a little bit about textures, however, after loading a texture using either function I can't get the texture displayed, here's what my code looks like:
Very Simple Texture Display Function (not working)
void drawTexture(GLuint texture, float x, float y, float w, float h)
{
glBindTexture(GL_TEXTURE_2D,texture);
GLfloat box[] = {x,y+h, x+w,y+h, x,y, x+w,y};
GLfloat tex[] = {0,0, 1,0, 1,1, 0,1};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0,box);
glTexCoordPointer(2, GL_FLOAT, 0, tex);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
Normally, I'd not create an array every single frame only to display an image, but this is just an example. When I run this function, I get nothing. Blank- no image, nothing (unless of course I'd previously enabled a color array and hadn't disabled it afterwards)
Second Simple Display Function (this one uses a quick little class)
void draw_rect(RectObject* robj){
glVertexPointer(2, GL_FLOAT, 0, [robj vertices]);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, [robj colors]);
glEnableClientState(GL_COLOR_ARRAY);
if ([robj texture] != -1){
glEnable(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glClientActiveTexture([robj texture]);
glTexCoordPointer(2, GL_FLOAT, 0, defaultTexCoord);
glBindTexture(GL_TEXTURE_2D, [robj texture]);
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_COORD_ARRAY);
}
This function on the other hand, does change the display, instead of outputting the texture however it outputs a black square...
Setup Background
In my init function I'm calling
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
Two LONG Texture Loading Functions
struct Texture2D LoadImage(NSString* path)
{
struct Texture2D tex;
tex.texture = -1;
// Id for texture
GLuint texture;
// Generate textures
glGenTextures(1, &texture);
// Bind it
glBindTexture(GL_TEXTURE_2D, texture);
// Set a few parameters to handle drawing the image
// at lower and higher sizes than original
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_GENERATE_MIPMAP, GL_TRUE);
//NSString *path = [[NSString alloc] initWithUTF8String:imagefile.c_str()];
path = [[NSBundle mainBundle] pathForResource:path ofType:#""];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
if (image == nil)
return tex;
// Get Image size
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Allocate memory for image
void *imageData = malloc( height * width * 4 );
CGContextRef imgcontext = CGBitmapContextCreate(
imageData, width, height, 8, 4 * width, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( imgcontext,
CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( imgcontext, 0, height - height );
CGContextDrawImage( imgcontext,
CGRectMake( 0, 0, width, height ), image.CGImage );
// Generate texture in opengl
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
// Release context
CGContextRelease(imgcontext);
// Free Stuff
free(imageData);
[image release];
[texData release];
// Create and return texture
tex.texture=texture;
tex.width=width;
tex.height=height;
return tex;
}
GLuint makeTexture(NSString* path){
GLuint texture[1]={-1};
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
path = [[NSBundle mainBundle] pathForResource:path ofType:#"png"];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
if (image == nil)
NSLog(#"Do real error checking here");
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef context = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( context, 0, height - height );
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
CGContextRelease(context);
free(imageData);
[image release];
[texData release];
return texture[0];
}
If you could point me in the right direction it would be much appreciated.
First of all, your draw_rect function has an error. Don't call glClientActiveTexture, it is used for multi-texturing and you don't need it. Calling it with a texture object will either bind some really strange texture unit or, most likely, result in an error.
And in the drawTexture function you are actually drawing the triangles in clockwise order. Assuming you didn't flip the y-direction in the projection matrix or something similar, if you have back-face culling enabled your whole geometry will get culled away. Try calling glDisable(GL_CULL_FACE), although back-face culling should be disabled by default. Or even better, change your vertices to counter-clockwise ordering:
box[] = { x,y+h, x,y, x+w,y+h, x+w,y };
You also have a mismatch of texture coordinates to vertices in your drawTexture function, but this shouldn't cause the texture not to be drawn, but rather just look a bit strange. Considering the changes to counter-clockwise ordering from the last paragraph, the texture coordinates should be:
tex[] = { 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f, 0.0f };
EDIT: Your draw_rect function is also messing up the state, because you enable the vertex and color arrays, but then don't disable them again when you are finished with rendering. When you now want to draw something different without a color array (like in drawTexture), the color array is still enabled and uses some arbitrary data. So you should add
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
right after
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
in draw_rect.
EDIT: And you should also wrap the drawTexture function in a pair of glEnable(GL_TEXTURE_2D) and glDisable(GL_TEXTURE_2D). You enable texturing in the initialization code, which is wrong. You should set all neccessary state right before rendering, especially such highly object-dependent state like texturing. For example once you call draw_rect before drawTexture, you end up with disabled texturing, although you enabled it in the initialization code and thought it to be always enabled. Do you see that this is not a good idea?
EDIT: I just spotted another error. In draw_rect you call glEnable and glDisable with GL_TEXTURE_COORD_ARRAY, which is wrong. You have to use glEnableClientState and glDisableClientState for enabling/disabling vertex arrays, like you did int drawTexture.
So as a little mid-way conclusion your functions should actually look like:
void drawTexture(GLuint texture, float x, float y, float w, float h)
{
glBindTexture(GL_TEXTURE_2D,texture);
glEnable(GL_TEXTURE_2D);
GLfloat box[] = {x,y+h, x+w,y+h, x,y, x+w,y};
GLfloat tex[] = {0,0, 1,0, 1,1, 0,1};
glTexCoordPointer(2, GL_FLOAT, 0, tex);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, box);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
void draw_rect(RectObject* robj)
{
if ([robj texture] != -1)
{
glBindTexture(GL_TEXTURE_2D, [robj texture]);
glEnable(GL_TEXTURE_2D);
glTexCoordPointer(2, GL_FLOAT, 0, defaultTexCoord);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
glColorPointer(4, GL_UNSIGNED_BYTE, 0, [robj colors]);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, [robj vertices]);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
if ([robj texture] != -1)
{
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
}
If one of the textures work and the other not, could it be a problem with the texture file ?
Dimensions sometimes can trick you, try to use the same file (the one working) on both textures and see if that solved. If it does it's a problem with the texture file.
The fun does work.
void drawTexture(GLuint texture, float x, float y, float w, float h)
{
glBindTexture(GL_TEXTURE_2D,texture);
glEnable(GL_TEXTURE_2D);
GLfloat box[] = {x,y+h, x+w,y+h, x,y, x+w,y};
GLfloat tex[] = {0,0, 1,0, 1,1, 0,1};
glTexCoordPointer(2, GL_FLOAT, 0, tex);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, box);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
but we should relate to the right coordinate for texture. wo should change the code
form
GLfloat box[] = {x,y+h, x+w,y+h, x,y, x+w,y};
GLfloat tex[] = {0,0, 1,0, 1,1, 0,1};
to
GLfloat box[] = {x,y+h, x+w,y+h, x,y, x+w,y};
GLfloat tex[] = { 0.0f,1.0f, 1.0f,1.0f, 0.0f,0.0f, 1.0f, 0.0f };
Thanks stackoverflow. Thanks your help.
Good luck!
I'm going to write a texture atlas manager, but I've run into a problem. When I modified my original UV coordinates, my program no longer rendered anything?? This is my draw code:
- (void)drawFrame {
[(EAGLView *)self.view setFramebuffer];
//GLfloat aspectRatio = self.view.bounds.size.height/self.view.bounds.size.width;
float m[16] = {2/self.view.bounds.size.width, 0, 0, 0, 0, 2/self.view.bounds.size.height, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 };
CGSize imageSize = CGSizeMake(512, 512);
CGRect drawBounds = CGRectMake(0, 0, 106, 126);
GLfloat x = (1/imageSize.width) * drawBounds.size.width;
GLfloat y = (1/imageSize.height) * drawBounds.size.height;
static const GLfloat vertices[] = {
-53.0f, -63.0f,
53.0f, -63.0f,
-53.0f, 63.0f,
53.0f, 63.0f,
};
GLfloat texCoords[] = {
0.0, 0.0,
x, 0.0,
0.0, y,
x, y
};
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
[[TextureLibrary sharedTextureLibrary] bindTexture:#"bounce" toSlot:GL_TEXTURE0];
glUniform1f(uniforms[UNIFORM_TEXTURE], 0);
glUniformMatrix4fv(UNIFORM_MVP_MATRIX, 1, GL_FALSE, m);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_TEXTURE_POSITION, 2, GL_FLOAT, 0, 0, texCoords);
glEnableVertexAttribArray(ATTRIB_TEXTURE_POSITION);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
[(EAGLView *)self.view presentFramebuffer];
}
However, it works fine like this:
GLfloat texCoords[] = {
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0
};
Help?
Just a guess, but OpenGL considers (0, 0) to be the lower left corner of an image. Is it possible your graphic is in the top left, so that the area you're displaying is actually from a transparent part of the texture?
If so then you can either adjust your texture coordinates or adjust the texture matrix stack so that (0, 0) is in the top left.