alexaschor / JuliaShapeModulus

Alexa Schor and Theodore Kim, "A Shape Modulus for Fractal Geometry Generation". SGP 2023.
https://doi.org/10.1111/cgf.14905
3 stars 3 forks source link

Some questions about reproducing the results of the paper #2

Open BigRayLee opened 11 hours ago

BigRayLee commented 11 hours ago

Hi, Thanks for your interesting work and code sharing. After reading your paper, I am trying to generate some models by using your code. In order to have more control over the generated geometry I have a few questions.

  1. How to choose the root number, maximum power, minimum power and the root value. I did the following test based on the armadillo model, for the first three items I increased their values ​​but I didn't see much change in the resulting shape. Maybe I'm going in the wrong direction.

Regarding to the root value, I saw some difference based on different root value.

First I generated the polynomial by this command: 
`./bin/genPoly GEN 10 2 13 ./data/p4d/armadillo.p4d SDF ./data/fields/armadillo100.f3d 0.01`

And then I used this command to generate the final result. ./bin/prun ./data/fields/armadillo100.f3d ./data/p4d/armadillo.p4d 200 4.0 0.05 0.0 0.0 0.0 ./data/armadillo_frac.obj armadillo

Then I scaled down the value of root by using ./bin/genPoly SCALE ./data/p4d/armadillo.p4d 0.1 ./data/p4d/armadillo_scale_01.p4d And generated the model by using armadillo_scale_01.p4d. This is the result: armadillo_scale_01

Meanwhile, I also have some questions about different polynomial generation methods, especially for OBJBOX and SDF. Do they produce different results based on different methods? I remember a line from your paper: For most shapes, laying down polynomial roots randomly inside a bounding box of the target shape produced an acceptable d(x).

  1. What the effect of maxIteration and escape of QuaternionJuliaSet, I tried to increase the value of maxIteration, the generated model has a small change.

  2. Regarding to the execution of prun. I found that there are some artifacts in generated model by using prun. The problem might relate to mesh_cat. The result generated by using /bin/run ./data/fields/chair100.f3d RANDOM 200 4.0 1.0 0.0 0.0 0.0 ./data/chair_frac.obj chair_fracts_single_thread The result generated by using /bin/prun ./data/fields/chair100.f3d RANDOM 200 4.0 1.0 0.0 0.0 0.0 ./data/chair_frac.obj chair_fracts_mesh_concat

  3. Finally, I would like to know how the visual effect in “Figure 11” in your paper was achieved. What kind of configuration did you use?

alexaschor commented 8 hours ago

Hi @BigRayLee,

Thank you so much for your questions, I'm so glad people are trying out the code! I'll do my best to answer your questions here, and please let me know if these answer your questions or feel free to ask further if not.


1. How to choose the root number, maximum power, minimum power and the root value?

This is a great question, and this is something we spent a decent amount of time investigating as well. The output of the quaternion polynomial is normalized before being used as the versor field, so what we're looking at here is how the root positions affect the overall patterns in the unit vector field $\mathbf d(\mathbf x) = {\mathbf P(\mathbf x)}{(||\mathbf P(\mathbf x)||)^{-1}}$.

The short answer to both this and your second question about the SDF and OBJBOX is that we don't have a full accounting of how to design quaternion polynomial versor functions -- this could be a good area for future work if there's enough interest in the aesthetic that quaternion polynomials produce.

Note: In theory you could use any gradient noise function for this, we just used quaternion polynomials to reproduce the style of the classic quadratic quaternion Julia sets. In our 2024 follow-up paper (code here), we chose to use a Perlin gradient noise instead of the quaternion polynomials -- if you're looking for greater control over the style of surface detail, it may be worth considering trying an alternative noise function. That said, the quaternion polynomials do produce some very pretty patterns that are more or less unique to them; consider for instance the rosette-like feature on the tail of the dragon in the 2023 paper.

That said, I think I do have some insight into how these tend to work. The dynamics of these functions are a bit counter-intuitive, at least for me -- moving or adding roots can have relatively nonlocal effects on the output, for instance. However, after playing around with these functions for a while I have observed a few general properties that can help in designing fractals.

The dynamics with respect to root positions are also very similar to those of complex polynomials. I have a separate repo with a 2D implementation of this algorithm on the complex numbers, which does allow for direct visualization of the direction field. I've made a short video showing some of the properties I've listed below for the 2D case.

