I have an OpenGL problem to solve. I have an object/mesh A, an object/mesh B and a background texture C.
Initially the framebuffer is filled with background texture C. We draw both A & B in the framebuffer. We want to keep object A visible, and object B always invisible.
In the beginning, A is in front of B. During rotation, at a certain angle, B is in front of A based on the depth test result, but since B is always invisible, B's part should be filled with background C.
Does anyone know a simple approach to solve this issue?
Is stencil test a good approach? Basically set object B with a color, compare the color of B with background C, and show the background C when the test fail.
Does anyone have any sample code I can read?
The easiest solution is to:
draw C;
draw B with the colour mask preventing writes to the frame buffer (but don't touch the depth mask, so that writes are still made to the depth buffer);
draw A, subject to the depth test.
The specific thing to use is glColorMask — if you supply GL_FALSE for each channel via that then subsequent geometry won't write any colour output. But assuming you haven't touched glDepthMask it'll still write depth output.
So, you've probably currently got the code:
drawBackground(C);
render(A);
render(B);
You'd just adapt that to:
drawBackground(C);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
render(B);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
render(A);
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.
continue to this question :
GLSL : accessing framebuffer to get RGB and change it
Is it possible to develop program based on GLSL as follows?
draw object1
get depth buffer using shader (also save rgb)
draw object 1, object 2 simultaneously
get detph
check if depths are different (depth 2 vs depth 4)
draw object 1
: for the range that depth isn't changed -> draw as original RGB
: for the range that depth is changed -> draw with different RGB
I confirmed this algorithm which distinguishes object 1 is hidden by other object using glut functions.
I used glReadbuffer, glDrawbuffer functions. However those are too slow, I want to use GLSL.
If the only goal is to render object1 with a different color set when it is hidden behind object2 (which are the pixels of object1 where depth has changed) I would go for a completely different approach.
Draw object2 with depth-write only (glDrawBuffer(GL_NONE) or glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE))
Draw object1 with depth test set to glDepthFunc(GL_GREATER) and the coloring you want when object1 is behind object2
Draw object1 with depth test set to glDepthFunc(GL_LESS) and the coloring you want when object1 is in front of object2.
In contrast to the algorithm you described in the question, there is no need for any read-back operations or additional framebuffers
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 have to support some legacy code which draws point clouds using the following code:
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, (float*)cloudGlobal.data());
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, (float*)normals.data());
glDrawArrays(GL_POINTS, 0, (int)cloudGlobal.size());
glFinish();
This code renders all vertices regardless of the angle between normal and the "line of sight". What I need is draw only vertices whose normals are directed towards us.
For faces this would be called "culling", but I don't know how to enable this option for mere vertices. Please suggest.
You could try to use the lighting system (unless you already need it for shading). Set ambient color alpha to zero, and then simply use alpha test to discard the points with zero alpha. You will probably need to set quite high alpha in diffuse color in order to avoid half-transparent points, in case alpha blending is required to antialiass the points (to render discs instead of squares).
This assumes that the vertices have normals (but since you are talking about "facing away", I assume they do).
EDIT:
As correctly pointed out by #derhass, this will not work.
If you have cube-map textures, perhaps you can copy normal to texcoord and perform lookup of alpha from a cube-map (also in combination with the texture matrix to take camera and point cloud transformations into account).
Actually in case your normals are normalized, you can scale them using the texture matrix to [-0.49, +0.49] and then use a simple 1D (or 2D) bar texture (half white, half black - incl. alpha). Note that counterintuitively, this requires texture wrap mode to be left as default GL_REPEAT (not clamp).
If your point clouds have shape of some closed objects, you can still get similar behavior even without cube-map textures by drawing a dummy mesh with glColorMask(0, 0, 0, 0) (will only write depth) that will "cover" the points that are facing away. You can generate this mesh also as a group of quads that are placed behind the points in the opposite direction of their normal, and are only visible from the other side than the points are supposed to be visible, thus covering them.
Note that this will only lead to visual improvement (it will look like the points are culled), not performance improvement.
Just out of curiosity - what's your application and why do you need to avoid shaders?
I would like to for example draw shapes A, B, C then set the blender, then draw shape D and everywhere where shape D is, the scene shows my background color of (1,1,1,0). Thanks
I'm not aware of that being possible directly. There are a few possibilities to get the same effect though. The cleanest would probably be to start by filling the stencil buffer with 1's, then drawing shape D into the stencil buffer with 0's, then draw everything else (with the stencil buffer enabled, of course).
Much simpler than other answers :
Display shapes A, B and C the normal way
glDisable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
Display shape D with color (1,1,1,0)
and you're done.
glBlendEquation can do it.