HTML5 Canvas Creative Alpha-Blending - javascript

So I have an animation that I'm coding in javascript and HTML5 (no libraries, no plugins, no nothing and I'd like it to stay that way). The animation uses physics (basically a bunch of unusual springs attached to masses) to simulate a simple liquid. The output from this part of the program is a grid (2d-array) of objects, each with a z value. This works quite nicely. My problem arises when drawing the data to an HTML5 Canvas.
That's what it looks like. Trust me, it's better when animated.
For each data point, the program draws one circle with a color determined by the z value. Just drawing these points, however, the grid pattern is painfully obvious and it is difficult to see the fluid that it represents. To solve this, I made the circles larger and more transparent so that they overlapped each other and the colors blended, creating a simple convolution blur. The result was both fast and beautiful, but for one small flaw:
As the circles are drawn in order, their color values don't stack equally, and so later-drawn circles obscure the earlier-drawn ones. Mathematically, the renderer is taking repeated weighted averages of the color-values of the circles. This works fine for two circles, giving each a value of 0.5*alpha_n, but for three circles, the renderer takes the average of the newest circle with the average of the other two, giving the newest circle a value of 0.5*alpha_n, but the earlier circles each a value of 0.25*alpha_n. As more circles overlap, the process continues, creating a bias toward newer circles and against older ones. What I want, instead, is for each of three or more circles to get a value of 0.33*alpha_n, so that earlier circles are not obscured.
Here's an image of alpha-blending in action. Notice that the later blue circle obscures earlier drawn red and green ones:
Here's what the problem looks like in action. Notice the different appearance of the left side of the lump.
To solve this problem, I've tried various methods:
Using different canvas "blend-modes". "Multiply" (as seen in the above image) did the trick, but created unfortunate color distortions.
Lumping together drawing calls. Instead of making each circle a separate canvas path, I tried lumping them all together into one. Unfortunately, this is incompatible with having separate fill colors and, what's more, the path did not blend with itself at all, creating a matte, monotone silhouette.
Interlacing drawing-order. Instead of drawing the circles in 0 to n order, I tried drawing first the evens and then the odds. This only partially solved the problem, and created an unsightly layering pattern in which the odds appeared to float above the evens.
Building my own blend mode using putImageData. I tried creating a manual pixel-shader to suit my needs using javascript, but, as expected, it was far too slow.
At this point, I'm a bit stuck. I'm looking for creative ways of solving or circumnavigating this problem, and I welcome your ideas. I'm not very interested in being told that it's impossible, because I can figure that out for myself. How would you elegantly draw a fluid from such data-points?

If you can decompose your circles into two groups (evens and odds), such that there is no overlap among circles within a group, the following sequence should give the desired effect:
Clear the background
Draw the evens with an alpha of 1.0 (opaque)
Draw the odds with an alpha of 1.0 (opaque)
Draw the evens with an alpha of 0.5
Places which are covered by neither evens nor odds will show the background. Those which are covered only by evens will show the evens at 100% opacity. Those covered by odds will show the odds with 100% opacity. Those covered by both will show a 50% blend.
There are other approaches one can use to try to blend three or more sets of objects, but doing it "precisely" is complicated. An alternative approach if one has three or more images that should be blended uniformly according to their alpha channel is to repeatedly draw all of the images while the global alpha decays from 1 to 0 (note that the aforementioned procedure actually does that, but it's numerically precise when there are only two images). Numerical rounding issues limit the precision of this technique, but even doing two or three passes may substantially reduce the severity of ordering-caused visual artifacts while using fewer steps than would be required for precise blending.
Incidentally, if the pattern of blending is fixed, it may be possible to speed up rendering enormously by drawing the evens and odds on separate canvases not as circles, but as opaque rectangles, and subtracting from the alpha channel of one of the canvases the contents of a a fixed "cookie-cutter" canvas or fill pattern. If one properly computes the contents of cookie-cutter canvases, this approach may be used for more than two sets of canvases. Determining the contents of the cookie-cutter canvases may be somewhat slow, but it only needs to be done once.

Well, thanks for all the help, guys. :) But, I understand, it was a weird question and hard to answer.
I'm putting this here in part so that it will provide a resource to future viewers. I'm still quite interested in other possible solutions, so I hope others will post answers if they have any ideas.
Anyway, I figured out a solution on my own: Before drawing the circles, I did a comb sort on them to put them in order by z-value, then drew them in reverse. The result was that the highest-valued objects (which should be closer to the viewer) were drawn last, and so were not obscured by other circles. The result is that the obscuring effect is still there, but it now happens in a way that makes sense with the geometry. Here is what the simulation looks like with this correction, notice that it is now symmetrical:

Related

Hidden and visible surface rendering in javascript by overlapping colours

