overlapping partially transparent shapes in openGL - opengl-es

Please check this neat piece of code I found:
glEnable(GL_LINE_SMOOTH);
glColor4ub(0, 0, 0, 150);
mmDrawCircle( ccp(100, 100), 20, 0, 50, NO);
glLineWidth(40);
ccDrawLine(ccp(100, 100), ccp(100 + 100, 100));
mmDrawCircle( ccp(100+100, 100), 20, 0, 50, NO);
where mmDrawCircle and ccDrawLine just draws these shapes [FILLED] somehow... (ccp means a point with the given x, y coordinates respectively).
My problem .... Yes, you guessed it, The line overlaps with the circle, and both are translucent (semi transparent). So, the final shape is there, but the overlapping part becomes darker and the overall shape looks ugly.. i.e, I would be fine if I was drawing with 255 alpha.
Is there a way to tell OpenGL to render one of the shapes in the overlapping parts??
(The shape is obviously a rectangle with rounded edges .. half-circles..)

You could turn on GL_DEPTH_TEST and render the line first and a little closer to the camera. When you then render the circle below, the fragments of the line won't be touched.
(You can also use the stencil buffer for an effect like this).
Note that this might still look ugly. If you want to use anti-aliasing you should think quite hard on which blending modes you apply and in what order you render the primitives.

Related

draw circle with varying stroke weight

