1

Well, I'm continuing this question without answer (Smoothing random noises with different amplitudes) and I have another question.

I have opted to use the contour/shadow of a shape (Translating/transforming? list of points from its center with an offset/distance).

This contour/shadow is bigger than the current path. I used this repository (https://github.com/n-yoda/unity-vertex-effects) to recreate the shadow. And this works pretty well, except for one fact.

To know the height of all points (obtained by this shadow algorithm (Line 13 of ModifiedShadow.cs & Line 69 of CircleOutline.cs)) I get the distance of the current point to the center and I divide between the maximum distance to the center:

float dist = orig.Max(v => (v - Center).magnitude);
foreach Point in poly --> float d = 1f - (Center - p).magnitude / dist;

Where orig is the entire list of points obtained by the shadow algorithm. D is the height of the shadow.

But the problem is obvious I get a perfect circle:

...

In red and black to see the contrast:

...

And this is not what I want:

...

As you can see this not a perfect gradient. Let's explain what's happening.

I use this library to generate noises: https://github.com/Auburns/FastNoise_CSharp

Note: If you want to know what I use to get noises with different amplitude: Smoothing random noises with different amplitudes (see first block of code), to see this in action, see this repo

...

  • Green background color represent noises with a mean height of -0.25 and an amplitude of 0.3
  • White background color represent noises with a mean height of 0 and an amplitude of 0.1
  • Red means 1 (total interpolation for noises corresponding to white pixels)
  • Black means 0 (total interpolation for noises corresponding to green pixels)

That's why we have this output:

...

Actually, I have tried comparing distances of each individual point to the center, but this output a weird and unexpected result.

Actually, I don't know what to try...

z3nth10n
  • 2,341
  • 2
  • 25
  • 49
  • Also, can you post a mock-up of the result you need? – Ruzihm Nov 27 '18 at 22:20
  • I had an earlier comment with some questions I figured out while writing my answer. Your question is a little unclear and would greatly benefit from having a question with a question mark somewhere in it so the exact purpose of this question could be answered specifically. The question I answered was: Why doesn't this lerp completely from/to the inner and outer levels? – Ruzihm Nov 27 '18 at 23:06

1 Answers1

2

The problem is that the lerp percentage (e.g., from high/low or "red" to "black" in your visualization) is only a function of the point's distance from the center, which is divided by a constant (which happens to be the maximum distance of any point from the center). That's why it appears circular.

For instance, the centermost point on the left side of the polygon might be 300 pixels away from the center, while the centermost point on the right might be 5 pixels. Both need to be red, but basing it off of 0 distance from center = red won't have either be red, and basing it off the min distance from center = red will only have red on the right side.

The relevant minimum and maximum distances will change depending on where the point is

One alternative method is for each point: find the closest white pixel, and find the closest green pixel, (or, the closest shadow pixel that is adjacent to green/white, such as here). Then, choose your redness depending on how the distances compare between those two points and the current point.

Therefore, you could do this (pseudo-C#):

foreach pixel p in shadow_region {

    // technically, closest shadow pixel which is adjacent to x Pixel: 
    float closestGreen_distance = +inf;
    float closestWhite_distance = +inf;

    // Possibly: find all shadow-adjacent pixels prior to the outer loop 
    // and cache them. Then, you only have to loop through those pixels.
    foreach pixel p2 in shadow {
        float p2Dist = (p-p2).magnitude;

        if (p2 is adjacent to green) {
           if (p2Dist < closestGreen_distance) {
               closestGreen_distance = p2Dist;
           }
        }

        if (p2 is adjacent to white) {
           if (p2Dist < closestWhite_distance) {
               closestWhite_distance = p2Dist;
           }
        }
    }

    float d = 1f - closestWhite_distance / (closestWhite_distance + closestGreen_distance)
}

Using the code you've posted in the comments, this might look like:

foreach (Point p in value)
{
    float minOuterDistance = outerPoints.Min(p2 => (p - p2).magnitude);
    float minInnerDistance = innerPoints.Min(p2 => (p - p2).magnitude);

    float d = 1f - minInnerDistance / (minInnerDistance + minOuterDistance);

    Color32? colorValue = func?.Invoke(p.x, p.y, d);

    if (colorValue.HasValue)
        target[F.P(p.x, p.y, width, height)] = colorValue.Value;
}

The above part was chosen for the solution. The below part, mentioned as another option, turned out to be unnecessary.


If you can't determine if a shadow pixel is adjacent to white/green, here's an alternative that only requires the calculation of the normals of each vertex in your pink (original) outline.

Create outer "yellow" vertices by going to each pink vertex and following its normal outward. Create inner "blue" vertices by going to each pink vertex and following its normal inward.

Then, when looping through each pixel in the shadow, loop through the yellow vertices to get your "closest to green" and through the blue to get "closest to white".

The problem is that since your shapes aren't fully convex, these projected blue and yellow outlines might be inside-out in some places, so you would need to deal with that somehow. I'm having trouble determining an exact method of dealing with that but here's what I have so far:

One step is to ignore any blues/yellows that have outward-normals that point towards the current shadow pixel.

However, if the current pixel is inside of a point where the yellow/blue shape is inside out, I'm not sure how to proceed. There might be something to ignoring blue/yellow vertexes that are closer to the closest pink vertex than they should be.

extremely rough pseudocode:

list yellow_vertex_list = new list 
list blue_vertex_list = new list 
foreach pink vertex p:
    given float dist;
    vertex yellowvertex = new vertex(p+normal*dist)
    vertex bluevertex = new vertex(p-normal*dist)

    yellow_vertex_list.add(yellowvertex)
    blue_vertex_list.add(bluevertex)

create shadow

for each pixel p in shadow:
    foreach vertex v in blue_vertex_list
        if v.normal points towards v: break;
        if v is the wrong side of inside-out region: break;
        if v is closest so far:
            closest_blue = v
            closest_blue_dist = (v-p).magnitude

    foreach vertex v in yellow_vertex_list
        if v.normal points towards v break;
        if v is the wrong side of inside-out region: break;
        if v is closest so far:
            closest_yellow = v
            closest_yellow_dist = (v-p).magnitude


    float d = 1f - closest_blue_dist / (closest_blue_dist + closest_yellow_dist)
Ruzihm
  • 19,749
  • 5
  • 36
  • 48
  • Ok, I'm trying to get inner & outter adjacent pixels by using scale method that I made in the other question (that you answered), but this is the result: https://i.imgur.com/UdFR0t1.png... But tell me one thing, when you say pixel is adjacent to white or green pixel is the equivalent to blue and yellow pixels that are represented in the texture I posted? – z3nth10n Nov 28 '18 at 09:47
  • Well, yes, that was an approximation, because I don't have any optimized way of get adjacent pixels to both pixels (by the moment). I'm working on it. – z3nth10n Nov 28 '18 at 14:52
  • The problem is that using the scale method projects from the center of the object, through the vertex, which won't produce the shape you need. Can you get the normals of each vertex in the pink outline? If so, you could project from each vertex outwards/inwards along that normal to get the yellow/blue vertices you need. But... since your shape is concave in many places, you have to account for spots where the blue/yellow turn inside out. When looping thru: only look for blue/yellow verts with normals pointing away from the current shadow point. I will edit my answer to include that – Ruzihm Nov 28 '18 at 17:24
  • Also, what I could check is if any of the 8 neighbors pixels is outofbounds (or checking HashSet.Contains) if yes, this is an adjacent pixel, to check if it's inner or outer I could check the color (if it's white this pixel is an inner adjacent pixel and if it is green this pixel is an outer adjacent pixel). – z3nth10n Nov 28 '18 at 17:34
  • That's probably easier. I realized after writing that comment that there's more to dealing with the inside-out projections than I originally thought. I'll still edit my answer to include as far as I got, just in case checking the 8 neighbors doesn't work out – Ruzihm Nov 28 '18 at 17:39
  • Ok, I have implemented everything, but there is an issue... Maybe you forgot something or I'm misleading something... Because if you do: `closestWhite_distance / (closestWhite_distance + closestGreen_distance)` you will get a constant value: https://i.imgur.com/ro7Z6Ro.png and doing: `1f - (minInnerDistance + (p - Center).magnitude) / (minInnerDistance + minOuterDistance)` returns this: https://i.imgur.com/sYAbpM1.png (it's bounded inside the shape, but this is a circle (not an oval that is what I want), not what I was expecting, maybe I should edit my answer explaining better what I need) – z3nth10n Nov 28 '18 at 19:53
  • oh yea sorry, it should be `(p-p2).magnitude`, not `(Center-p2).magnitude`. I updated the answer to use the correct variable lol. Try that. – Ruzihm Nov 28 '18 at 19:58
  • Sorry, I have one more question... What means shadow in the second foreach loop? Is that all the adjacents points on the shadow (blue and yellow pixels)? In that case, mininum distance will be always zero, I'm right? – z3nth10n Nov 28 '18 at 20:02
  • it's the same collection that you're getting `p` from. If and only if `p` is a yellow pixel, then `closestWhite_distance` will be `0` for that `p`. If and only if `p` is a blue pixel, then `closestGreen_distance` will be `0` for that `p`. If `p` is any other pixel, then neither `closestWhite_distance` nor `closestGreen_distance` will be `0`. – Ruzihm Nov 28 '18 at 20:05
  • Waching the second pseudo-code is clearer to me. I will try to implement. Thanks! – z3nth10n Nov 28 '18 at 20:05
  • The 2nd has some potential edge cases that might be hard to work out. You can try the 2nd but the 1st is a lot more rigorous. The first should produce an oval if you use `float p2Dist = (p-p2).magnitude;` and `float d = 1f - closestWhite_distance / (closestWhite_distance + closestGreen_distance)` It was a solid red before because I had the wrong code for `p2Dist`. – Ruzihm Nov 28 '18 at 20:09
  • 1
    Could you complete the pseudo-code from the first block (a little bit more), please?, this what I have: https://pastebin.com/f1zBHAkq and this is what this outputs: https://i.imgur.com/xdtvarI.png, the problem here is that it's causing NaN values as output. I don't fully understand what I have to assign here `closestWhite_distance;` exactly. Also, I thought that by adjacent you meant contained on the inner/outer point list, but I think I'm wrong. – z3nth10n Nov 28 '18 at 20:23
  • 1
    Also, I tried this: `float minOuterDistance = outerPoints.Min(p => (Center - p).magnitude), minInnerDistance = innerPoints.Min(p => (Center - p).magnitude);`, without luck... – z3nth10n Nov 28 '18 at 20:24
  • @z3nth10n I added an implementation based off your code. Let me know what that outputs so I can fix any errors in my use of your code. – Ruzihm Nov 28 '18 at 20:33
  • Ok, that was what I tried before, and I get a constant value. See [this comment](https://stackoverflow.com/questions/53505973/smoothing-noises-with-different-amplitudes-part-2#comment93923032_53509294). Thanks ^^ – z3nth10n Nov 28 '18 at 20:39
  • Can you link a pastebin of the code that produces that? I'm immensely surprised `xPoints.Min(p2 => (p - p2).magnitude);` gets you a solid color. – Ruzihm Nov 28 '18 at 20:41
  • 1
    Thanks!!!!! That was my error, because I was comparing to center instead of p2. Now this is what this returns: https://i.imgur.com/9NJ2QiE.png. I would pay you a beer but I think this is a little bit impossible hahaha (I would like to keep in contact with you, this is my Discord: @z3nth10n#0775) – z3nth10n Nov 28 '18 at 20:45
  • 1
    I can't edit my own post, this is the result with Noises: https://i.imgur.com/AAKFncB.png (I think this is very fine). I'm happy :P Now it's optimization time :P – z3nth10n Nov 28 '18 at 20:57