P1: Far away from the roots, the function tends to exhibit a radial ring- or star-like pattern. You can see this at the beginning of the video, where I generate several random polynomials with roots clustered around the center and you can see that the pattern far away from them is pretty consistent. You can also see this in Figure 9 in the original paper, and in Figure 11 and the bunny in Figure 1 -- I believe all of those are using polynomials with the roots clumped up inside the object.

I believe that this explains why scaling down your polynomial produced those step-like features along the armadillo's body -- as the roots cluster towards the origin, the noise pattern becomes simpler and more periodic further away from them.

P2: Near the roots, things are much more chaotic-looking. You can see this when I zoom into the field in the video, and in the armadillo in Figure 1, for instance, which I believe uses a polynomial with roots more evenly spread nearer to the surface.

P3: Using more roots or using higher-power roots tend to create "stronger", more dynamic structures within root clusters. I didn't demonstrate this in the video, but I have two screenshots below from the 2D case, with root power=5 on the left and root power=15 on the right.


2. Meanwhile, I also have some questions about different polynomial generation methods, especially for OBJBOX and SDF. Do they produce different results based on different methods? I remember a line from your paper: For most shapes, laying down polynomial roots randomly inside a bounding box of the target shape produced an acceptable d(x).

The difference between these two methods are that OBJBOX will lay the roots randomly within the target shape's bounding box, while SDF will specifically place them within a user-supplied berth of the object's surface. In practice, and particularly with lower root counts, neither of these produces a consistent style - it's still worth generating a few and seeing which you like best. The main difference is if you want to avoid the ring or star-like features like you see in Figures 9 and 11 in the original paper, I believe that using the SDF mode with a larger number of roots tends to give the surface a more evenly chaotic pattern like in the Figure 1 armadillo and dragon.


3. What is the effect of maxIteration and escape of QuaternionJuliaSet? I tried to increase the value of maxIteration, the generated model has a small change.

In terms of what this literally changes, this determines how many times the code will iterate the map onto the starting points in order to determine if they are members of the set (maxIterations) and how far away those points need to get from the origin in order to be considered not part of the set. In practice, these don't tend to have a huge effect on the overall output shape, at least at a macro level. With every increase in maxIterations, higher and higher-frequency detail will appear on the surface: at $i=1$ you'll get a very smooth surface, and with increased iterations it will quickly become very detailed.

A good example of this is to compare two of the videos from my coauthor Dr. Theodore Kim's 2015 paper: Quaternion Julia Set Shape Optimization. On this page, see the difference between the "Optimization sequence, single iteration" video and the "Optimization sequence, multiple iterations" video.

However, the difference between $i=1$ and $i=3$ is usually visually much more significant than from $i=3$ to e.g. $i=10$. If you start zooming in really far or meshing at a very high resolution, higher iterations might be necessary, but it definitely is a diminishing return.

For the escape value, this makes even less a difference. If too small a value is used, some points that are "really" inside the set could be considered outside, but with the dynamics that the shape modulus function produces, the sphere of that radius would have to be quite close to the target surface (or appropriate isosurface of the target SDF given your $\beta$ parameter) for this to happen. In theory you can't have too large a value for escape, but using a tighter escape radius could let you squeeze more detail out of a given iteration count (or conversely, if your escape radius is too large, an escaping iterate might not be able to reach it within maxIterations iterations).


4. Regarding to the execution of prun. I found that there are some artifacts in generated model by using prun.

Ah, yes -- I've encountered this too, I think I even made that mistake myself in one of my examples for issue #1. This is what happens when you pass in RANDOM as the polynomial argument to prun. Each of the parallel jobs will pick a different random polynomial. This can be bypassed by generating a polynomial with genPoly and then passing it into prun. I really should adjust the prun script to warn the user when doing this (or better yet, adjust things to allow e.g. a specified seed to make them use the same polynomial).


5. Finally, I would like to know how the visual effect in “Figure 11” in your paper was achieved. What kind of configuration did you use?

Oh good question -- I forgot I hadn't included the previs program in the code release. The way that I generated those figures was to lightly modify trimesh2's mesh_view utility to compute vertex colors according to a quaternion polynomial. I've uploaded the code here: https://gist.github.com/alexaschor/cbd5fc09368de94bcdde0962aab65d46. I think most (all?) the modification that I did is adding and calling the recomputeVertexColors() method.