Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
Let's say I have three triangles, and I want to draw these triangles on top of each other.
I draw simple triangles, most of them will be drawn on top of each other
My triangle is black, if it falls on a triangle with black, I want to fill that side with white, if it's on the white side, I want to fill it with black. I hope this picture will help you better understand my problem
I want to invert colors, is it possible to flip pixels on the GPU.
Possible solutions - stencil buffer or winding number, but i don't even know where to start.
There are a few ways to achieve this.
(Note: I use the C API bellow out of convenience.)
(Disclaimer: The code snippets below are untested.)
Stencil test
Make sure that your framebuffer has a stencil attachment -- depends on the way you create it.
Clear the stencil buffer to 0 (the default):
glClear(GL_STENCIL_BUFFER_BIT);
Disable stencil test and color writes:
glDisable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
Make rasterized fragments 'invert' the stencil value:
glStencilMask(1);
glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
... draw all your triangles. When done, your stencil buffer will have a non-zero value only for fragments with an odd winding number -- these are the fragments that need to be filled. This is, in essence, the mask you need to use to shade your final shape.
Enable stencil test, passing only if non-zero:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 0 /*value*/, 1 /*mask*/);
Enable color writes and disable stencil writes:
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilMask(0);
Now draw a full-screen quad with the color/shading that you want your shape to be (e.g. flat black in your example). Only fragments with non-zero stencil will be filled.
Logical operations
This is more limited in application, but is simpler and cuts the bill for what you requested. Simply enable:
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_INVERT);
And proceed drawing your triangles. The color of the triangles doesn't matter, cause it'll just apply a bitwise NOT on the values in the framebuffer.
Blending
Similar to the above, but will give different results when colors are other than black&white. You can invert through the blending equation as follows:
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_SUBTRACT);
glBlendFunc(GL_ONE, GL_ONE);
Then the color will be computed as:
out = 1 * src - 1 * dst
So if you render your triangles in white (src = 1) you effectively get color inversion every time you blend a fragment:
out = 1 - dst
Analytic triangulation
Finally you can use a geometry library to compute the resulting shape analytically, triangulate it, and draw that. However I cannot point to any readily accessible library or code to accomplish that.
Related
I am trying to make a specific VBO (vertex buffer object) to not be drawn in specific screen area (but be drawn in the rest of the screen).
I have two VBOs that must be drawn in the screen. They can appear independently, parts of VBO1 overlap VBO2 and VBO1 has priority over VBO2. When both objects are being drawn, I want VBO2 to not render in the overlap area, but render the rest of object. Since the size of VBO2 can change, trying to achieve this behaviour with EBOs (element buffer objects) can be tricky.
In some sense, what i am looking for is the inverse of GL_SCISSORS_TEST. If in GL_SCISSORS_TEST I can define an area where the symbol can only de drawn inside (and the part that is outside is clipped), I want to be able to define an area where a symbol will not be drawn inside (but the part of the symbol that is outside the area will be drawn).
Which strategies would be best to achieve this in Opengl?
There are several different ways how the desired result can be achieved:
For the general case, the stencil test seems to be a good option. Note, that your framebuffer needs to support stencil buffering.
Clear stencil buffer with 0
Draw VBO1 with the following settings which set the stencil buffer to 1 for all pixels covered by VBO1. If you need a larger range than just VBO1, consider rendering a special stencil shape with color writing disabled that marks the non-drawable area.
glStencilFunc(GL_ALWAYS, 1, 0xFF),
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
Draw VBO2 with following settings which only draw VBO2 where the stencil buffer has a value of 0 (aka, where VBO1 has not drawn):
glStencilFunc(GL_EQUAL, 0, 0xFF),
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
If the VBOs contain only 2-dimensional objects parallel to the screen plane (like sprites), then you could also use depth testing and render VBO2 further away than VBO1.
If you have a more specialized case like rectangles (as suggested by siccor testing which only works on rectangular areas), it is also an option to pass the boundary of the VBO1 rectangle to the fragment shader of VBO2 and discard fragments if they fall inside the boundary area.
Before diving into details, I have added opengl tag because JoGL is a Java OpenGL binding and the questions seem to be accessible for experts of both to answer.
Basically what I am trying to do is to render the grid over the texture in JoGL using GLSL. So far, my idea was to render first the texture and draw the grid on top. So what I am doing is:
gl2.glBindTexture(GL2.GL_TEXTURE_2D, textureId);
// skipped the code where I setup the model-view matrix and where I do fill the buffers
gl2.glVertexAttribPointer(positionAttrId, 3, GL2.GL_FLOAT, false, 0, vertexBuffer.rewind());
gl2.glVertexAttribPointer(textureAttrId, 2, GL2.GL_FLOAT, false, 0, textureBuffer.rewind());
gl2.glDrawElements(GL2.GL_TRIANGLES, indices.length, GL2.GL_UNSIGNED_INT, indexBuffer.rewind());
And after I draw the grid, using:
gl2.glBindTexture(GL2.GL_TEXTURE_2D, 0);
gl2.glDrawElements(GL2.GL_LINE_STRIP, indices, GL2.GL_UNSIGNED_INT, indexBuffer.rewind());
Without enabling the depth test, the result look pretty awesome.
But when I start updating the coordinates of the vertices (namely updating one of its axes which corresponds to height), the rendering is done in a wrong way (some things which should be in front appear behind, which makes sense without the depth test enabled). So I have enabled the depth test:
gl.glEnable(GL2.GL_DEPTH_TEST);
gl.glDepthMask(true);
An the result of the rendering is the following:
You can clearly see that the lines of the grid are blured, some of the are displayed thinner then others, etc. What I have tried to do to fix the problem is some line smoothing:
gl2.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST);
gl2.glEnable(GL2.GL_LINE_SMOOTH);
The result is better, but I am not still satisfied.
QUESTION: So basically the question is how to improve further the solution, so I can see solid lines and those are displayed nicely when I start updating the vertex coordinates.
If it is required I can provide the code of Shaders (which is really simple, Vertex Shader only calculates the position based on projection, model view matrix and the vertex coords and Fragment Shader calculates the color from texture sampler).
In libGdx, i'm trying to create a shaped texture: Take a fully-visible rectangle texture and mask it to obtain a shaped textured, as shown here:
Here I test it on rectangle, but i will want to use it on any shape. I have looked into this tutorial and came with an idea to first draw the texture, and then the mask with blanding function:
batch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_ALPHA);
GL20.GL_ZERO - because i really don't want to paint any pixels from the mask
GL20.GL_SRC_ALPHA - from original texture i want to paint only those pixels, where mask was visible (= white).
Crucial part of the test code:
batch0.enableBlending();
batch0.begin();
batch0.draw(original, 0, 0); //to see the original
batch0.draw(mask, width1, 0); //and the mask
batch0.draw(original, 0, height1); //base for the result
batch0.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_ALPHA);
batch0.draw(mask, 0, height1); //draw mask on result
batch0.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
batch0.end();
The center ot the texture get's selected well, but instead of transparent color around, i see black:
Why is the result blank and not transparent?
(Full code - Warning: very messy)
What you're trying to do looks like a pretty clever use of blending. But I believe the exact way you apply it is "broken by design". Let's walk through the steps:
You render your background with red and green squares.
You render an opaque texture on top of you background.
You erase parts of the texture you rendered in step 2 by applying a mask.
The problem is that for the parts you erase in step 3, the previous background is not coming back. It really can't, because you wiped it out in step 2. The background of the whole texture area was replaced in step 2, and once it's gone there's no way to bring it back.
Now the question is of course how you can fix this. There are two conventional approaches I can think of:
You can combine the texture and mask by rendering them into an off-sreen framebuffer object (FBO). You perform steps 1 and 2 as you do now, but render into an FBO with a texture attachment. The texture you rendered into is then a texture with alpha values that reflect your mask, and you can use this texture to render into your default framebuffer with standard blending.
You can use a stencil buffer. Masking out parts of rendering is a primary application of stencil buffers, and using stencil would definitely be a very good solution for your use case. I won't elaborate on the details of how exactly to apply stencil buffers to your case in this answer. You should be able to find plenty of examples both online and in books, including in other answers on this site, if you search for "OpenGL stencil". For example this recent question deals with doing something similar using a stencil buffer: OpenGL stencil (Clip Entity).
So those would be the standard solutions. But inspired by the idea in your attempt, I think it's actually possible to get this to work with just blending. The approach that I came up with uses a slightly different sequence and different blend functions. I haven't tried this out, but I think it should work:
You render the background as before.
Render the mask. To prevent it from wiping out the background, disable writing to the color components of the framebuffer, and only write to the alpha component. This leaves the mask in the alpha component of the framebuffer.
Render the texture, using the alpha component from the framebuffer (DST_ALPHA) for blending.
You will need a framebuffer with an alpha component for this to work. Make sure that you request alpha bits for your framebuffer when setting up your context/surface.
The code sequence would look like this:
// Draw background.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glDisable(GL_BLEND);
// Draw mask.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_BLEND);
glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
// Draw texture.
A very late answer, but with the current version this is very easy. You simply draw the mask, set the blending mode to use the source color to the destination and draw the original. You'll only see the original image where the mask is.
//create batch with blending
SpriteBatch maskBatch = new SpriteBatch();
maskBatch.enableBlending();
maskBatch.begin();
//draw the mask
maskBatch.draw(mask);
//store original blending and set correct blending
int src = maskBatch.getBlendSrcFunc();
int dst = maskBatch.getBlendDstFunc();
maskBatch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
//draw original
maskBatch.draw(original);
//reset blending
maskBatch.setBlendFunction(src, dst);
//end batch
maskBatch.end();
If you want more info on the blending options, check How to do blending in LibGDX
I'm rendering a quad-mesh to an off-screen framebuffer in OpenGL with possibly overlapping quads (more fragments into a single framebuffer pixel). All quads lie in the plane y=0.
I would like to know if there is an easy way to set a color (e.g. black) to pixels that have overlapping quads (preferrably without the need of an extra shader pass, using simple OpenGL functionality).
You could use the stencil buffer to count the "overdraw" per pixel with something like
glClear(GL_STENCIL_BUFFER_BIT)<
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_INCR, GL_INCR);
glStencilFunc(GL_ALWAYS, 0, 0xffffff);
which would simply clear the stencil buffer to 0 and increase it for every fragment you draw after that (and also for all fragments you generate, but fail the depth test). After you have drawn all quads, you could draw another fullscreen quad with the desired color, but the stencil test set to
glStencilFunc(GL_GREATER, 1, 0xffffff);
to only affect the pixels with more than one quad.
This still requires some kind of "extra pass", but the stencil test is quite efficient.
I have drawn multiple different colored polygons on the screen, now I have to draw another polygon of different color, but this polygon should be drawn only on those pixels which have a specific color.
I render each of the different colored polygons at same time in their own "layers", (= one color at a time). They can cover each other; newest layer covers all previous layers. The black color in the image is the "no polygons" area: empty space, and it should ignore that too.
So, basically I just render polygons, and then the N'th (not first) layer of polygons must be masked with the next polygon layer, and nothing else under it should be affected.
Image of the method needed:
What method can I use to achieve this with OpenGL ? I would prefer non-shader solution for this, if possible(?).
The only method I can do currently is to render each of the layers separately into the memory, then go through the pixels myself and combine the layers "manually", but that seems like a very slow method, doable though, but the speed is important here.
To use the stencil buffer for this, what you can do is:
Make sure you request a context that has a stencil buffer, this is windowing system specific so I won't cover it here. Call glGet(GL_STENCIL_BITS) to make sure that you get a sufficient number of bits.
The stencil buffer maintains an integer alongside each pixel, and allows you to modify it as things are drawn. As you draw each layer, set up the stencil buffer to set to a specific value when you draw each layer. You do this with
//draw layer N
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, N, -1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
Now as you draw each layer, the last polygon that was drawn to the screen also stores it's layer number into the stencil buffer.
At this point when you want to go back and draw your green star, you just tell it to only draw on pixels where the stencil buffer is equal to N.
//draw only where stencil == N
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, N, -1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
drawStar();