Search Unity

Question Random samples in geometry shader not generating appropriately

Discussion in 'Shaders' started by iamvideep, Jan 7, 2021.

  1. iamvideep

    iamvideep

    Joined:
    Oct 27, 2017
    Posts:
    118
    I am trying to generate quad geometry at random points in a triangle using a geometry shader. I am able to generate a few but the result isnt as it should be. I am confused if it is correct or incorrect?

    Code (CSharp):
    1.            
    2. float4 RandomPointOnTriangleR(v2g IN[3], int id)
    3.             {
    4.                 float4 mp = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3.0;
    5.                 //Reflection method:
    6.                 float4 a = IN[1].vertex - IN[0].vertex;
    7.                 float4 b = IN[2].vertex - IN[0].vertex;
    8.                 float u1 = rand(a.x);
    9.                 float u2 = rand(u1);
    10.                 if ((u1 + u2) > 1.0)
    11.                 {
    12.                     u2 = 1.0 - u2;
    13.                     u1 = 1.0 - u1;
    14.                 }
    15.                 float4 w = (u1 * a) + (u2 * b);
    16.                 return mp+w;
    17.             }
    18.  
    19.             [maxvertexcount(80)]
    20.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> outstream)
    21.             {
    22.                 AddBaseMesh(IN, outstream);
    23.                 float4 mp = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3.0;
    24.                 //sample a random point:
    25.                 for (int i = 0; i < 10; i++)
    26.                 {
    27.                     mp = RandomPointOnTriangleR(IN, i);
    28.                     AddQuadMesh(IN, outstream, mp);
    29.                 }
    30.             }
    31.  
    upload_2021-1-7_22-47-54.png upload_2021-1-7_22-48-15.png
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    A few problems I see with the above code:

    Code (csharp):
    1. float4 w = (u1 * a) + (u2 * b);
    2. return mp+w;
    w
    is a position relative to vertex 0, not the "mid point" (
    mp
    ). So the position you're calculating is offset. If you hard code the u1 and u2 to be 0.33, that should put the quad in the center of the triangle, and with that code it'll instead be on the edge between vertex 1 and vertex 2.

    Code (csharp):
    1. float4 a = IN[1].vertex - IN[0].vertex;
    2. float u1 = rand(a.x);
    a
    is the difference between vertex 0 and vertex 1. That's going to be constant for each triangle, so calling
    rand(a.x)
    will also give you the same value every time since the input seed of
    a.x
    is the same every time. That means you're spawning 10 quads in the same spot. You're already passing in a "unique" seed (
    id
    ), use that instead. Or you can use
    rand(a.x + id)
    to randomize it per triangle. Alternatively, or in addition, you could add
    SV_PrimitiveID
    as an input to the geometry shader function to get the invoking triangle's index.

    Code (csharp):
    1. void geom(triangle v2g IN[3], inout TriangleStream<g2f> outstream, uint primID : SV_PrimitiveID)
    2. {
    3.   // ...
    4.     // primID * 10 because you're making 10 quads
    5.     // this ensures each triangle randoms a unique set of points
    6.     // make that 10 any value you want, but at least roughly the max number of
    7.     // points you might spawn per tri
    8.     mp = RandomPointOnTriangleR(IN, i + primID * 10);
    9.  
    10. // in the rand point on tri function use the id to seed the rand() function
    11. float u1 = rand((float)id);
     
  3. iamvideep

    iamvideep

    Joined:
    Oct 27, 2017
    Posts:
    118
    I modified the code to :
    Code (CSharp):
    1. float rand(float n) { return frac(sin(n) * 43758.5453123); }
    2.  
    3.             float4 RandomPointOnTriangleR(v2g IN[3], int id)
    4.             {
    5.                 float4 mp = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3.0;
    6.                 //Reflection method:
    7.                 float4 a = normalize(IN[1].vertex - IN[0].vertex);
    8.                 float4 b = normalize(IN[2].vertex - IN[0].vertex);
    9.                 float u1 = saturate(rand(a.x+id));
    10.                 float u2 = saturate(rand(u1));
    11.                 if ((u1 + u2) > 1.0)
    12.                 {
    13.                     u2 = 1.0 - u2;
    14.                     u1 = 1.0 - u1;
    15.                 }
    16.                 float4 avg = (u1 * a) + (u2 * b);
    17.                 return mp+avg;
    18.             }
    19.  
    20.             [maxvertexcount(80)]
    21.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> outstream, uint primID : SV_PrimitiveID)
    22.             {
    23.                 AddBaseMesh(IN, outstream);
    24.                 float4 mp = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3.0;
    25.                 //sample a random point:
    26.                 for (int i = 0; i < _Iter; i++)
    27.                 {
    28.                     mp = RandomPointOnTriangleR(IN,i + primID);
    29.                     AddQuadMesh(IN, outstream, mp);
    30.                 }
    31.             }
    Thats awesome! I can now have random points, but outside of the triangle. Somehow, the function should calculate the area within the triangle: Why arent the triangles getting on the surface, simple maths applied wrong?
    upload_2021-1-8_8-28-20.png

    upload_2021-1-8_8-30-34.png
     

    Attached Files:

  4. iamvideep

    iamvideep

    Joined:
    Oct 27, 2017
    Posts:
    118
    Somehow, if I do not position it by the midpoint of the triangle, I am able to get a good distribution:
    upload_2021-1-8_9-21-55.png

    Code (CSharp):
    1. float4 RandomPointOnTriangleR(v2g IN[3], float id)
    2.             {
    3.                 float4 mp = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3.0;
    4.                 //Reflection method:
    5.  
    6.                 float4 v1 = IN[0].vertex;
    7.                 float4 v2 = IN[1].vertex;
    8.                 float4 v3 = IN[2].vertex;
    9.  
    10.                 float4 midPoint = (v1 + v2 + v3) / 3.0;
    11.  
    12.                 float4 a = v2 - v1;
    13.                 float4 b = v3-v1;
    14.                 float u1 = abs(rand(a.x+id));
    15.                 float u2 = abs(rand(u1));
    16.                 if ((u1 + u2) > 1.0)
    17.                 {
    18.                     u2 = 1.0 - u2;
    19.                     u1 = 1.0 - u1;
    20.                 }
    21.                 float4 avg = (u1 * a) + (u2 * b);
    22.                 return v1 + avg;
    23.             }
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    Yeah, that's why I said it's a position relative to vertex 0, not the mid point. ;)
     
  6. iamvideep

    iamvideep

    Joined:
    Oct 27, 2017
    Posts:
    118
    But why and how? Even if we do an average of the 3 vertices? Still it will be relative to vertex0?
    Because I was calculating
    a and b with respect to v0?

    even if I do so, and add the point to v0 I still do not get satisfactory random sampling, most of them are either away in the z direction.
     
    Last edited: Jan 8, 2021
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    This isn't an average, that's an offset position relative to vertex 0.
    You're adding the offset
    a
    (the difference between vertex 0 and vertex 1) and the offset
    b
    (the difference between vertex 0 and vertex 2) to each other, not averaging.

    Think of a right triangle where vertex 0 is at the 90 degree corner:
    upload_2021-1-7_22-26-50.png
    a
    and
    b
    are the relative direction & magnitude from vertex 0 to the other two vertices. Your
    u1
    and
    u2
    values are how far along those two vectors you want to be. In this right angle case, it's basically the same as a basic "xy" position, where vertex 0 is the origin.

    But the same math works on any kind of triangle. The
    a
    and
    b
    vectors are the direction & magnitude to the other two vertices, and the
    u1
    and
    u2
    are how much along that vector towards the other vertices and away from vector 0 should it be.
     
  8. iamvideep

    iamvideep

    Joined:
    Oct 27, 2017
    Posts:
    118
    Okay okay, I was a lil confused, until I opened up 3ds Max and wrote a maxscript on a triangle to understand the concept. Then applied in unity:
    upload_2021-1-9_23-48-50.png
    upload_2021-1-9_23-48-36.png

    Thank you for your help :)