Приложение Paint, использующее кадровый буфер для визуализации текстуры в OpenGL ES

Я пытаюсь сделать простое приложение для рисования на основе 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"
}

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


person medvedNick    schedule 21.11.2011    source источник


Ответы (1)


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

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

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

так как он уже был привязан в касаниях начал. Но больно не будет.

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

person the swine    schedule 10.01.2012