Как я могу отобразить растровое изображение RGB в GLKView с использованием 2D-текстур?

У меня есть "растровое изображение" в памяти, которое представляет собой всего лишь malloced массив Byte *, который содержит пиксельные данные в простом формате RGB (поэтому размер массива байтов равен 3 * числу пикселей).

Мое приложение - это просто контроллер вида с экземпляром GLKView. Я внедрил его делегат следующим образом:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}

и, как и ожидалось, это устанавливает цвет фона GLKView.

Теперь я хотел бы добавить код к этой реализации glkView:drawInRect:, чтобы моя "растровая карта" была отображена в этот GLKView. Но я не могу найти способ сделать это просто; Я немного ошеломлен всеми тем, что может сделать OpenGL, все из которых намного сложнее, чем то, что я пытаюсь сделать здесь.

glReadPixels кажется вроде бы тем, что я здесь, поскольку он, кажется, предоставляет указатель на данные буфера.

Изменить:, очевидно, это может быть достигнуто только с использованием текстур. Я попытался реализовать это с помощью этого образца кода (обратите внимание, что мой "битмап" здесь составляет 4 байта на образец, соответствующий параметрам формата):

- (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);

}

... , но он не работает. Вызов glClear() работает так, как ожидалось, и устанавливает весь красный фон; но если я правильно понимаю образцы текстур, то следующий код должен рисовать белый квадрат 30x30 в углу, но все, что я получаю, это сплошной красный фон.

Может ли кто-нибудь определить, что я делаю неправильно здесь?

Ответ 1

Извините, что нажал в OpenGL:)

Я бы создал CGImage, а затем 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];

а затем нарисуйте его drawInRect:

Изменить:

Вот код, который я написал. Это не производственный код: ему не хватает проверки на ошибки openGL, некоторые незначительные утечки и глобальные переменные, но это хорошее место для его улучшения.

#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

Я не знаю, как вы хотите обновлять данные, возможно, вам не нужно malloc/освобождать их каждый раз, а делать небольшие изменения и обновлять их с помощью glTexSubImage2D. Большую часть времени тратит на заполнение данных.

Этот код работал на моем MacBook с Xcode 7 в симуляторах и iPhone 6 с iOS 9.0.2.