How to avoid transparency overlap using OpenGL?
Asked Answered
L

3

7

I am working on a handwriting application on iOS. I found the sample project "GLPaint" from iOS documentation which is implemented by OpenGL ES, and I did something modification on it.

I track the touch points and calculate the curves between the points and draw particle images alone the curve to make it looks like where the finger passby.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData); // burshData is from CGImage, it is 

// vertexBuffer is generated based on the calculated points, it's just a sequence of point where need to draw image.
glVertexPointer(2, GL_FLOAT, 0, vertexBuffer); 
glDrawArrays(GL_POINTS, 0, vertexCount);

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

What I got is a solid line which looks quite good. But now I want to draw semi-transparent highlight instead of solid line. So I replace the particle image with a 50% transparency one without changing code.

Result of 50% transparency particle image

Result of 50% transparency particle image

There is something wrong with blend.

What I need

What I need

I draw three points using the semi-transparency particle image, and the intersection area should keep 50% transparency.

What's the solution?

Lessor answered 4/1, 2013 at 9:48 Comment(2)
The problem is you need to eliminate the overdraw, which for the general case is not possible with "just blending". You could use depth buffer (with alpha test) but that will not properly antialias. If you want this to be properly antialiased, that's not trivial to get right, at least not in the general case. You would need to render to an extra framebuffer first, and composite in a second pass.Iseult
Thanks for your reply. I am a newbie to OpenGL, I am still not clear about the most conceptions in OpenGL. I think antialias is not the problem. Could you please state the code with more details?Lessor
S
6

Im maybe two years later answering that question, but i hope it helps somebody who comes here looking for a solution to this problem, like it happened to me.

You are going to need to assign to each cirle a different z value. It doesn't matter how big or small this difference is, we only need them to not be strictly equal.

First, you disable writing in the color buffer glColorMask(false,false,false,false) , and then draw the circles normally. The Z-buffer will be updated as desired, but no circles will be drawn yet.

Then, you enable writing in the color buffer (glColorMask(true,true,true,true) ) and set the depthFunc to LEQUAL ( glDepthFunc(GL_LEQUAL) ). Only the nearest circle pixels will pass the depth test (Setting it to LEQUAL instead of EQUAL deals with some rare but possible floating point approximation errors). Enabling blending and drawing them again will produce the image you wanted, with no transparency overlap.

Superfluid answered 18/9, 2015 at 16:34 Comment(3)
Really thanks for your answer. One more question. What do you mean by "assigning different z value". Is that the third dimension of the coordinate or the z-buffer value? Sorry if this question make little sense, I am a newbie to opengl.Adventitia
Both at the same time! Inside the shaders, the X and Y coordinates control the position in screen of each vertex (-1 to 1) and the Z coordinate control the depth (0 to 1, being 0 nearer). This depth is the one compared against the Z-buffer - if the Z-buffer in that pixel has a lesser value it means that there is already a pixel drawn nearer to the camera, so the fragment is discarded. So, if every circle has a different Z coordinate, only one fragment per pixel will not be discarded (the nearest to the camera) so the blending will be successful.Superfluid
You might be confused because most of the times we apply a coordinate transformation inside the vertex shader using transformation matrixes. So, you have an initial X,Y,Z for a vertex and it gets transformed to another position to align it with the camera or whatever, so the real Z used by the shader might not be the original Z of your vertex. It's slightly complicated to explain if you are new to OpenGL, so i strongly recommend you to read this tutorial (until the "Depth and stencil" part), open.gl , it helped me greatly when i was starting. Don't hesitate to ask me more if you need!Superfluid
M
0

You have to change the blend function. You can play around it with:

glBlendFunc(GL_SRC_ALPHA,GL_ONE);

Maybe (GL_ONE, GL_ONE), forgot how to handle your case, but the solution is in that function.

http://www.opengl.org/sdk/docs/man/xhtml/glBlendFunc.xml

Mcevoy answered 4/1, 2013 at 9:54 Comment(0)
P
0

Late reply but hopefully useful for others.

Another way to avoid that effect is to grab the color buffer before transparent circles are drawn (ie. do a GrabPass) and then read and blend manually with the opaque buffer in the fragment shader of your circles.

Percent answered 10/4, 2019 at 6:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.