简体   繁体   English

绘制应用程序,使用framebuffer在OpenGL ES中渲染纹理

[英]Paint app, using framebuffer to render texture in OpenGL ES

I'm trying to make simple paint application, based on Apple's GLPaint. 我正在尝试基于Apple的GLPaint制作简单的绘画应用程序。 To draw a line, GLPaint draws an array of points with brush texture. 要绘制线条,GLPaint会使用画笔纹理绘制一系列点。 With blending in OpenGL turned on, every point of that line with alpha less than 1 is blended with previous ones. 打开OpenGL中的混合后,alpha小于1的那一行的每个点都与之前的点混合。 I want to avoid this. 我想避免这种情况。 For example, you set red color to brush and alpha to 0.5, draw a line and all the line is single-color - red with alpha 0.5, without self crossings... I can't explain clear, but I hope you've understood me)) 例如,你将红色设置为画笔,将alpha设置为0.5,绘制一条线,所有线条都是单色 - 红色,带alpha 0.5,没有自我交叉......我无法解释清楚,但我希望你能明白我))

So, first question is how can I do this? 那么,第一个问题是我该怎么做?

I've decided to draw current line to texture, without blending, and then put over current image. 我已决定将当前线绘制到纹理,而不进行混合,然后将当前图像放在当前图像上。 The code is: 代码是:

- (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"
}

The second question is: this code draws only draws brush texture to the center of screen, what am I doing wrong? 第二个问题是:这段代码只绘制画笔纹理到屏幕中心,我做错了什么?

there is a simple way of how to do this with lines (with no brush): simply use depth test (clean depth buffer, set depth func to GL_NOT_EQUAL or keep it at GL_LESS) to avoid rendering on the same position twice. 有一种简单的方法可以用线条(没有画笔):只需使用深度测试(干净深度缓冲,将深度函数设置为GL_NOT_EQUAL或将其保持在GL_LESS),以避免在同一位置渲染两次。 However, that would not work with more complex brushes. 但是,这对于更复杂的画笔不起作用。

Your code has several principal flaws. 您的代码有几个主要缺陷。 First, if you want to render to a texture using FBO, you need to call glFramebufferTexture2D() before you do any rendering to the buffer (because that functions tells OpenGL that it should direct rendering output to that texture from that point on, it doesn't do any copying). 首先,如果你想使用FBO渲染到纹理,你需要在对缓冲区进行任何渲染之前调用glFramebufferTexture2D()(因为该函数告诉OpenGL它应该从那个点开始将渲染输出引导到那个纹理,它不会不做任何复制)。 So the order of operations in touchesBegan become: 所以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.

Next, you don't call glTexImage2D() in touchesEnded, that would erase all the rendering done to that texture so far. 接下来,你不要在touchesEnded中调用glTexImage2D(),这将擦除到目前为止对该纹理所做的所有渲染。 It is okay to call glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 可以调用glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); but you would be better off to move that line to your init section where you create bufferTexture. 但最好将该行移动到创建bufferTexture的init部分。 To stop rendering to framebuffer, one simply calls: 要停止渲染到帧缓冲区,只需调用:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

The texture contains the image rendered while FBO was active, and drawing returned to the screen (the application framebuffer). 纹理包含FBO处于活动状态时渲染的图像,并且绘图返回到屏幕(应用程序帧缓冲区)。

Now, because you were drawing using brushTexture, you need to: 现在,因为您使用brushTexture绘图,所以您需要:

glBindTexture(GL_TEXTURE_2D, bufferTexture);

And then you just render fullscreen quad: 然后你只需渲染全屏四边形:

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

And that's it ... 就是这样......

In renderLineFromPoint() it is not required to call 在renderLineFromPoint()中,不需要调用

glBindFramebufferOES(GL_FRAMEBUFFER_OES, brushFramebuffer);

as it was already bound in touchesBegan. 因为它已经绑定在touchesBegan中。 But it won't hurt. 但它不会受到伤害。

And one more thing - you should make sure that the touchesBegan, touchesMoved and touchesEnded really come in that order. 还有一件事 - 你应该确保touchesBegan,touchesMoved和touchesEnded确实按顺序排列。 I'm not sure this is guaranteed. 我不确定这是否有保证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM