#ios #opengl-es #textures
#iOS #opengl-es #Текстуры
Вопрос:
Я работаю над простой маленькой игрой для iPhone, и я хотел бы использовать текстуры, однако, похоже, у меня не получается заставить ее работать…
После некоторых исследований я нашел эту страницу и этот сайт. Оба являются отличными ссылками и немного научили меня разбираться в текстурах, однако после загрузки текстуры с помощью любой функции я не могу отобразить текстуру, вот как выглядит мой код:
Очень простая функция отображения текстур (не работает)
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);
}
Обычно я бы не создавал массив для каждого отдельного кадра только для отображения изображения, но это всего лишь пример. Когда я запускаю эту функцию, я ничего не получаю. Пусто — нет изображения, ничего (если, конечно, я ранее не включил цветовой массив и не отключил его впоследствии)
Вторая простая функция отображения (в этой используется небольшой урок)
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);
}
С другой стороны, эта функция изменяет отображение, однако вместо вывода текстуры она выводит черный квадрат…
Фон настройки
В моей функции инициализации я вызываю
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
Две функции длительной загрузки текстур
struct Texture2D LoadImage(NSString* path)
{
struct Texture2D tex;
tex.texture = -1;
// Id for texture
GLuint texture;
// Generate textures
glGenTextures(1, amp;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];
}
Если бы вы могли указать мне правильное направление, я был бы очень признателен.
Ответ №1:
Прежде всего, в вашей draw_rect
функции ошибка. Не вызывайте glClientActiveTexture
, он используется для мультитекстурирования и вам это не нужно. Вызов его с помощью текстурного объекта либо привяжет какой-то действительно странный текстурный объект, либо, скорее всего, приведет к ошибке.
И в drawTexture
функции вы фактически рисуете треугольники по часовой стрелке. Предполагая, что вы не изменили направление y в матрице проекции или что-то подобное, если у вас включена отбраковка обратной стороны, вся ваша геометрия будет отбракована. Попробуйте вызвать glDisable(GL_CULL_FACE)
, хотя отбраковка обратной стороны должна быть отключена по умолчанию. Или, что еще лучше, измените порядок вершин на порядок против часовой стрелки:
box[] = { x,y h, x,y, x w,y h, x w,y };
У вас также есть несоответствие координат текстуры вершинам в вашей drawTexture
функции, но это не должно приводить к тому, что текстура не будет отрисовываться, а скорее просто будет выглядеть немного странно. Учитывая изменения в порядке против часовой стрелки из последнего абзаца, координаты текстуры должны быть:
tex[] = { 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f, 0.0f };
РЕДАКТИРОВАТЬ: Ваша draw_rect
функция также изменяет состояние, потому что вы включаете массивы вершин и цветов, но затем не отключаете их снова, когда заканчиваете рендеринг. Когда вы теперь хотите нарисовать что-то другое без цветового массива (как в drawTexture
), цветовой массив по-прежнему включен и использует некоторые произвольные данные. Итак, вы должны добавить
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
сразу после
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
в draw_rect
.
РЕДАКТИРОВАТЬ: И вам также следует обернуть функцию drawTexture в пару glEnable(GL_TEXTURE_2D)
и glDisable(GL_TEXTURE_2D)
. Вы включаете текстурирование в коде инициализации, что неверно. Вы должны установить все необходимые состояния непосредственно перед рендерингом, особенно такое сильно зависящее от объекта состояние, как текстурирование. Например, после того, как вы вызываете draw_rect
before drawTexture
, вы в конечном итоге отключаете текстурирование, хотя вы включили его в коде инициализации и думали, что оно всегда включено. Вы видите, что это не очень хорошая идея?
РЕДАКТИРОВАТЬ: я только что обнаружил другую ошибку. В draw_rect
вы вызываете glEnable
и glDisable
с GL_TEXTURE_COORD_ARRAY
помощью, что неправильно. Вы должны использовать glEnableClientState
и glDisableClientState
для включения / отключения массивов вершин, как вы делали int drawTexture
.
Итак, в качестве небольшого промежуточного вывода ваши функции на самом деле должны выглядеть следующим образом:
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);
}
}
Комментарии:
1. Большое спасибо за ответ — я изменил порядок расположения вершин против часовой стрелки, но без кубиков… Отключение отбраковки обратной стороны также, похоже, не повлияло на проблему.. Итак, мне кажется, проблема сейчас не в функциях рисования, а скорее где-то еще в моем коде, вы бы тоже склонялись в этом направлении?
2. @ultifinitus Вы также внесли предлагаемые изменения в
draw_rect
(удалите вызовglClientActiveTexture
? Это отображает текстуру? И вы убедились, что прямоугольник действительно виден в вашем текущем представлении и настройке проекции?3. Да, и да — интересно, что я удалил
glEnable(GL_TEXTURE_COORD_ARRAY);
(и отключил) изdraw_rect
функции и добавилglEnable(GL_TEXTURE_2D);
вdrawTexture
функцию. Теперь вторая функция работает так, как ожидалось, изображение действительно отображается!! Но другого нет. Большое вам спасибо за вашу помощь, если вы включите эти результаты в свой ответ, это было бы здорово!4. @ultifinitus Почему вы удалили включение / выключение массива texCoord? Это не должно сработать, потому что теперь у вас нет действительных координат текстуры, или вы случайно используете координаты из
drawTexture
функции, что еще более неправильно.5. @ultifinitus И вам также следует отключить массивы вершин и цветов перед возвратом из
draw_rect
.
Ответ №2:
Если одна из текстур работает, а другая нет, может ли это быть проблемой с файлом текстуры? Размеры иногда могут обмануть вас, попробуйте использовать один и тот же файл (тот, который работает) для обеих текстур и посмотрите, решается ли это. Если это происходит, это проблема с файлом текстуры.
Комментарии:
1. Действительно, так получилось, что его размеры должны быть в степени 2 (или, по крайней мере, это единственные, которые работали до сих пор)
Ответ №3:
Забава действительно работает.
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);
}
но мы должны привязать текстуру к правильной координате. нам следует изменить код
форма
GLfloat box[] = {x,y h, x w,y h, x,y, x w,y};
GLfloat tex[] = {0,0, 1,0, 1,1, 0,1};
Для
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 };
Спасибо stackoverflow. Спасибо за помощь.
Удачи!