We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hi everyone,
I'm desperately and unsuccessfully trying to compute Delaunay triangulation from a 3D point cloud. It's a thing that many people have been trying to tackle for years (the same question has been asked over a dozen times on this forum: here, here, also here, here again...) but that has never found a proper solution.
To the best of my knowledge, there are 4 libraries capable of computing Delaunay triangulation:
However:
Voronoi
& DelaunayTriangulation
classes from Toxiclibs do not support 3D It seems therefore the only solution lies in the Hemesh library.
GOAL
I would like to transform the colors of a painting into a 3D mesh via Delaunay triangulation. It's a relatively widespread technique that media artist Quayola (among others) has been brilliantly using for years.
PROBLEMS (hard to explain in english)
triangulation.getAlphaTriangles(threshold)
) to limit the amount of surrounding points to connect to, but that very threshold prevents from connecting to neighboring points that are far.As a result, this painting from John William Waterhouse:
(some dude getting hit on at a pool party)
is tranformed to this:
(all the shapes are "closed" (P3 connected to P1) but it shouldn't be that way)
With a higher threshold:
(Between two layers of polyhedrons)
QUESTIONS
Important parts of the code (it's in Python but PLEASE DO NOT MOVE THIS QUESTION TO PYTHON MODE):
setup()
threshold = 35
render = WB_Render(this)
for p in points:
new_points = WB_Point(p.x, p.y, p.z)
list.append(new_points)
triangulation = WB_Triangulate.alphaTriangulate3D(list)
triangles = triangulation.getAlphaTriangles(threshold)
draw()
noStroke()
for i in range(0, len(triangles), 3):
render.drawTriangle(liste[triangles[i]], liste[triangles[i+1]], liste[triangles[i+2]])
Thank you
Answers
What do you mean exactly? With a higher threshold you reached your goal already, that "connecting to neighboring points that are far" - thing.
What don't you like about this result?
Oh man that's really hard to explain...
1/ On the example pictures from Quayola's work you can see triangles of various sizes (large ones and small ones) whereas on my sketch you can clearly see that the triangles are about the same size.
2/ On the example pictures, every point is connected to its neighbours. On my sketch they are not ! P3 is connected to P1 instead of being connected to P4. That's why in the last picture you can see 2 layers of polyhedrons. There shouldn't be any polyhedrons in the first place, just a 3D terrain.
In other words, there are polyhedrons because Hemesh automatically closes the shapes (P3 with P1). With Toxiclibs a volume is closed only when specified (
volume.closeSides()
). I don't know if it's possible to keep a volume "open" with Hemesh.you could have an dynamic threshold in that it varies randomly or for different areas on the screen
yould also make it so that the connetion is done where the same colors are involved
Where does the 3d come into this? You're starting with a 2d image, you're finishing with a 2d image (the last one you posted). I don't get where the 3rd dimension comes from.
Can you not do a 2d transformation and then randomly / noisily offset the points in z to give you a terrain? (Like the last but one picture you posted)
@koogs Indeed I didn't explain the process, sorry. After running a Sobel operator (for edge detection) x and y are placed to the edges. z is the brightness of the pixel. The last picture I posted is actually in 3d, it shows the cavity formed inside the painting because of the missing connections between P3s and P4s.
@solub,
Am I right in understanding that you want a perfect 2D division of a plane from a particular perspective, and then you want to height-map the points of that surface? This approach that is sometimes called 2.5D
If so, one approach:
@jeremydouglass I realize my explanation was incomplete and somewhat inconsistent. No, I'm not looking for a pseudo-3D approach. The sketch is in true 3D with a dynamic camera in order to navigate through the significant points of a painting (projected on a z axis) or around the terrain generated from those points. If I recall correctly Nicolas Clavaud's library (Triangulate) doesn't work in full 3D and I also should mention that it is unable to read array lists in Python mode.
I have contacted Frederik Vanhoutte (W-Blut) on github regarding this 3d triangulation issue that I have, not sure he'll respond but be assured I'll post his answer here if he ever does.
In the meantime I'm considering alternative approaches, notably the Poisson surface reconstruction method. Maybe you have any info on that ?
@solub -- I think you may be misunderstanding my point -- whether your call it 2.5D or not, it still uses real 3D coordinates in a real OpenGL P3D view with a real dynamic camera etc -- it is a height-map projection of a 2D mesh, but every point has an xyz.
Triangulate (or any equivalent code, such as the do-it-yourself code that appears in that thread) does not need to work in full 3D -- you instead run it on a 2D triangulation generated by Mesh in order to recover the 2D triangles. You now have your x+y dimensions for a true 3D surface. You then use point sampling to generate your z heights. You now have an x, y, and z for each point, and you know how the points go together to form facets, so you can render them using vertex or PShape et cetera.
I'm not able to help you there, but the algorithm for retrieving the triangles is I believe in that thread / in the Triangulate source, so perhaps you could implement that in Python if you wish.
If you're sure it's actually a Java's ArrayList issue:
https://Docs.Oracle.com/javase/10/docs/api/java/util/ArrayList.html
You may import it via:
from java.util import ArrayList
So you have the real deal, rather than depending on Jython's automatic List/ArrayList conversion. :)>-
@GoToLoop great tips, I didn't know it was possible to play with real Java's ArrayList in Python mode. I just got the library working ! @jeremydouglass I was wrong, Triangulate library DO work with 3D points. Thank you for the explanations.
Thank you both of you ! It's such a relief to finally see a script working...
@solub -- very glad to hear you got it working!
If you have a very simple example of the working approach you ended up taking to 3D Delaunay triangulation of an image in Processing.py, it might be nice of you to share an example here for future forum users.
@jeremydouglass I was about to post a 3D example sketch yesterday but I ran into an issue. I'll post the final script as soon as I manage to solve the problem.
To run the script you'll need Peasycam and Triangulate libraries (download) + the sobel fragment shader below from the PostFx library (only needed for edge detection). I first had my own sobel operator to detect the edges but found that @cansik's shader implementation was much more accurate and efficient.
Also please note that this sketch is in Python.
sobelFrag.glsl
sketch.pyde
Cheers !
Really beautiful work!
Thank you Jeremy, I'm really glad to hear you like it.
I just updated the script to add points to the edges of the picture.
I have an issue involving the script above so I hope you won't mind me asking a question on this thread again.
I'm trying to add a bloom effect (from the PostFX library...again) to the sketch but for some reason I have 2 canvas being displayed on the screen:
one at the right location (center) but without the bloom effect
the other one, smaller, at the bottom right corner with the bloom effect
Do you guys have an idea what I'm doing wrong here ?
So, I tried to draw the vertices on a canvas and then applying a bloom effect on this canvas but i still get strange artifacts.
in setup
in draw
Maybe @cansik have an idea what could possibly be wrong ?
@solub Can you show me / tell me what "strange artefacts" are?
The problem with your python sketch is, that you are translating the coordinate system, but are not using the matrix stack. So the image (output of the post fx) will be drawn at the wrong location.
Hi @cansik, thank you for your answer.
I first though it was because of the missing
pushMatrix
/popMatrix
as well but pushing the transformation matrix doesn't solve the problem.When drawing on a canvas:
sketch.pyde
@solub Please show me the code, where you applied the bloom effect, with push and pop of the matrix stack.
And by the way, if you are using peasycam, you have to do the post processing in the HUD state (beginHUD, endHUD).
It maybe makes sense to look at the examples of the libraries first. There is an example with the peasycam in the postfx library, and there is an offscreen rendering example in the peasycam library.
Looking at them should solve all your problems.
I'm not sure to understand, the code is right above your comment. Maybe you're refering to the first script (which is basically the same but without an off-screen graphics buffer)
It did solve the problem, thank you very much cansik. Also let me thank you once again for all the work you've put in the PostFX library.