Related
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.
I'm attempting to convert camera frames which are in YUV 4.2.2 (NV12) format to YUV 4.2.0 (YV12/i420) format. The U and V channels of NV12 are interleaved, while the YV12 are not.
For performance reasons, I'm trying to do this conversion in an OpenGl ES 2.0 shader.
The shaders are fairly easy. I separate out the Y and the UV channels and then pass the UV to a shader that separates it out into a U channel in gl_FragData[0] and V channel in gl_FragData[1]
Here is the Fragment Shader:
precision mediump float;
uniform sampler2D t_texture_uv;
varying highp vec2 uvVarying;
void main()
{
/*
vec2 uvu,uvv;
int x,y;
x = uvVarying.x;
y = uvVarying.y;
uvu = vec2(x*2,y);
uvv = vec2((x*2)+1,y);
gl_FragData[0] = texture2D(t_texture_uv, uvu);
gl_FragData[1] = texture2D(t_texture_uv, uvv);
*/
// this is the same as the logic above, but is less processor intensive
gl_FragData[0] = texture2D(t_texture_uv, vec2( uvVarying.x*2 ,uvVarying.y)); // u-plane
gl_FragData[1] = texture2D(t_texture_uv, vec2((uvVarying.x*2)+1,uvVarying.y)); // v-plane
}
And the vertex shader:
attribute vec2 position;
attribute vec2 uv;
uniform mat4 proj_matrix;
varying vec2 uvVarying;
void main()
{
gl_Position = proj_matrix * vec4(vec3(position.xy, 0.0), 1.0);
uvVarying = uv;
}
My problem is that I'm lost at how to retrieve the data from the Gl_FragData buffers back in my Objective C code. I tried glReadPixels, but it doesn't seem to work.
This is my code, but I think I've gone down the wrong path.
- (void) nv12_to_yv12_converter:(unsigned char*)YBuf UVBuff:(unsigned char*)UVBuf Width:(GLsizei)UVWidth Height:(GLsizei)UVHeight {
unsigned char *Ubufout;
unsigned char *Vbufout;
EAGLContext *context;
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context || ![EAGLContext setCurrentContext:context]) {
NSLog(#"Failed to create ES context");
}
[self loadShaders];
glGenFramebuffers(1, &_framebuffer);
glGenRenderbuffers(1, &_renderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
;
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, UVWidth, UVHeight);
// [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.view.layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderbuffer);
_vertices[0] = -1.0f; // x0
_vertices[1] = -1.0f; // y0
_vertices[2] = 1.0f; // ..
_vertices[3] = -1.0f;
_vertices[4] = -1.0f;
_vertices[5] = 1.0f;
_vertices[6] = 1.0f; // x3
_vertices[7] = 1.0f; // y3
int orientation = 0;
static const GLfloat texCoords[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
glViewport(0, 0, _backingWidth, _backingHeight);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(_program);
if (!validateProgram(_program))
{
NSLog(#"Failed to validate program");
return;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glDeleteTextures(1, &_textures[0]);
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &_textures[0]);
glBindTexture(GL_TEXTURE_2D, _textures[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(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);
glEnable(GL_TEXTURE_2D);
glUniform1i(glGetUniformLocation(_program, "t_texture_uv"), 0);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_LUMINANCE,
16,
8,
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
UVBuf);
_uniformMatrix = glGetUniformLocation(_program, "proj_matrix");
GLfloat modelviewProj[16];
mat4f_LoadOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, modelviewProj);
glUniformMatrix4fv(_uniformMatrix, 1, GL_FALSE, modelviewProj);
//draw RECT
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, _vertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
//ATTRIB_TEXTUREPOSITON
glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 0, 0, texCoords);
glEnableVertexAttribArray(ATTRIB_UV);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glReadPixels(0, 0, UVWidth, UVHeight, GL_COLOR_ATTACHMENT0, GL_UNSIGNED_BYTE, &Ubufout);
glReadPixels(0, 0, UVWidth, UVHeight, GL_COLOR_ATTACHMENT0+1, GL_UNSIGNED_BYTE, &Vbufout);
NSLog(#"Complete");
}
Can anyone shed some light on how to retrieve the gl_FragData buffers?
I am trying to pass UIImage to GLSL shader. The fragment shader is:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
void main()
{
highp vec4 color = texture2D(inputImageTexture, textureCoordinate);
highp vec4 color2 = texture2D(inputImageTexture2, textureCoordinate);
gl_FragColor = color * color2;
}
What I want to do is send images from camera and do multiply blend with texture. When I just send data from camera, everything is fine. So problem should be with sending another texture to shader. I am doing it this way:
- (void)setTexture:(UIImage*)image forUniform:(NSString*)uniform {
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), kCGImageAlphaPremultipliedLast);
//draw image into context
CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, pixelSizeOfImage.width, pixelSizeOfImage.height), image.CGImage);
//get uniform of texture
GLuint uniformIndex = glGetUniformLocation(__programPointer, [uniform UTF8String]);
//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);
free(spriteData);
CGContextRelease(spriteContext);
}
Uniform for texture is fine, glGetUniformLocation function do not returns -1. The texture is PNG file of resolution 2000x2000 pixels.
PROBLEM: When the texture is passed to shader, I have got "black screen". Maybe problem are parameters of the CGContext or parameters of the function glTexImage2D
UPDATE: I have just found out I need to pass texture unit number to uniform every draw cycle and everything works just fine. It is really necessary to do it?
Thank you
I have 6 squares made up of 2 trangles, each of which is supposed to have a different texture mapped onto it. Instead, each texture is having the last binded texture on it instead of its own. Heres my drawView and setView:
- (void)drawView:(GLView*)view
{
glBindTexture(GL_TEXTURE_2D, texture[0]);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
static const Vertex3D vertices[] = {
{0,0, 1}, //TL
{ 1024,0, 1}, //TR
{0,-1024, 1}, //BL
{ 1024.0f, -1024.0f, 1} //BR
};
static const GLfloat texCoords[] = {
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0
};
glVertexPointer(3, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
- (void)setupView:(GLView*)view {
// Bind the number of textures we need.
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_GENERATE_MIPMAP,GL_TRUE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glLoadIdentity();
NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:#"jpg"];
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 );
// Flip the Y-axis
CGContextTranslateCTM (context, 0, height);
CGContextScaleCTM (context, 1.0, -1.0);
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context, CGRectMake( 0, 0, width, 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);
}
You're always using texture[0], so you will indeed get the same texture every time. You need to pass the id of the texture you want to glBindTexture ().
I think the problem is related to the texture binding and in particular to this line:
glBindTexture(GL_TEXTURE_2D, texture[0]);
Double check that you use the right value of the gluint required for the texture binding.
Are you using shaders? In case double check it as well though it is most probably not the case.
I suggest to use texture atlas in order to not kill the overall engine's performances by binding every time a different texture in the GPU.
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!