Приложение Paint, использующее фреймбуфер для рендеринга текстур в OpenGL ES - PullRequest
3 голосов
/ 21 ноября 2011

Я пытаюсь сделать простое приложение для рисования на основе Apple GLPaint.Чтобы нарисовать линию, GLPaint рисует массив точек с текстурой кисти.С включенным смешиванием в OpenGL каждая точка этой линии с альфа-каналом меньше 1 смешивается с предыдущими.Я хочу избежать этого.Например, вы устанавливаете красный цвет на кисть, а альфа на 0,5, рисуете линию, и вся линия одноцветная - красная с альфа 0,5, без самопересечений ... Я не могу объяснить, ясно, но выпонял меня))

Итак, первый вопрос: как я могу это сделать?

Я решил нарисовать текущую линию в текстуру, без наложения, а затем наложить текущее изображение.Код:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        CGRect  bounds = [mainView bounds];
         UITouch* touch = [[event touchesForView:mainView] anyObject];
        firstTouch = YES;       location = [touch locationInView:mainView];
        location.y = bounds.size.height - location.y;

         // Offscreen buffer
    glGenRenderbuffersOES(1, &brushFramebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER_OES, brushFramebuffer);
        glGenRenderbuffers(1, &brushDepthBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, brushDepthBuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rushDepthBuffer);

        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if(status != GL_FRAMEBUFFER_COMPLETE) {
                NSLog(@"failed to make complete framebuffer object %x", status);
                return;
        }
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
        CGRect  bounds = [mainView bounds];
        UITouch* touch = [[event touchesForView:mainView] anyObject];
        if (firstTouch) {
                firstTouch = NO;
                previousLocation = [touch locationInView:mainView];
                previousLocation.y = bounds.size.height - previousLocation.y;
        }
         else {
                location = [touch locationInView:mainView];
                location.y = bounds.size.height - location.y;
                previousLocation = [touch previousLocationInView:mainView];
                previousLocation.y = bounds.size.height - previousLocation.y;
        }

        [self renderLineFromPoint:previousLocation toPoint:location];
}

 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
         CGRect bounds = [mainView bounds];
         UITouch* touch = [[event touchesForView:mainView] anyObject];
        if (firstTouch) {
                firstTouch = NO;
                previousLocation = [touch previousLocationInView:mainView];
                previousLocation.y = bounds.size.height - previousLocation.y;
                [self renderLineFromPoint:previousLocation toPoint:location];
         }

          // getting texture from buffer
        glBindTexture(GL_TEXTURE_2D, bufferTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 768, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, bufferTexture, 0);

        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);

        // drawing texture to image
        CGFloat vertices[] = {backingWidth/2, backingHeight/2};

        glVertexPointer(2, GL_FLOAT, 0, vertices);
        glDrawArrays(GL_POINTS, 0, 1);

        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

        [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
- (void) renderLineFromPoint:(CGPoint)start toPoint:(CGPoint)end {
        [EAGLContext setCurrentContext:context];
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, brushFramebuffer);
        glBindTexture(GL_TEXTURE_2D, brushTexture);
          /// .... then drawing a line from "start" to "end"
}

Второй вопрос: этот код рисует только рисование текстуры кисти в центре экрана, что я делаю не так?

1 Ответ

0 голосов
/ 11 января 2012

существует простой способ сделать это со строками (без кисти): просто используйте тест глубины (очистите буфер глубины, установите для функции func значение GL_NOT_EQUAL или оставьте значение GL_LESS), чтобы избежать рендеринга в одной и той же позиции дважды.Однако это не сработает с более сложными кистями.

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

glGenRenderbuffersOES(1, &brushFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER_OES, brushFramebuffer);
// creates FBO

glGenRenderbuffers(1, &brushDepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, brushDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rushDepthBuffer);
// creates and attaches RB for depth

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, bufferTexture, 0);
// attaches texture to render to. now we're ready.

Далее, вы не вызываете glTexImage2D () в touchesEnded, что стерло бы весь рендеринг, сделанный для этой текстуры до сих пор.Можно вызывать glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);но вам лучше переместить эту строку в раздел инициализации, где вы создаете bufferTexture.Чтобы прекратить рендеринг в кадровый буфер, достаточно просто вызвать:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

Текстура содержит изображение, отображаемое при активном FBO, и рисунок возвращается на экран (кадровый буфер приложения).

Теперь,потому что вы рисовали с использованием brushTexture, вам нужно:

glBindTexture(GL_TEXTURE_2D, bufferTexture);

А затем вы просто отрисовываете полноэкранный квад:

CGFloat vertices[] = {0, 0,
                      backingWidth, 0,
                      backingWidth, backingHeight,
                      0, backingHeight};
CGFloat texCoords[] = {0, 0,
                      1, 0,
                      1, 1,
                      0, 1,};
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_QUADS, 0, 4);

И это все ...

ВrenderLineFromPoint () не требуется вызывать

glBindFramebufferOES(GL_FRAMEBUFFER_OES, brushFramebuffer);

, так как он уже был связан в touchesBegan.Но это не повредит.

И еще одна вещь - вы должны убедиться, что прикосновения Beg, TouchMoved и TouchEnded действительно идут в таком порядке.Я не уверен, что это гарантировано.

...