I am trying to implement a hidden surface determination algorithm in my 3D renderer. I have found very good approaches, such as Z-Buffer or Warnock's algorithm. However, they are extremely resource-consuming. Thus I wondered, Why not use opaque overlapping colours, with which I could get the same visual results?. I would like to receive some feedback an opinions before going further, and in case it turns out to be a good solution, of course, to use this post as a way of sharing it with the community. The method would basically come down to: 1) Ordering all polygons in the scene by their Z coordinate 2)Rendering all of them in order, using opaque colours. The image/view/visual effect would be the same, without having to resort to a costly pixel-by-pixel computational process.
(Example: say I have two intersecting polygons (P1, P2). Given that the viewer's closest Z coordinate is 0, if P1z=10 and P2z=3, then the rendering order would be: P2>P1. When drawn, P2's colour will cover those P1's edges and colours placed in the 2D-XY-intersection between the two polygons ) What cons do you think this could have? Do you think that it would suffice the problem?
PS: I do not use polygonal meshes, the 3D-objects to be processed are simple convex and concave figures.
Polygons can intersect (even partially) and determine what part of one polygon is in front of the other it's a very expensive computation.

How to detect collision in not easily polygon divided body

Say we are coding something in Javascript and we have a body, say an apple, and want to detect collision of a rock being thrown at it: it's easy because we can simply consider the apple as a circle.
But how about we have, for example, a "very complex" fractal? Then there is no polygon similar to it and we also cannot break it into smaller polygons without a herculean amount of effort. Is there any way to detect perfect collision in this case, as opposed to making something that "kind" of works, like considering the fractal a polygon (not perfect because there will be collision detected even in blank spaces)?
You can use a physics editor
https://www.codeandweb.com/physicseditor
It'll work with most game engines. You'll have to figure how to make it work in JS.
Here's an tutorial from the site using typescript - related to JS
http://www.gamefromscratch.com/post/2014/11/27/Adventures-in-Phaser-with-TypeScript-Physics-using-P2-Physics-Engine.aspx
If you have coordinates of the polygons, you can make an intersection of subject and clip polygons using Javascript Clipper
The question doesn't provide too much information of the collision objects, but usually anything can be represented as polygon(s) to certain precision.
EDIT:
It should be fast enough for real time rendering (depending of complexity of polygons). If the polygons are complex (many self intersections and/or many points), there are many methods to speedup the intersection detection:
reduce the point count using ClipperLib.JS.Lighten(). It removes the points that have no effect to the outline (eg. duplicate points and points on edge)
get first bounding rectangles of polygons using ClipperLib.JS.BoundsOfPath() or ClipperLib.JS.BoundsOfPaths(). If bounding rectangles are not in collision, there is no need to make intersection operation. This function is very fast, because it just gets min/max of x and y.
If the polygons are static (ie their geometry/pointdata doesn't change during animation), you can lighten and get bounds of paths and add polygons to Clipper before animation starts. Then during each frame, you have to do only minimal effort to get the actual intersections.
EDIT2:
If you are worried about the framerate, you could consider using an experimental floating point (double) Clipper, which is 4.15x faster than IntPoint version and when big integers are needed in IntPoint version, the float version is 8.37x faster than IntPoint version. The final speed is actually a bit higher because IntPoint Clipper needs that coordinates are first scaled up (to integers) and then scaled down (to floats) and this scaling time is not taken into account in the above measurements. However float version is not fully tested and should be used with care in production environments.
The code of experimental float version: http://jsclipper.sourceforge.net/6.1.3.4b_fpoint/clipper_unminified_6.1.3.4b_fpoint.js
Demo: http://jsclipper.sourceforge.net/6.1.3.4b_fpoint/main_demo3.html
Playground: http://jsbin.com/sisefo/1/edit?html,javascript,output
EDIT3:
If you don't have polygon point coordinates of your objects and the objects are bitmaps (eg. png/canvas), you have to first trace the bitmaps eg. using Marching Squares algorithm. One implementation is at
https://github.com/sakri/MarchingSquaresJS.
There you get an array of outline points, but because the array consists of huge amount of unneeded points (eg. straight lines can easily be represented as start and end point), you can reduce the point count using eg. ClipperLib.JS.Lighten() or http://mourner.github.io/simplify-js/.
After these steps you have very light polygonal representations of your bitmap objects, which are fast to run through intersection algorithm.
You can create bitmaps that indicate the area occupied by your objects in pixels. If there is intersection between the bitmaps, then there is a collision.

Alternative HTML5 Canvas Clear Method

The answer to this stackoverflow question effectively says to retrace the steps taken to draw a rectangle:
fillRect
by doing:
clearRect
First of all when I read this answer, I thought to just draw a white box the entire width and height of the canvas.
After thinking a bit more about how the Canvas element can save, and restore and the fact it implements a clearRect, does this mean that simply drawing another large rectangle could eat resources, if doing something like redrawing an entire bar graph every 100ms.
Originally drawing a bar graph every 100ms drew bars on top of each other, thus not being able to see the new bars because they're being layered.
But now, drawing a white rectangle as well, means that many rectangles are getting drawn, layered and it seems that the Canvas element tracks these?
Does this mean that it is possible to effectively overload the element, or overload the browser?
the canvas does not track the drawing operations, but it tracks states. And the color values of individual pixels. If you draw a red rectangle you actually set the state to "draw red" and then set a rectangular shaped area of pixels to the currently drawn color (there are also different other drawing operations than just draw red on-top but I don't have experience with the so I can't tell you much other than that they exist)
For performance reasons you want to (among many other things) minimize
the amount of pixels you change
the amount of states you change
the difference between clearRect and width = width is that clearRect clears out the pixel data in the given area, while width = width clears out all pixel data and all states, like transformations, and styles. I think you already see the difference, there are a lot more things to consider (like Garbage Collection blocking your drawloop being one) but that would get a bit offtopic.
I'm not sure what you mean with overloading the browser. If you mean blocking and making the UI unresponsive then yes it's a thing that can and will happen since javascript is single-threaded and there are many ways you can accomplish it, but most likely not with such a reasonable operation :)
The worst thing you can do with drawing is make it super choppy on slow CPUs. Using requestAnimationFrame() instead of setTimeout()(which I assume you currently use because you mentioned 100ms) for your drawloop(s) is almost always a good and safe way to make sure your drawing will not block the UI.
the Canvas element tracks these [rectangles]
It doesn't track painting operations, as far as I know. (It's up to the implementation, but I don't know of any implementation that does.) You might be thinking of how it can save and restore things like transforms and paint colors.

Transparency Face-Jumping?

I've been working on a WebGL project that runs on top of the Three.js library. I am rendering several semi-transparent meshes, and I notice that depending on the angle you tilt the camera, a different object is on top.
To illustrate the problem, I made a quick demo using three semi-transparent cubes. When you rotate the image past perpendicular to the screen, the second half of the smallest cube "jumps" and is no longer visible. However, shouldn't it still be visible? I tried adjusting some of the blending equations, but that didn't seem to make a difference.
What I'm wondering is whether or not this is a bug in WebGL/Three, or something I can fix. Any insight would be much appreciated :)
Well, that's something they weren't able to solve when they invented all this hardware accelerated graphics business and sounds like we'll have to deal with this for a long while.
The issue here is that graphic cards do not sort the polygons, nor objects. The graphics card is "dumb", you tell it to draw an object and it will draw the pixels that represent it and also, in another non-visible "image" called zbuffer (or depthbuffer), will draw the pixels that represent the object but instead of color it will draw the distance to the camera for each pixels. Any other objects that you draw afterwards, the graphics card will check if the distance to the camera for each pixel, and if it's farther, it won't draw it (unless you disable the check, that is).
This speeds up things a lot and gives you nice intersections between solid objects. But it doesn't play well with transparency. Say that you have 2 transparent objects and you want A to be drawn behind B. You'll need to tell the graphics card to draw A first and then B. This works fine as long as they're not intersecting. In order to draw 2 transparent objects intersecting then the graphics would have to sort all the polygons, and as the graphics card doesn't do that, then you'll have to do it.
It's one of these things that you need to understand and specifically tweak for your case.
In three.js, if you set material.transparent = true we'll sort that object so it's drawn before (earlier) other objects that are in front. But we can't really help you if you want to intersect them.

How to determine Z-order when drawing in a one-point perspective?

I'm trying to make a simple javascript that draws rectangles on a canvas, then draws the side faces based on a one-point perspective. The problem is, the amount of rectangles and their positioning is arbitrary, so faces tend to overlap in a way that wouldn't happen in true 3D perspective. How can I determine the correct drawing order so that this won't happen?
Here are some screenshots to illustrate what I mean:
Screenshot 1 - The wrong way. In this one, the grouping of red, green, and blue blocks is being drawn in the reverse order of how they should be drawn.
Screenshot 2 - The right way. This is the way that it should be drawn.
What you are looking for is called the Painter's Algorithm that is as long as you don't have any intersecting polygons
Since your screenshots don't work, I'm going to take a wild guess and assume that the problem your having is that drawing rectangles from back to front yields weird overlaps.
One approach to fixing this would be to use simple binary space partitioning. Essentially, expand every front facing rectangle to an infinite plane. Then, split all of the side rectangles where those planes intersect. Then, drawing from front to back should not product overlaps anymore, since the side rectangles will be split wherever any overlap problems would have occurred.
Edit: Ah, now that your screenshots work I don't think that's your problem. Ah well, I'll leave the answer anyways.

Categories