Как я могу рисовать (как в GLPaint) на фоновом изображении и с временными рисунками? - PullRequest
4 голосов
/ 12 ноября 2010

Я пишу приложение для рисования в стиле GLPaint для iPad, однако наткнулся на камень преткновения.В частности, я сейчас пытаюсь реализовать две вещи:

1) Фоновое изображение, на которое можно рисовать.

2) Возможность рисовать временные фигуры, например, вы можете нарисоватьлиния, но окончательная форма будет принята только после того, как палец поднимется.

Для фонового изображения я понимаю, что идея состоит в том, чтобы нарисовать изображение в VBO и нарисовать его прямо перед каждым рисованием линий.Это нормально, но теперь мне нужно добавить возможность рисовать временные фигуры ... с kEAGLDrawablePropertyRetainedBacking, установленным в YES (как в GLPaint), временные, очевидно, не являются временными!Превращение сохраненного свойства поддержки в NO прекрасно работает для временных объектов, но теперь мои предыдущие строки от руки не сохраняются.

Какой здесь лучший подход?Должен ли я использовать более одного EAGLLayer?Кажется, что вся документация и учебники, которые я нашел, предполагают, что большинство вещей должно быть возможно с одним слоем.Они также говорят, что для сохраненной поддержки почти всегда должно быть установлено значение NO.Есть ли способ работы моего приложения в такой конфигурации?Я пытался сохранить каждую точку рисования в постоянно расширяющемся массиве вершин, чтобы перерисовывать каждый кадр, но из-за большого числа нарисованных спрайтов это не работает.

Я был бы очень признателен за любую помощь в этом,как я искал онлайн и ничего не нашел!

1 Ответ

11 голосов
/ 06 января 2011

С тех пор я нашел решение этой проблемы. Лучше всего использовать пользовательские объекты кадрового буфера и рендеринг в текстуру. Я не слышал об этом, прежде чем задавать вопрос, но он выглядит как невероятно полезный инструмент для набора инструментов OpenGLer!

Для тех, кто может захотеть сделать что-то подобное, идея заключается в том, что вы создаете FBO и прикрепляете к нему текстуру (вместо рендеринга буфера). Затем вы можете привязать это FBO и нарисовать его, как и любое другое, с той лишь разницей, что рисунки отображаются вне экрана. Тогда все, что вам нужно сделать, чтобы отобразить текстуру, это связать основное FBO и нарисовать текстуру (используя квад).

Итак, для своей реализации я использовал два разных FBO с прикрепленной к каждому текстурой - один для «сохраненного» изображения (для рисования от руки), а другой для «скретч-изображения» (для временных рисунков). Каждый раз, когда отображается кадр, я сначала рисую фоновую текстуру (в моем случае я просто использовал класс Texture2D), затем рисую оставшуюся текстуру и, наконец, текстуру царапин, если требуется. При рисовании временной формы все отрисовывается с нуля текстуры, и это очищается в начале каждого кадра. Как только это закончено, текстура царапины обращается к сохраненной структуре.

Вот несколько фрагментов кода, которые могут кому-то пригодиться:

1) Создайте фрейм-буферы (я только показал пару здесь, чтобы сэкономить место!):

// ---------- DEFAULT FRAMEBUFFER ---------- //
// Create framebuffer.
glGenFramebuffersOES(1, &viewFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

// Create renderbuffer.
glGenRenderbuffersOES(1, &viewRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

// Get renderbuffer storage and attach to framebuffer.
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

// Check for completeness.
status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
    NSLog(@"Failed to make complete framebuffer object %x", status);
    return NO;
}

// Unbind framebuffer.
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);


// ---------- RETAINED FRAMEBUFFER ---------- //
// Create framebuffer.
glGenFramebuffersOES(1, &retainedFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, retainedFramebuffer);

// Create the texture.
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
glGenTextures(1, &retainedTexture);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, retainedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);

// Attach the texture as a renderbuffer.
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, retainedTexture, 0);

// Check for completeness.
status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
    NSLog(@"Failed to make complete framebuffer object %x", status);
    return NO;
}

// Unbind framebuffer.
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

2) Рисуем на рендере в текстуру FBO:

// Ensure that we are drawing to the current context.
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, retainedFramebuffer);
glViewport(0, 0, 1024, 1024);

// DRAWING CODE HERE

3) Визуализируйте различные текстуры для основного FBO и представьте:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);       // Clear to white.
glClear(GL_COLOR_BUFFER_BIT);


glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

[self drawBackgroundTexture];
[self drawRetainedTexture];
[self drawScratchTexture];

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);


glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];

Например, при рисовании сохраненной текстуры с использованием [self drawRetainedTexture] будет использоваться следующий код:

// Bind the texture.
glBindTexture(GL_TEXTURE_2D, retainedTexture);

// Destination coords.
GLfloat retainedVertices[] = {
    0.0,          backingHeight,    0.0,
    backingWidth, backingHeight,    0.0,
    0.0,          0.0,              0.0,
    backingWidth, 0.0,              0.0
};

// Source coords.
GLfloat retainedTexCoords[] = {
    0.0, 1.0,
    1.0, 1.0,
    0.0, 0.0,
    1.0, 0.0
};

// Draw the texture.
glVertexPointer(3, GL_FLOAT, 0, retainedVertices);
glTexCoordPointer(2, GL_FLOAT, 0, retainedTexCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// Unbind the texture.
glBindTexture(GL_TEXTURE_2D, 0);

Много кода, но я надеюсь, что это кому-нибудь поможет. Это определенно поставило меня в тупик на некоторое время!

...