Objective c Paint应用程序,在OpenGL ES中使用帧缓冲区渲染纹理

Objective c Paint应用程序,在OpenGL ES中使用帧缓冲区渲染纹理,objective-c,opengl-es,textures,paint,framebuffer,Objective C,Opengl Es,Textures,Paint,Framebuffer,我正在尝试基于苹果的GLPaint制作简单的绘画应用程序。要绘制直线,GLPaint将使用笔刷纹理绘制点阵列。启用“在OpenGL中混合”后,alpha小于1的直线上的每个点都将与以前的点混合。我想避免这种情况。例如,将“红色”设置为“笔刷”,将“alpha”设置为0.5,绘制一条线,所有线都是单色的-红色,alpha为0.5,没有自交叉。。。我无法解释清楚,但我希望你理解我的意思 所以,第一个问题是,我如何才能做到这一点 我决定将当前线绘制到纹理,而不进行混合,然后覆盖当前图像。代码是: -

我正在尝试基于苹果的GLPaint制作简单的绘画应用程序。要绘制直线,GLPaint将使用笔刷纹理绘制点阵列。启用“在OpenGL中混合”后,alpha小于1的直线上的每个点都将与以前的点混合。我想避免这种情况。例如,将“红色”设置为“笔刷”,将“alpha”设置为0.5,绘制一条线,所有线都是单色的-红色,alpha为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"
}

第二个问题是:这段代码只将画笔纹理绘制到屏幕的中心,我做错了什么?

有一种简单的方法来处理线条(不使用画笔):只需使用深度测试(清除深度缓冲区,将depth func设置为GL_NOT_或将其保持在GL_LESS)即可避免在同一位置渲染两次。但是,对于更复杂的笔刷,这将不起作用

您的代码有几个主要缺陷。首先,如果要使用FBO渲染到纹理,则需要在对缓冲区进行任何渲染之前调用glFramebufferTexture2D()(因为该函数告诉OpenGL,它应该从该点开始将渲染输出定向到该纹理,而不进行任何复制)。因此,touches中的操作顺序开始变为:

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.
接下来,不要在touchesEnded中调用glTexImage2D(),这将擦除到目前为止对该纹理所做的所有渲染。可以调用glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);但最好将该行移到创建bufferTexture的init部分。要停止渲染到帧缓冲区,只需调用:

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);
因为它已经在接触中被束缚了,所以开始了。但是不会痛的

还有一件事——你应该确保触摸开始、触摸移动和触摸结束的顺序是正确的。我不确定这是不是可以保证的