how would a go about drawing the inner blue slice of this circle, to simulate varying stroke weight.
I have tried a approach where i draw the stroke by drawing small circles on each angle of the circle and increasing the radius on certain parts of the circle. But this doesnt give the right result because the circle gets "pixelated" in the edge, and it skews the circle outwards.
There is no easy way to accomplish this. Part of the difficulty is that Canvas, the underlying technology that p5.js uses to draw graphics, doesn't support variable stroke weights either. In Scalable Vector Graphics, which has similar limitations, the best way to accomplish this would be to describe the shape as the outer perimeter, and the perimeter of the inner void, and then fill the shape without any stroke. I think Canvas would support this approach, but I don't think it can be done easily with p5.js because there's now way to jump to a new position when drawing bezier curves with beginShape()/bezierVertex(). However, one way you could do this in p5.js would be to fill the outer shape and then "remove" the inner void. If you want to draw this on top of other existing graphics then the best way is to draw this shape to a separate p5.Graphics object which you then draw to your main canvas with image():
let sprite;
function setup() {
createCanvas(windowWidth, windowHeight);
sprite = createGraphics(100, 100);
sprite.noStroke();
sprite.fill('black');
sprite.angleMode(DEGREES);
sprite.circle(50, 50, 100);
// switch to removing elements from the graphics
sprite.erase();
// Translate and rotate to match the shape you showed in your question
sprite.translate(50, 50);
sprite.rotate(-45);
// Remove a perfect semi circle from one half, producing regular 5px stroke circle
sprite.arc(0, 0, 90, 90, -90, 90);
// Remove a half-ellipse from the other side of the circle, but this time the
// height matches the previous arc, but the width is narrower.
// Note: the angles for this arc overlap the previous removal by a few degrees
// to prevent there from being a visible seam in between the two removed shapes.
sprite.arc(0, 0, 70, 90, 85, 275, OPEN);
}
function draw() {
background('lightgray');
image(sprite, mouseX - 50, mouseY - 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

HTML canvas fillRect with low opacity doesn't affect dark pixels

Repeatedly drawing a semi-opaque black rectangle over the entire canvas before each animation frame is an easy way to get an afterimage effect for moving shapes and it gives me exactly what I need - up to a point. With too slow a fade it doesn't fade all the way to black. Here's an example:
var canv = document.createElement('canvas');
document.body.appendChild(canv);
var ctx = canv.getContext('2d');
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
ctx.fillRect(0, 0, 100, 100);
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.fillRect(20, 20, 60, 60);
window.requestAnimationFrame(doFade);
function doFade() {
// Never fades away completely
ctx.fillStyle = 'rgba(0, 0, 0, 0.02)';
ctx.fillRect(20, 20, 60, 60);
window.requestAnimationFrame(doFade);
}
jsfiddle
This looks to me like a numeric precision problem - you can't expect the canvas to keep floating point pixel values around - but I'm not sure how to get around this.
I tried reading the image into a pattern, blanking the canvas, and then filling with the pattern at lower opacity in the hope that I could make rounding error work in my favor, but it seems to have the same result.
Short of reading out the image data and setting to black any pixels below a certain threshold, which would be prohibitively slow, I'm running out of ideas and could use some suggestions.
Thanks!
I thought I'd share my solution for the benefit of anyone else who might run into this problem. I was hoping to avoid doing any pixel-level manipulation, but beyond a certain threshold it's just not possible with the built-in canvas operations because the underlying bitmap is only 8 bits per channel and small fades will work out to less than one least significant bit and won't have any effect on the image data.
My solution was to create an array representing the age of each pixel. After each frame is drawn, I scan the imageData array, looking only at the alpha channel. If the alpha is 255 I know the pixel has just been written, so I set the age to 0 and set the alpha to 254. For any other non-zero alpha values, I increment the pixel age and then set the new alpha based on the pixel age.
The mapping of pixel age to alpha value is done with a lookup table that's populated when the fade rate is set. This lets me use whatever decay curve I want without extra math during the rendering loop.
The CPU utilization is a bit higher, but it's not too much of a performance hit and it can do smooth fades over several seconds and always fades entirely to black eventually.

Libgdx Animation using images from TextureAtlas gets incorrectly rotated

In my TextureAtlas the Sprite's for my Animation are rotated 90 degrees.
When I draw my Animation it's still rotaed by 90 degrees. How can I fix that?
My code looks like that:
TextureAtlas spritesheet = new TextureAtlas(Gdx.files.internal("images/spritesheet/spritesheet.atlas"));
Array<AtlasRegion> CLOUD_ANIMATION_REGIONS = spritesheet.findRegions("cloud_animation");
Animation animation = new Animation(0.1f,ImageProvider.CLOUD_ANIMATION_REGIONS);
In the render method:
batch.draw(animation.getKeyFrame(elapsedTime, true), x, y);
The animation works perfectly fien but it's rotated by 90 degree like in the spritesheet.
I realize that if I have a Sprite I can call Sprite.draw(batch) and it will fix the rotation but I don't seem to be able to use that mechanism for Animation's?
EDIT:
Like Alexander said, this will do the trick:
batch.draw(textureRegion, x, y, 0, 0,textureRegion.getRegionWidth(), textureRegion.getRegionHeight(), 1, 1, 90);
Ok, here is untested code:
TextureRegion textureRegion = animation.getKeyFrame(elapsedTime, true);
if (textureRegion instanceof TextureAtlas.AtlasRegion && ((TextureAtlas.AtlasRegion) textureRegion).rotate)
{
batch.draw(textureRegion, x, y, 0, 0, textureRegion.getRegionWidth(), textureRegion.getRegionHeight(), 1, 1, 90, true);
}
else
{
batch.draw(textureRegion, x, y);
}
What I'm doing here: I check if atlas packer marked the region as rotated and then draw it rotated 90 angle clockwise to compensate original 90 angle counter-clockwise rotation. See AtlasRegion's javadoc and special version of draw method that can rotate TextureRegion.
EDITED: fix arguments based on Markus comment
Somehow you should be using AtlasSprite I think. That carries out the unrotate in its constructor. You dont want to be rotating every frame - thats some overhead. Also the AtlasSprite should take care of trimmed regions in the atlas : something thats very important to maximise a single atlas texture. Alas it doesnt seem very easy to use as it seems one needs a seperate sprite for each frame which seems massive overhead.

Why does the Processing `fill()` with alpha never completely fill?

Let's say we have the following code:
void setup() {
background(0);
size(200, 200);
fill(255);
rect(75, 75, 50, 50);
}
void draw() {
fill(0, 2);
rect(0, 0, width, height);
}
Even after waiting 'forever,' the white 50x50 rectangle is still visible, albeit faded. Why doesn't the fill(0, 2) eventually cover this up?
I suppose this question is twofold:
Why doesn't it eventually fade to black, as in why does drawing another dark rectangle on top of the white one not erase it eventually (I'm thinking along the lines of putting tinted windows over each other; eventually even the brightest light won't shine through), and
Why doesn't it eventually fade to black, as in why is this the behavior intended by the Processing community?
Here's a post explaining what's going on: http://processing.org/discourse/beta/num_1138703939.html
Basically, the problem is that Processing stores colors as ints, but takes float arguments. When combining colors, Processing rounds the floats to ints. In your case, your color is getting stuck at a value of 63, 63, 63 because at that point the blending is too slight to make a difference that is detectable after rounding.
The solution is to do the fading from the source, not by overlaying an alpha color over top.
I had the same issue with fill(0, 0, 0, 5);.
Interestingly, changing the alpha value to 20 helped (and I'm sure many other values work too).
void draw() {
fill(0, 0, 0, 20); // Note the value 20 for the alpha channel.
rect(0, 0, width, height);
}
The default background color is darker than the color you assigned to the first rectangle, thus it gets black sooner.
Why doesn't it eventually fade to black, as in why does drawing another dark rectangle on top of the white one not erase it eventually
(I'm thinking along the lines of putting tinted windows over each
other; eventually even the brightest light won't shine through), and
Why doesn't it eventually fade to black, as in why is this the behavior intended by the Processing community?
Also, in your original code (not the above sample) you're probably drawing the white rectangle continuously, so it will never fade.

glDrawPixels() with 0.375 translation

I've noticed some strange behaviour with glDrawPixels() when using a 0.375 translation. This is my GL initialization:
width = 640; height = 480;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity( );
glOrtho(0, width, height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity( );
glTranslatef(0.375, 0.375, 0.0);
Now I want to draw a 640x30 pixel buffer to the very last 30 rows of my GL window. Hence, I do the following:
glRasterPos2i(0, 480);
glDrawPixels(640, 30, GL_RGBA, GL_UNSIGNED_BYTE, pixelbuffer);
Unfortunately, nothing gets drawn using this code. glGetError() also returns 0. The interesting thing is that as soon as I remove the call to glTranslatef(0.375, 0.375, 0.0) everything works fine!
So could somebody explain to me why this 0.375 translation on both axes confuses glDrawPixels()? Is this somehow rounded to 1.0 internally making my call to glDrawPixels() suddenly want to draw beyond the context's boundaries and thus it gets clipped by OpenGL? This is the only explanation I can think of but I don't understand why OpenGL should round a 0.375 translation to 1.0... it should be rounded down to 0.0 instead, shouldn't it?
The point (0,480) actually straddles one of your clipping planes given your projection matrix. Your sub-pixel shift hack pushes the point beyond the breaking point and the raster position is clipped. In GL, glRasterPos (...) will invalidate all following raster operations as long as the initial position is clipped (which in this case, it is).
You could try glRasterPos2i (0, 479). This is altogether more meaningful given the dimensions of your window anyway. You could also drop the whole charade and use glWindowPos2i (...) instead of relying on your projection and modelview matrices to position the raster coordinate in window-space.
I can't answer your question on why glTranslatef stops glDrawPixels from working, but I can tell you that isn't the way to select where to draw. Check the man page for glDrawPixels for a bit more info. It will tell you about glRasterPos and glWindowPos

Resources